xref: /openbmc/linux/net/ipv4/devinet.c (revision fd5e9fccbd504c5179ab57ff695c610bca8809d6)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	NET3	IP device support routines.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
602c30a84SJesper Juhl  * 		Authors:	Ross Biro
71da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
81da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *	Additional Authors:
111da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
121da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Changes:
151da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
161da177e4SLinus Torvalds  *					lists.
171da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
181da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
191da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
201da177e4SLinus Torvalds  *					fall back to comparing just the label
211da177e4SLinus Torvalds  *					if no match found.
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
261da177e4SLinus Torvalds #include <linux/bitops.h>
274fc268d2SRandy Dunlap #include <linux/capability.h>
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds #include <linux/types.h>
301da177e4SLinus Torvalds #include <linux/kernel.h>
31174cd4b1SIngo Molnar #include <linux/sched/signal.h>
321da177e4SLinus Torvalds #include <linux/string.h>
331da177e4SLinus Torvalds #include <linux/mm.h>
341da177e4SLinus Torvalds #include <linux/socket.h>
351da177e4SLinus Torvalds #include <linux/sockios.h>
361da177e4SLinus Torvalds #include <linux/in.h>
371da177e4SLinus Torvalds #include <linux/errno.h>
381da177e4SLinus Torvalds #include <linux/interrupt.h>
391823730fSThomas Graf #include <linux/if_addr.h>
401da177e4SLinus Torvalds #include <linux/if_ether.h>
411da177e4SLinus Torvalds #include <linux/inet.h>
421da177e4SLinus Torvalds #include <linux/netdevice.h>
431da177e4SLinus Torvalds #include <linux/etherdevice.h>
441da177e4SLinus Torvalds #include <linux/skbuff.h>
451da177e4SLinus Torvalds #include <linux/init.h>
461da177e4SLinus Torvalds #include <linux/notifier.h>
471da177e4SLinus Torvalds #include <linux/inetdevice.h>
481da177e4SLinus Torvalds #include <linux/igmp.h>
495a0e3ad6STejun Heo #include <linux/slab.h>
50fd23c3b3SDavid S. Miller #include <linux/hash.h>
511da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
521da177e4SLinus Torvalds #include <linux/sysctl.h>
531da177e4SLinus Torvalds #endif
541da177e4SLinus Torvalds #include <linux/kmod.h>
55edc9e748SNicolas Dichtel #include <linux/netconf.h>
561da177e4SLinus Torvalds 
5714c85021SArnaldo Carvalho de Melo #include <net/arp.h>
581da177e4SLinus Torvalds #include <net/ip.h>
591da177e4SLinus Torvalds #include <net/route.h>
601da177e4SLinus Torvalds #include <net/ip_fib.h>
6163f3444fSThomas Graf #include <net/rtnetlink.h>
62752d14dcSPavel Emelyanov #include <net/net_namespace.h>
635c766d64SJiri Pirko #include <net/addrconf.h>
641da177e4SLinus Torvalds 
652e605463SMatteo Croce #define IPV6ONLY_FLAGS	\
662e605463SMatteo Croce 		(IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \
672e605463SMatteo Croce 		 IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \
682e605463SMatteo Croce 		 IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY)
692e605463SMatteo Croce 
700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7142f811b8SHerbert Xu 	.data = {
7202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
772690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
78fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
7942f811b8SHerbert Xu 	},
801da177e4SLinus Torvalds };
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8342f811b8SHerbert Xu 	.data = {
8402291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8802291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
902690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
91fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
9242f811b8SHerbert Xu 	},
931da177e4SLinus Torvalds };
941da177e4SLinus Torvalds 
959355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
969355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9742f811b8SHerbert Xu 
98ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
995c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
1005c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
1015c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1025176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1035c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
104ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
105af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
106d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
10747f0bd50SJacques de Laval 	[IFA_PROTO]		= { .type = NLA_U8 },
1085c753978SThomas Graf };
1095c753978SThomas Graf 
110978a46faSChristian Brauner struct inet_fill_args {
111978a46faSChristian Brauner 	u32 portid;
112978a46faSChristian Brauner 	u32 seq;
113978a46faSChristian Brauner 	int event;
114978a46faSChristian Brauner 	unsigned int flags;
115978a46faSChristian Brauner 	int netnsid;
1165fcd266aSDavid Ahern 	int ifindex;
117978a46faSChristian Brauner };
118978a46faSChristian Brauner 
11940384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
12040384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
12140384999SEric Dumazet 
122fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
123fd23c3b3SDavid S. Miller 
inet_addr_hash(const struct net * net,__be32 addr)1246eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
125fd23c3b3SDavid S. Miller {
12640384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
127fd23c3b3SDavid S. Miller 
12840384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
129fd23c3b3SDavid S. Miller }
130fd23c3b3SDavid S. Miller 
inet_hash_insert(struct net * net,struct in_ifaddr * ifa)131fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
132fd23c3b3SDavid S. Miller {
13340384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
134fd23c3b3SDavid S. Miller 
13532a4be48SWANG Cong 	ASSERT_RTNL();
136fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
137fd23c3b3SDavid S. Miller }
138fd23c3b3SDavid S. Miller 
inet_hash_remove(struct in_ifaddr * ifa)139fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
140fd23c3b3SDavid S. Miller {
14132a4be48SWANG Cong 	ASSERT_RTNL();
142fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
143fd23c3b3SDavid S. Miller }
144fd23c3b3SDavid S. Miller 
1459435eb1cSDavid S. Miller /**
1469435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1479435eb1cSDavid S. Miller  * @net: the net namespace
1489435eb1cSDavid S. Miller  * @addr: the source address
1499435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1509435eb1cSDavid S. Miller  *
1519435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1529435eb1cSDavid S. Miller  */
__ip_dev_find(struct net * net,__be32 addr,bool devref)1539435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1549435eb1cSDavid S. Miller {
1559435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1569435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1579435eb1cSDavid S. Miller 
1589435eb1cSDavid S. Miller 	rcu_read_lock();
1596e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1606e617de8SPaolo Abeni 	if (!ifa) {
161406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
162406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
163406b6f97SDavid S. Miller 		struct fib_table *local;
164406b6f97SDavid S. Miller 
165406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
166406b6f97SDavid S. Miller 		 * over loopback subnets work.
167406b6f97SDavid S. Miller 		 */
168406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
169406b6f97SDavid S. Miller 		if (local &&
170406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
171406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
172406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1736e617de8SPaolo Abeni 	} else {
1746e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
175406b6f97SDavid S. Miller 	}
1769435eb1cSDavid S. Miller 	if (result && devref)
1779435eb1cSDavid S. Miller 		dev_hold(result);
1789435eb1cSDavid S. Miller 	rcu_read_unlock();
1799435eb1cSDavid S. Miller 	return result;
1809435eb1cSDavid S. Miller }
1819435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1829435eb1cSDavid S. Miller 
1836e617de8SPaolo Abeni /* called under RCU lock */
inet_lookup_ifaddr_rcu(struct net * net,__be32 addr)1846e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1856e617de8SPaolo Abeni {
1866e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1876e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1886e617de8SPaolo Abeni 
1896e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1906e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1916e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1926e617de8SPaolo Abeni 			return ifa;
1936e617de8SPaolo Abeni 
1946e617de8SPaolo Abeni 	return NULL;
1956e617de8SPaolo Abeni }
1966e617de8SPaolo Abeni 
197d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1981da177e4SLinus Torvalds 
199e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
2003ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
2012638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
2022638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
2031da177e4SLinus Torvalds 			 int destroy);
2041da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20651602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20751602b2aSPavel Emelyanov #else
devinet_sysctl_register(struct in_device * idev)20820e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20951602b2aSPavel Emelyanov {
21020e61da7SWANG Cong 	return 0;
21151602b2aSPavel Emelyanov }
devinet_sysctl_unregister(struct in_device * idev)21240384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
21351602b2aSPavel Emelyanov {
21451602b2aSPavel Emelyanov }
2151da177e4SLinus Torvalds #endif
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /* Locks all the inet devices. */
2181da177e4SLinus Torvalds 
inet_alloc_ifa(void)2191da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2201da177e4SLinus Torvalds {
2216126891cSVasily Averin 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
inet_rcu_free_ifa(struct rcu_head * head)2241da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2271da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2281da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2291da177e4SLinus Torvalds 	kfree(ifa);
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
inet_free_ifa(struct in_ifaddr * ifa)23240384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
in_dev_free_rcu(struct rcu_head * head)2379d40c84cSEric Dumazet static void in_dev_free_rcu(struct rcu_head *head)
2389d40c84cSEric Dumazet {
2399d40c84cSEric Dumazet 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2409d40c84cSEric Dumazet 
2419d40c84cSEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2429d40c84cSEric Dumazet 	kfree(idev);
2439d40c84cSEric Dumazet }
2449d40c84cSEric Dumazet 
in_dev_finish_destroy(struct in_device * idev)2451da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2481da177e4SLinus Torvalds 
249547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
250547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2511da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
25291df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2531da177e4SLinus Torvalds #endif
254d62607c3SJakub Kicinski 	netdev_put(dev, &idev->dev_tracker);
2551da177e4SLinus Torvalds 	if (!idev->dead)
2569f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2579f9354b9SEric Dumazet 	else
2589d40c84cSEric Dumazet 		call_rcu(&idev->rcu_head, in_dev_free_rcu);
2591da177e4SLinus Torvalds }
2609f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2611da177e4SLinus Torvalds 
inetdev_init(struct net_device * dev)26271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	struct in_device *in_dev;
26520e61da7SWANG Cong 	int err = -ENOMEM;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	ASSERT_RTNL();
2681da177e4SLinus Torvalds 
2690da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2701da177e4SLinus Torvalds 	if (!in_dev)
2711da177e4SLinus Torvalds 		goto out;
272c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2739355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2741da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2751da177e4SLinus Torvalds 	in_dev->dev = dev;
2769f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2779f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2781da177e4SLinus Torvalds 		goto out_kfree;
2790187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2800187bdfbSBen Hutchings 		dev_disable_lro(dev);
2811da177e4SLinus Torvalds 	/* Reference in_dev->dev */
282d62607c3SJakub Kicinski 	netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL);
28330c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2847658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2851da177e4SLinus Torvalds 
2861ee5ef31SXin Long 	if (dev != blackhole_netdev) {
28720e61da7SWANG Cong 		err = devinet_sysctl_register(in_dev);
28820e61da7SWANG Cong 		if (err) {
28920e61da7SWANG Cong 			in_dev->dead = 1;
2901b49cd71SYang Yingliang 			neigh_parms_release(&arp_tbl, in_dev->arp_parms);
29120e61da7SWANG Cong 			in_dev_put(in_dev);
29220e61da7SWANG Cong 			in_dev = NULL;
29320e61da7SWANG Cong 			goto out;
29420e61da7SWANG Cong 		}
2951da177e4SLinus Torvalds 		ip_mc_init_dev(in_dev);
2961da177e4SLinus Torvalds 		if (dev->flags & IFF_UP)
2971da177e4SLinus Torvalds 			ip_mc_up(in_dev);
2981ee5ef31SXin Long 	}
299483479ecSJarek Poplawski 
30030c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
301cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
302483479ecSJarek Poplawski out:
30320e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
3041da177e4SLinus Torvalds out_kfree:
3051da177e4SLinus Torvalds 	kfree(in_dev);
3061da177e4SLinus Torvalds 	in_dev = NULL;
3071da177e4SLinus Torvalds 	goto out;
3081da177e4SLinus Torvalds }
3091da177e4SLinus Torvalds 
inetdev_destroy(struct in_device * in_dev)3101da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3111da177e4SLinus Torvalds {
3121da177e4SLinus Torvalds 	struct net_device *dev;
3132638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	ASSERT_RTNL();
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	dev = in_dev->dev;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	in_dev->dead = 1;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3221da177e4SLinus Torvalds 
3232638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3241da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3251da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3261da177e4SLinus Torvalds 	}
3271da177e4SLinus Torvalds 
328a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3291da177e4SLinus Torvalds 
33051602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3311da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3321da177e4SLinus Torvalds 	arp_ifdown(dev);
3331da177e4SLinus Torvalds 
3349d40c84cSEric Dumazet 	in_dev_put(in_dev);
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
inet_blackhole_dev_init(void)3371ee5ef31SXin Long static int __init inet_blackhole_dev_init(void)
3381ee5ef31SXin Long {
3391ee5ef31SXin Long 	int err = 0;
3401ee5ef31SXin Long 
3411ee5ef31SXin Long 	rtnl_lock();
3421ee5ef31SXin Long 	if (!inetdev_init(blackhole_netdev))
3431ee5ef31SXin Long 		err = -ENOMEM;
3441ee5ef31SXin Long 	rtnl_unlock();
3451ee5ef31SXin Long 
3461ee5ef31SXin Long 	return err;
3471ee5ef31SXin Long }
3481ee5ef31SXin Long late_initcall(inet_blackhole_dev_init);
3491ee5ef31SXin Long 
inet_addr_onlink(struct in_device * in_dev,__be32 a,__be32 b)350ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3511da177e4SLinus Torvalds {
352d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
353d519e870SFlorian Westphal 
3541da177e4SLinus Torvalds 	rcu_read_lock();
355d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3561da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3571da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3581da177e4SLinus Torvalds 				rcu_read_unlock();
3591da177e4SLinus Torvalds 				return 1;
3601da177e4SLinus Torvalds 			}
3611da177e4SLinus Torvalds 		}
362d519e870SFlorian Westphal 	}
3631da177e4SLinus Torvalds 	rcu_read_unlock();
3641da177e4SLinus Torvalds 	return 0;
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds 
__inet_del_ifa(struct in_device * in_dev,struct in_ifaddr __rcu ** ifap,int destroy,struct nlmsghdr * nlh,u32 portid)3672638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3682638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
36915e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3701da177e4SLinus Torvalds {
3718f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3722638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
373ac28b1ecSLiu Jian 	struct in_ifaddr __rcu **last_prim;
3740ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3750ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	ASSERT_RTNL();
3781da177e4SLinus Torvalds 
3792638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
380ac28b1ecSLiu Jian 	last_prim = ifap;
381fbd40ea0SDavid S. Miller 	if (in_dev->dead)
382fbd40ea0SDavid S. Miller 		goto no_promotions;
383fbd40ea0SDavid S. Miller 
3848f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3858f937c60SHarald Welte 	 * unless alias promotion is set
3868f937c60SHarald Welte 	 **/
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3892638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3901da177e4SLinus Torvalds 
3912638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3920ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3930ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
394ac28b1ecSLiu Jian 				last_prim = &ifa->ifa_next;
3950ff60a45SJamal Hadi Salim 
3961da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3971da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3981da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3991da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
4000ff60a45SJamal Hadi Salim 				prev_prom = ifa;
4011da177e4SLinus Torvalds 				continue;
4021da177e4SLinus Torvalds 			}
4031da177e4SLinus Torvalds 
4040ff60a45SJamal Hadi Salim 			if (!do_promote) {
405fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
4061da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
4071da177e4SLinus Torvalds 
40815e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
409e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
410e041c683SAlan Stern 						NETDEV_DOWN, ifa);
4111da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4128f937c60SHarald Welte 			} else {
4138f937c60SHarald Welte 				promote = ifa;
4148f937c60SHarald Welte 				break;
4158f937c60SHarald Welte 			}
4161da177e4SLinus Torvalds 		}
4171da177e4SLinus Torvalds 	}
4181da177e4SLinus Torvalds 
4192d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4202d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4212d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4222d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4232d230e2bSJulian Anastasov 	 */
4242638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4252d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4262d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4272d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4282d230e2bSJulian Anastasov 	}
4292d230e2bSJulian Anastasov 
430fbd40ea0SDavid S. Miller no_promotions:
4311da177e4SLinus Torvalds 	/* 2. Unlink it */
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
434fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4391da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4401da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4411da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4421da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4431da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4441da177e4SLinus Torvalds 	   So that, this order is correct.
4451da177e4SLinus Torvalds 	 */
44615e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
447e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4480ff60a45SJamal Hadi Salim 
4490ff60a45SJamal Hadi Salim 	if (promote) {
4502638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4510ff60a45SJamal Hadi Salim 
4522638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4530ff60a45SJamal Hadi Salim 		if (prev_prom) {
4542638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4552638eb8bSFlorian Westphal 
4562638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4576a9e9ceaSFlorian Westphal 
458ac28b1ecSLiu Jian 			last_sec = rtnl_dereference(*last_prim);
4592638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
460ac28b1ecSLiu Jian 			rcu_assign_pointer(*last_prim, promote);
4610ff60a45SJamal Hadi Salim 		}
4620ff60a45SJamal Hadi Salim 
4630ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
46415e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
465e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
466e041c683SAlan Stern 				NETDEV_UP, promote);
4672638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4682638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4690ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4700ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4710ff60a45SJamal Hadi Salim 					continue;
4720ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4730ff60a45SJamal Hadi Salim 		}
4740ff60a45SJamal Hadi Salim 
4750ff60a45SJamal Hadi Salim 	}
4766363097cSHerbert Xu 	if (destroy)
4771da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4781da177e4SLinus Torvalds }
4791da177e4SLinus Torvalds 
inet_del_ifa(struct in_device * in_dev,struct in_ifaddr __rcu ** ifap,int destroy)4802638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4812638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
482d6062cbbSThomas Graf 			 int destroy)
483d6062cbbSThomas Graf {
484d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
485d6062cbbSThomas Graf }
486d6062cbbSThomas Graf 
4875c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4885c766d64SJiri Pirko 
4895c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4905c766d64SJiri Pirko 
__inet_insert_ifa(struct in_ifaddr * ifa,struct nlmsghdr * nlh,u32 portid,struct netlink_ext_ack * extack)491d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
492de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4931da177e4SLinus Torvalds {
4942638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4951da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4963ad7d246SKrister Johansen 	struct in_validator_info ivi;
4972638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4983ad7d246SKrister Johansen 	int ret;
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	ASSERT_RTNL();
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
5031da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5041da177e4SLinus Torvalds 		return 0;
5051da177e4SLinus Torvalds 	}
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
5081da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
5091da177e4SLinus Torvalds 
5102e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
5112e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
5122e605463SMatteo Croce 
5132638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
5142638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
5152638eb8bSFlorian Westphal 
5162638eb8bSFlorian Westphal 	while (ifa1) {
5171da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
5181da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5191da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5201da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5211da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5221da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5231da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5241da177e4SLinus Torvalds 				return -EEXIST;
5251da177e4SLinus Torvalds 			}
5261da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
527b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value");
5281da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5291da177e4SLinus Torvalds 				return -EINVAL;
5301da177e4SLinus Torvalds 			}
5311da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5321da177e4SLinus Torvalds 		}
5332638eb8bSFlorian Westphal 
5342638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5352638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5361da177e4SLinus Torvalds 	}
5371da177e4SLinus Torvalds 
5383ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5393ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5403ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5413ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5423ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5433ad7d246SKrister Johansen 	 * not the flags.
5443ad7d246SKrister Johansen 	 */
5453ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5463ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
547de95e047SDavid Ahern 	ivi.extack = extack;
5483ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5493ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5503ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5513ad7d246SKrister Johansen 	if (ret) {
5523ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5533ad7d246SKrister Johansen 		return ret;
5543ad7d246SKrister Johansen 	}
5553ad7d246SKrister Johansen 
556d4150779SJason A. Donenfeld 	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
5571da177e4SLinus Torvalds 		ifap = last_primary;
5581da177e4SLinus Torvalds 
5592638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5602638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5611da177e4SLinus Torvalds 
562fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
563fd23c3b3SDavid S. Miller 
5645c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
565906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5665c766d64SJiri Pirko 
5671da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5681da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5691da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
57015e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
571e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	return 0;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
inet_insert_ifa(struct in_ifaddr * ifa)576d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
577d6062cbbSThomas Graf {
578de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
579d6062cbbSThomas Graf }
580d6062cbbSThomas Graf 
inet_set_ifa(struct net_device * dev,struct in_ifaddr * ifa)5811da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5821da177e4SLinus Torvalds {
583e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	ASSERT_RTNL();
5861da177e4SLinus Torvalds 
58771e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5881d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5891da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
590547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5911da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5921da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5931da177e4SLinus Torvalds 	}
594f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5951da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5961da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
5998723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
6008723e1b4SEric Dumazet  * We dont take a reference on found in_device
6018723e1b4SEric Dumazet  */
inetdev_by_index(struct net * net,int ifindex)6027fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
6031da177e4SLinus Torvalds {
6041da177e4SLinus Torvalds 	struct net_device *dev;
6051da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
606c148fc2eSEric Dumazet 
607c148fc2eSEric Dumazet 	rcu_read_lock();
608c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
6091da177e4SLinus Torvalds 	if (dev)
6108723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
611c148fc2eSEric Dumazet 	rcu_read_unlock();
6121da177e4SLinus Torvalds 	return in_dev;
6131da177e4SLinus Torvalds }
6149f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6171da177e4SLinus Torvalds 
inet_ifa_byprefix(struct in_device * in_dev,__be32 prefix,__be32 mask)61860cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
61960cad5daSAl Viro 				    __be32 mask)
6201da177e4SLinus Torvalds {
621d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
622d519e870SFlorian Westphal 
6231da177e4SLinus Torvalds 	ASSERT_RTNL();
6241da177e4SLinus Torvalds 
625d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6261da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6271da177e4SLinus Torvalds 			return ifa;
628d519e870SFlorian Westphal 	}
6291da177e4SLinus Torvalds 	return NULL;
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds 
ip_mc_autojoin_config(struct net * net,bool join,const struct in_ifaddr * ifa)632690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
633690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
63493a714d6SMadhu Challa {
635690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
63693a714d6SMadhu Challa 	struct ip_mreqn mreq = {
63793a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
63893a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
63993a714d6SMadhu Challa 	};
640690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
64193a714d6SMadhu Challa 	int ret;
64293a714d6SMadhu Challa 
64393a714d6SMadhu Challa 	ASSERT_RTNL();
64493a714d6SMadhu Challa 
64593a714d6SMadhu Challa 	lock_sock(sk);
64693a714d6SMadhu Challa 	if (join)
64754ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
64893a714d6SMadhu Challa 	else
64954ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
65093a714d6SMadhu Challa 	release_sock(sk);
65193a714d6SMadhu Challa 
65293a714d6SMadhu Challa 	return ret;
653690cc863STaras Chornyi #else
654690cc863STaras Chornyi 	return -EOPNOTSUPP;
655690cc863STaras Chornyi #endif
65693a714d6SMadhu Challa }
65793a714d6SMadhu Challa 
inet_rtm_deladdr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)658c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
659c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6601da177e4SLinus Torvalds {
6613b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6622638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
663dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6641da177e4SLinus Torvalds 	struct in_device *in_dev;
665dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6662638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
66730e2379eSMenglong Dong 	int err;
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	ASSERT_RTNL();
6701da177e4SLinus Torvalds 
6718cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6728cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
673dfdd5fd4SThomas Graf 	if (err < 0)
674dfdd5fd4SThomas Graf 		goto errout;
675dfdd5fd4SThomas Graf 
676dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6777fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
67851456b29SIan Morris 	if (!in_dev) {
679b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
680dfdd5fd4SThomas Graf 		err = -ENODEV;
681dfdd5fd4SThomas Graf 		goto errout;
682dfdd5fd4SThomas Graf 	}
683dfdd5fd4SThomas Graf 
6842638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6851da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
686dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
68767b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6881da177e4SLinus Torvalds 			continue;
689dfdd5fd4SThomas Graf 
690dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
691dfdd5fd4SThomas Graf 			continue;
692dfdd5fd4SThomas Graf 
693dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
694dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
69567b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
696dfdd5fd4SThomas Graf 			continue;
697dfdd5fd4SThomas Graf 
69893a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
699690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
70015e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
7011da177e4SLinus Torvalds 		return 0;
7021da177e4SLinus Torvalds 	}
703dfdd5fd4SThomas Graf 
704b4672c73SHangbin Liu 	NL_SET_ERR_MSG(extack, "ipv4: Address not found");
705dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
706dfdd5fd4SThomas Graf errout:
707dfdd5fd4SThomas Graf 	return err;
7081da177e4SLinus Torvalds }
7091da177e4SLinus Torvalds 
7105c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
7115c766d64SJiri Pirko 
check_lifetime(struct work_struct * work)7125c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
7135c766d64SJiri Pirko {
7145c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
7155c766d64SJiri Pirko 	struct in_ifaddr *ifa;
716c988d1e8SJiri Pirko 	struct hlist_node *n;
7175c766d64SJiri Pirko 	int i;
7185c766d64SJiri Pirko 
7195c766d64SJiri Pirko 	now = jiffies;
7205c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
7215c766d64SJiri Pirko 
7225c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
723c988d1e8SJiri Pirko 		bool change_needed = false;
724c988d1e8SJiri Pirko 
725c988d1e8SJiri Pirko 		rcu_read_lock();
726b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
7275c766d64SJiri Pirko 			unsigned long age;
7285c766d64SJiri Pirko 
7295c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
7305c766d64SJiri Pirko 				continue;
7315c766d64SJiri Pirko 
7325c766d64SJiri Pirko 			/* We try to batch several events at once. */
7335c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
7345c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7355c766d64SJiri Pirko 
7365c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
7375c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
738c988d1e8SJiri Pirko 				change_needed = true;
739c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
740c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
741c988d1e8SJiri Pirko 				continue;
742c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
743c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
744c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
745c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
746c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
747c988d1e8SJiri Pirko 
748c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
749c988d1e8SJiri Pirko 					change_needed = true;
750c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
751c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
752c988d1e8SJiri Pirko 					       next)) {
753c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
754c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
755c988d1e8SJiri Pirko 			}
756c988d1e8SJiri Pirko 		}
757c988d1e8SJiri Pirko 		rcu_read_unlock();
758c988d1e8SJiri Pirko 		if (!change_needed)
759c988d1e8SJiri Pirko 			continue;
760c988d1e8SJiri Pirko 		rtnl_lock();
761c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
762c988d1e8SJiri Pirko 			unsigned long age;
763c988d1e8SJiri Pirko 
764c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
765c988d1e8SJiri Pirko 				continue;
766c988d1e8SJiri Pirko 
767c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
768c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
769c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
770c988d1e8SJiri Pirko 
771c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
772c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7732638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7742638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7755c766d64SJiri Pirko 
7762638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7772638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7782638eb8bSFlorian Westphal 				while (tmp) {
77940008e92SFlorian Westphal 					if (tmp == ifa) {
7805c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7815c766d64SJiri Pirko 							     ifap, 1);
782c988d1e8SJiri Pirko 						break;
7835c766d64SJiri Pirko 					}
7842638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7852638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
786c988d1e8SJiri Pirko 				}
787c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
788c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
789c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
790c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7915c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7925c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7935c766d64SJiri Pirko 			}
7945c766d64SJiri Pirko 		}
795c988d1e8SJiri Pirko 		rtnl_unlock();
7965c766d64SJiri Pirko 	}
7975c766d64SJiri Pirko 
7985c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7995c766d64SJiri Pirko 	next_sched = next;
8005c766d64SJiri Pirko 
8015c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
8025c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
8035c766d64SJiri Pirko 		next_sched = next_sec;
8045c766d64SJiri Pirko 
8055c766d64SJiri Pirko 	now = jiffies;
8065c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
8075c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
8085c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
8095c766d64SJiri Pirko 
810906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
811906e073fSviresh kumar 			next_sched - now);
8125c766d64SJiri Pirko }
8135c766d64SJiri Pirko 
set_ifa_lifetime(struct in_ifaddr * ifa,__u32 valid_lft,__u32 prefered_lft)8145c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
8155c766d64SJiri Pirko 			     __u32 prefered_lft)
8165c766d64SJiri Pirko {
8175c766d64SJiri Pirko 	unsigned long timeout;
8185c766d64SJiri Pirko 
8195c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
8205c766d64SJiri Pirko 
8215c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
8225c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
8235c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
8245c766d64SJiri Pirko 	else
8255c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
8265c766d64SJiri Pirko 
8275c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8285c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8295c766d64SJiri Pirko 		if (timeout == 0)
8305c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
8315c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
8325c766d64SJiri Pirko 	}
8335c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
8345c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
8355c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
8365c766d64SJiri Pirko }
8375c766d64SJiri Pirko 
rtm_to_ifaddr(struct net * net,struct nlmsghdr * nlh,__u32 * pvalid_lft,__u32 * pprefered_lft,struct netlink_ext_ack * extack)8385c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
839dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
840dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8411da177e4SLinus Torvalds {
8425c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8435c753978SThomas Graf 	struct in_ifaddr *ifa;
8445c753978SThomas Graf 	struct ifaddrmsg *ifm;
8451da177e4SLinus Torvalds 	struct net_device *dev;
8461da177e4SLinus Torvalds 	struct in_device *in_dev;
8477b218574SDenis V. Lunev 	int err;
8481da177e4SLinus Torvalds 
8498cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8508cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8515c753978SThomas Graf 	if (err < 0)
8525c753978SThomas Graf 		goto errout;
8531da177e4SLinus Torvalds 
8545c753978SThomas Graf 	ifm = nlmsg_data(nlh);
855c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
856b4672c73SHangbin Liu 
857b4672c73SHangbin Liu 	if (ifm->ifa_prefixlen > 32) {
858b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length");
8595c753978SThomas Graf 		goto errout;
860b4672c73SHangbin Liu 	}
861b4672c73SHangbin Liu 
862b4672c73SHangbin Liu 	if (!tb[IFA_LOCAL]) {
863b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied");
864b4672c73SHangbin Liu 		goto errout;
865b4672c73SHangbin Liu 	}
8661da177e4SLinus Torvalds 
8674b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8685c753978SThomas Graf 	err = -ENODEV;
869b4672c73SHangbin Liu 	if (!dev) {
870b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
8715c753978SThomas Graf 		goto errout;
872b4672c73SHangbin Liu 	}
8731da177e4SLinus Torvalds 
8745c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8755c753978SThomas Graf 	err = -ENOBUFS;
87651456b29SIan Morris 	if (!in_dev)
8775c753978SThomas Graf 		goto errout;
87871e27da9SHerbert Xu 
8795c753978SThomas Graf 	ifa = inet_alloc_ifa();
88051456b29SIan Morris 	if (!ifa)
8815c753978SThomas Graf 		/*
8825c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8835c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8845c753978SThomas Graf 		 */
8855c753978SThomas Graf 		goto errout;
8865c753978SThomas Graf 
887a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8881d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8895c753978SThomas Graf 	in_dev_hold(in_dev);
8905c753978SThomas Graf 
89151456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8925c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8935c753978SThomas Graf 
894fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8951da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8961da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
897ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
898ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8991da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
9001da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
9015c753978SThomas Graf 
90267b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
90367b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
9045c753978SThomas Graf 
9055c753978SThomas Graf 	if (tb[IFA_BROADCAST])
90667b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
9075c753978SThomas Graf 
9085c753978SThomas Graf 	if (tb[IFA_LABEL])
909872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
9101da177e4SLinus Torvalds 	else
9111da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
9121da177e4SLinus Torvalds 
913af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
914af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
915af4d768aSDavid Ahern 
91647f0bd50SJacques de Laval 	if (tb[IFA_PROTO])
91747f0bd50SJacques de Laval 		ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
91847f0bd50SJacques de Laval 
9195c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
9205c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
9215c766d64SJiri Pirko 
9225c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
9235c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
924b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid");
9255c766d64SJiri Pirko 			err = -EINVAL;
926446266b0SDaniel Borkmann 			goto errout_free;
9275c766d64SJiri Pirko 		}
9285c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
9295c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9305c766d64SJiri Pirko 	}
9315c766d64SJiri Pirko 
9325c753978SThomas Graf 	return ifa;
9335c753978SThomas Graf 
934446266b0SDaniel Borkmann errout_free:
935446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9365c753978SThomas Graf errout:
9375c753978SThomas Graf 	return ERR_PTR(err);
9385c753978SThomas Graf }
9395c753978SThomas Graf 
find_matching_ifa(struct in_ifaddr * ifa)9405c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9415c766d64SJiri Pirko {
9425c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
943ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9445c766d64SJiri Pirko 
9455c766d64SJiri Pirko 	if (!ifa->ifa_local)
9465c766d64SJiri Pirko 		return NULL;
9475c766d64SJiri Pirko 
948ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9495c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9505c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9515c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9525c766d64SJiri Pirko 			return ifa1;
9535c766d64SJiri Pirko 	}
9545c766d64SJiri Pirko 	return NULL;
9555c766d64SJiri Pirko }
9565c766d64SJiri Pirko 
inet_rtm_newaddr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)957c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
958c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9595c753978SThomas Graf {
9603b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9615c753978SThomas Graf 	struct in_ifaddr *ifa;
9625c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9635c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9645c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9655c753978SThomas Graf 
9665c753978SThomas Graf 	ASSERT_RTNL();
9675c753978SThomas Graf 
968dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9695c753978SThomas Graf 	if (IS_ERR(ifa))
9705c753978SThomas Graf 		return PTR_ERR(ifa);
9715c753978SThomas Graf 
9725c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9735c766d64SJiri Pirko 	if (!ifa_existing) {
9745c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
975614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9765c766d64SJiri Pirko 		 */
9775c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
97893a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
979690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
98093a714d6SMadhu Challa 
98193a714d6SMadhu Challa 			if (ret < 0) {
982b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed");
98393a714d6SMadhu Challa 				inet_free_ifa(ifa);
98493a714d6SMadhu Challa 				return ret;
98593a714d6SMadhu Challa 			}
98693a714d6SMadhu Challa 		}
987de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
988de95e047SDavid Ahern 					 extack);
9895c766d64SJiri Pirko 	} else {
990af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
9915c4a9aa8SPetr Machata 		u8 new_proto = ifa->ifa_proto;
992af4d768aSDavid Ahern 
9935c766d64SJiri Pirko 		inet_free_ifa(ifa);
9945c766d64SJiri Pirko 
9955c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
996b4672c73SHangbin Liu 		    !(nlh->nlmsg_flags & NLM_F_REPLACE)) {
997b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: Address already assigned");
9985c766d64SJiri Pirko 			return -EEXIST;
999b4672c73SHangbin Liu 		}
100034e2ed34SJiri Pirko 		ifa = ifa_existing;
1001af4d768aSDavid Ahern 
1002af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
1003af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
1004af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
1005af4d768aSDavid Ahern 		}
1006af4d768aSDavid Ahern 
10075c4a9aa8SPetr Machata 		ifa->ifa_proto = new_proto;
10085c4a9aa8SPetr Machata 
100934e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
101005a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
1011906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
1012906e073fSviresh kumar 				&check_lifetime_work, 0);
101334e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
10145c766d64SJiri Pirko 	}
10155c766d64SJiri Pirko 	return 0;
10161da177e4SLinus Torvalds }
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds /*
10191da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
10201da177e4SLinus Torvalds  */
10211da177e4SLinus Torvalds 
inet_abc_len(__be32 addr)102240384999SEric Dumazet static int inet_abc_len(__be32 addr)
10231da177e4SLinus Torvalds {
10241da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
10251da177e4SLinus Torvalds 
102665cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
10271da177e4SLinus Torvalds 		rc = 0;
10281da177e4SLinus Torvalds 	else {
1029714e85beSAl Viro 		__u32 haddr = ntohl(addr);
1030714e85beSAl Viro 		if (IN_CLASSA(haddr))
10311da177e4SLinus Torvalds 			rc = 8;
1032714e85beSAl Viro 		else if (IN_CLASSB(haddr))
10331da177e4SLinus Torvalds 			rc = 16;
1034714e85beSAl Viro 		else if (IN_CLASSC(haddr))
10351da177e4SLinus Torvalds 			rc = 24;
103665cab850SDave Taht 		else if (IN_CLASSE(haddr))
103765cab850SDave Taht 			rc = 32;
10381da177e4SLinus Torvalds 	}
10391da177e4SLinus Torvalds 
10401da177e4SLinus Torvalds 	return rc;
10411da177e4SLinus Torvalds }
10421da177e4SLinus Torvalds 
10431da177e4SLinus Torvalds 
devinet_ioctl(struct net * net,unsigned int cmd,struct ifreq * ifr)104403aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10451da177e4SLinus Torvalds {
10461da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
104703aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10482638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10491da177e4SLinus Torvalds 	struct in_device *in_dev;
10501da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10511da177e4SLinus Torvalds 	struct net_device *dev;
10521da177e4SLinus Torvalds 	char *colon;
10531da177e4SLinus Torvalds 	int ret = -EFAULT;
10541da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10551da177e4SLinus Torvalds 
105603aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10571da177e4SLinus Torvalds 
10581da177e4SLinus Torvalds 	/* save original address for comparison */
10591da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10601da177e4SLinus Torvalds 
106103aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10621da177e4SLinus Torvalds 	if (colon)
10631da177e4SLinus Torvalds 		*colon = 0;
10641da177e4SLinus Torvalds 
106503aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds 	switch (cmd) {
10681da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10691da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10701da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10711da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10721da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10731da177e4SLinus Torvalds 		   so that we do not impose a lock.
10741da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10751da177e4SLinus Torvalds 		 */
10761da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10771da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10781da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10791da177e4SLinus Torvalds 		break;
10801da177e4SLinus Torvalds 
10811da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1082bf5b30b8SZhao Hongjiang 		ret = -EPERM;
108352e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10841da177e4SLinus Torvalds 			goto out;
10851da177e4SLinus Torvalds 		break;
10861da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10871da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10881da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10891da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1090bf5b30b8SZhao Hongjiang 		ret = -EPERM;
109152e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10921da177e4SLinus Torvalds 			goto out;
10931da177e4SLinus Torvalds 		ret = -EINVAL;
10941da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10951da177e4SLinus Torvalds 			goto out;
10961da177e4SLinus Torvalds 		break;
10971da177e4SLinus Torvalds 	default:
10981da177e4SLinus Torvalds 		ret = -EINVAL;
10991da177e4SLinus Torvalds 		goto out;
11001da177e4SLinus Torvalds 	}
11011da177e4SLinus Torvalds 
11021da177e4SLinus Torvalds 	rtnl_lock();
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	ret = -ENODEV;
110503aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
11069f9354b9SEric Dumazet 	if (!dev)
11071da177e4SLinus Torvalds 		goto done;
11081da177e4SLinus Torvalds 
11091da177e4SLinus Torvalds 	if (colon)
11101da177e4SLinus Torvalds 		*colon = ':';
11111da177e4SLinus Torvalds 
11129f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
11139f9354b9SEric Dumazet 	if (in_dev) {
11141da177e4SLinus Torvalds 		if (tryaddrmatch) {
11151da177e4SLinus Torvalds 			/* Matthias Andree */
11161da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
11171da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
11181da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
11191da177e4SLinus Torvalds 			   This is checked above. */
11202638eb8bSFlorian Westphal 
11212638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11222638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11231da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
112403aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
11251da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
11266c91afe1SDavid S. Miller 							ifa->ifa_local) {
11271da177e4SLinus Torvalds 					break; /* found */
11281da177e4SLinus Torvalds 				}
11291da177e4SLinus Torvalds 			}
11301da177e4SLinus Torvalds 		}
11311da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
11321da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
11331da177e4SLinus Torvalds 		   comparing just the label */
11341da177e4SLinus Torvalds 		if (!ifa) {
11352638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11362638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11371da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
113803aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11391da177e4SLinus Torvalds 					break;
11401da177e4SLinus Torvalds 		}
11411da177e4SLinus Torvalds 	}
11421da177e4SLinus Torvalds 
11431da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11441da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11451da177e4SLinus Torvalds 		goto done;
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds 	switch (cmd) {
11481da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
114930e948a3STonghao Zhang 		ret = 0;
11501da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
115103aef17bSAl Viro 		break;
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
115430e948a3STonghao Zhang 		ret = 0;
11551da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
115603aef17bSAl Viro 		break;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
115930e948a3STonghao Zhang 		ret = 0;
11601da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
116103aef17bSAl Viro 		break;
11621da177e4SLinus Torvalds 
11631da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
116430e948a3STonghao Zhang 		ret = 0;
11651da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
116603aef17bSAl Viro 		break;
11671da177e4SLinus Torvalds 
11681da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11691da177e4SLinus Torvalds 		if (colon) {
11701da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11711da177e4SLinus Torvalds 			if (!ifa)
11721da177e4SLinus Torvalds 				break;
11731da177e4SLinus Torvalds 			ret = 0;
117403aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11751da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11761da177e4SLinus Torvalds 			break;
11771da177e4SLinus Torvalds 		}
1178567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11791da177e4SLinus Torvalds 		break;
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11821da177e4SLinus Torvalds 		ret = -EINVAL;
11831da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11841da177e4SLinus Torvalds 			break;
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds 		if (!ifa) {
11871da177e4SLinus Torvalds 			ret = -ENOBUFS;
1188d4c4653bSKuniyuki Iwashima 			if (!in_dev)
1189d4c4653bSKuniyuki Iwashima 				break;
11909f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11919f9354b9SEric Dumazet 			if (!ifa)
11921da177e4SLinus Torvalds 				break;
1193c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11941da177e4SLinus Torvalds 			if (colon)
119503aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11961da177e4SLinus Torvalds 			else
11971da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11981da177e4SLinus Torvalds 		} else {
11991da177e4SLinus Torvalds 			ret = 0;
12001da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
12011da177e4SLinus Torvalds 				break;
12021da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12031da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1204148f9729SBjorn Mork 			ifa->ifa_scope = 0;
12051da177e4SLinus Torvalds 		}
12061da177e4SLinus Torvalds 
12071da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
12081da177e4SLinus Torvalds 
12091da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
12101da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
12111da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
12121da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12131da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
12141da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
12151da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
12161da177e4SLinus Torvalds 		} else {
12171da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
12181da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
12191da177e4SLinus Torvalds 		}
12205c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
12211da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
12221da177e4SLinus Torvalds 		break;
12231da177e4SLinus Torvalds 
12241da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
12251da177e4SLinus Torvalds 		ret = 0;
12261da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
12271da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12281da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
12291da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12301da177e4SLinus Torvalds 		}
12311da177e4SLinus Torvalds 		break;
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
12341da177e4SLinus Torvalds 		ret = 0;
12351da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
12361da177e4SLinus Torvalds 			break;
12371da177e4SLinus Torvalds 		ret = -EINVAL;
12381da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12391da177e4SLinus Torvalds 			break;
12401da177e4SLinus Torvalds 		ret = 0;
12411da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12421da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12431da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12441da177e4SLinus Torvalds 		break;
12451da177e4SLinus Torvalds 
12461da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12471da177e4SLinus Torvalds 
12481da177e4SLinus Torvalds 		/*
12491da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12501da177e4SLinus Torvalds 		 */
12511da177e4SLinus Torvalds 		ret = -EINVAL;
12521da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12531da177e4SLinus Torvalds 			break;
12541da177e4SLinus Torvalds 		ret = 0;
12551da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1256a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12571da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12581da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12591da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12601da177e4SLinus Torvalds 
12611da177e4SLinus Torvalds 			/* See if current broadcast address matches
12621da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12631da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12641da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12651da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12661da177e4SLinus Torvalds 			 */
12671da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12681da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12691da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1270dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12711da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12721da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12731da177e4SLinus Torvalds 			}
12741da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12751da177e4SLinus Torvalds 		}
12761da177e4SLinus Torvalds 		break;
12771da177e4SLinus Torvalds 	}
12781da177e4SLinus Torvalds done:
12791da177e4SLinus Torvalds 	rtnl_unlock();
12801da177e4SLinus Torvalds out:
12811da177e4SLinus Torvalds 	return ret;
12821da177e4SLinus Torvalds }
12831da177e4SLinus Torvalds 
inet_gifconf(struct net_device * dev,char __user * buf,int len,int size)1284b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12851da177e4SLinus Torvalds {
1286e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1287ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12881da177e4SLinus Torvalds 	struct ifreq ifr;
12891da177e4SLinus Torvalds 	int done = 0;
12901da177e4SLinus Torvalds 
129136fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
129236fd633eSAl Viro 		goto out;
129336fd633eSAl Viro 
12949f9354b9SEric Dumazet 	if (!in_dev)
12951da177e4SLinus Torvalds 		goto out;
12961da177e4SLinus Torvalds 
1297ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12981da177e4SLinus Torvalds 		if (!buf) {
129936fd633eSAl Viro 			done += size;
13001da177e4SLinus Torvalds 			continue;
13011da177e4SLinus Torvalds 		}
130236fd633eSAl Viro 		if (len < size)
13031da177e4SLinus Torvalds 			break;
13041da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
13051da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
13081da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
13091da177e4SLinus Torvalds 								ifa->ifa_local;
13101da177e4SLinus Torvalds 
131136fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
13121da177e4SLinus Torvalds 			done = -EFAULT;
13131da177e4SLinus Torvalds 			break;
13141da177e4SLinus Torvalds 		}
131536fd633eSAl Viro 		len  -= size;
131636fd633eSAl Viro 		done += size;
13171da177e4SLinus Torvalds 	}
13181da177e4SLinus Torvalds out:
13191da177e4SLinus Torvalds 	return done;
13201da177e4SLinus Torvalds }
13211da177e4SLinus Torvalds 
in_dev_select_addr(const struct in_device * in_dev,int scope)13228b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
13238b57fd1eSGao Feng 				 int scope)
13248b57fd1eSGao Feng {
1325d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1326d519e870SFlorian Westphal 
1327d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1328d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1329d519e870SFlorian Westphal 			continue;
13308b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
13318b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
13328b57fd1eSGao Feng 			return ifa->ifa_local;
1333d519e870SFlorian Westphal 	}
13348b57fd1eSGao Feng 
13358b57fd1eSGao Feng 	return 0;
13368b57fd1eSGao Feng }
13378b57fd1eSGao Feng 
inet_select_addr(const struct net_device * dev,__be32 dst,int scope)1338a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13391da177e4SLinus Torvalds {
1340d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1341a61ced5dSAl Viro 	__be32 addr = 0;
1342d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13431da177e4SLinus Torvalds 	struct in_device *in_dev;
1344*91d93fb5SEric Dumazet 	struct net *net;
13453f2fb9a8SDavid Ahern 	int master_idx;
13461da177e4SLinus Torvalds 
13471da177e4SLinus Torvalds 	rcu_read_lock();
1348*91d93fb5SEric Dumazet 	net = dev_net_rcu(dev);
1349e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13501da177e4SLinus Torvalds 	if (!in_dev)
13511da177e4SLinus Torvalds 		goto no_in_dev;
13521da177e4SLinus Torvalds 
1353d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1354d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1355d8c444d5SShijie Luo 
1356d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1357d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1358d519e870SFlorian Westphal 			continue;
1359d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13601da177e4SLinus Torvalds 			continue;
13611da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13621da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13631da177e4SLinus Torvalds 			break;
13641da177e4SLinus Torvalds 		}
13651da177e4SLinus Torvalds 		if (!addr)
13661da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1367d519e870SFlorian Westphal 	}
13681da177e4SLinus Torvalds 
13691da177e4SLinus Torvalds 	if (addr)
1370c6d14c84SEric Dumazet 		goto out_unlock;
13719f9354b9SEric Dumazet no_in_dev:
13723f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13731da177e4SLinus Torvalds 
137417b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
137517b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
137617b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
137717b693cdSDavid Lamparter 	 * equality check in the loop below.
137817b693cdSDavid Lamparter 	 */
137917b693cdSDavid Lamparter 	if (master_idx &&
138017b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
138117b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13828b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13838b57fd1eSGao Feng 		if (addr)
138417b693cdSDavid Lamparter 			goto out_unlock;
138517b693cdSDavid Lamparter 	}
138617b693cdSDavid Lamparter 
13871da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1388ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13891da177e4SLinus Torvalds 	   in dev_base list.
13901da177e4SLinus Torvalds 	 */
1391c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13923f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13933f2fb9a8SDavid Ahern 			continue;
13943f2fb9a8SDavid Ahern 
13959f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13969f9354b9SEric Dumazet 		if (!in_dev)
13971da177e4SLinus Torvalds 			continue;
13981da177e4SLinus Torvalds 
13998b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
14008b57fd1eSGao Feng 		if (addr)
1401c6d14c84SEric Dumazet 			goto out_unlock;
14021da177e4SLinus Torvalds 	}
1403c6d14c84SEric Dumazet out_unlock:
14041da177e4SLinus Torvalds 	rcu_read_unlock();
14051da177e4SLinus Torvalds 	return addr;
14061da177e4SLinus Torvalds }
14079f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
14081da177e4SLinus Torvalds 
confirm_addr_indev(struct in_device * in_dev,__be32 dst,__be32 local,int scope)140960cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
141060cad5daSAl Viro 			      __be32 local, int scope)
14111da177e4SLinus Torvalds {
1412650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1413ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1414a144ea4bSAl Viro 	__be32 addr = 0;
1415ef11db33SFlorian Westphal 	int same = 0;
14161da177e4SLinus Torvalds 
1417650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1418650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1419650638a7SShijie Luo 
1420ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1421650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1422650638a7SShijie Luo 
14231da177e4SLinus Torvalds 		if (!addr &&
14241da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1425650638a7SShijie Luo 		    min_scope <= scope) {
14261da177e4SLinus Torvalds 			addr = ifa->ifa_local;
14271da177e4SLinus Torvalds 			if (same)
14281da177e4SLinus Torvalds 				break;
14291da177e4SLinus Torvalds 		}
14301da177e4SLinus Torvalds 		if (!same) {
14311da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
14321da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
14331da177e4SLinus Torvalds 			if (same && addr) {
14341da177e4SLinus Torvalds 				if (local || !dst)
14351da177e4SLinus Torvalds 					break;
14361da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
14371da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
14381da177e4SLinus Torvalds 					break;
14391da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1440650638a7SShijie Luo 				if (min_scope <= scope) {
14411da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14421da177e4SLinus Torvalds 					break;
14431da177e4SLinus Torvalds 				}
14441da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14451da177e4SLinus Torvalds 				same = 0;
14461da177e4SLinus Torvalds 			}
14471da177e4SLinus Torvalds 		}
1448ef11db33SFlorian Westphal 	}
14491da177e4SLinus Torvalds 
14501da177e4SLinus Torvalds 	return same ? addr : 0;
14511da177e4SLinus Torvalds }
14521da177e4SLinus Torvalds 
14531da177e4SLinus Torvalds /*
14541da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1455b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1456b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14571da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14581da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14591da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14601da177e4SLinus Torvalds  */
inet_confirm_addr(struct net * net,struct in_device * in_dev,__be32 dst,__be32 local,int scope)1461b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14629bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14631da177e4SLinus Torvalds {
146460cad5daSAl Viro 	__be32 addr = 0;
14659bd85e32SDenis V. Lunev 	struct net_device *dev;
14661da177e4SLinus Torvalds 
146700db4124SIan Morris 	if (in_dev)
14689bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14691da177e4SLinus Torvalds 
14701da177e4SLinus Torvalds 	rcu_read_lock();
1471c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14729f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14739f9354b9SEric Dumazet 		if (in_dev) {
14741da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14751da177e4SLinus Torvalds 			if (addr)
14761da177e4SLinus Torvalds 				break;
14771da177e4SLinus Torvalds 		}
14781da177e4SLinus Torvalds 	}
14791da177e4SLinus Torvalds 	rcu_read_unlock();
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds 	return addr;
14821da177e4SLinus Torvalds }
1483eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14841da177e4SLinus Torvalds 
14851da177e4SLinus Torvalds /*
14861da177e4SLinus Torvalds  *	Device notifier
14871da177e4SLinus Torvalds  */
14881da177e4SLinus Torvalds 
register_inetaddr_notifier(struct notifier_block * nb)14891da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14901da177e4SLinus Torvalds {
1491e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14921da177e4SLinus Torvalds }
14939f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14941da177e4SLinus Torvalds 
unregister_inetaddr_notifier(struct notifier_block * nb)14951da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14961da177e4SLinus Torvalds {
1497e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14981da177e4SLinus Torvalds }
14999f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
15001da177e4SLinus Torvalds 
register_inetaddr_validator_notifier(struct notifier_block * nb)15013ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
15023ad7d246SKrister Johansen {
15033ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
15043ad7d246SKrister Johansen }
15053ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
15063ad7d246SKrister Johansen 
unregister_inetaddr_validator_notifier(struct notifier_block * nb)15073ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
15083ad7d246SKrister Johansen {
15093ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
15103ad7d246SKrister Johansen 	    nb);
15113ad7d246SKrister Johansen }
15123ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
15133ad7d246SKrister Johansen 
15149f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
15159f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
15161da177e4SLinus Torvalds */
inetdev_changename(struct net_device * dev,struct in_device * in_dev)15171da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
15181da177e4SLinus Torvalds {
15191da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
15201da177e4SLinus Torvalds 	int named = 0;
15211da177e4SLinus Torvalds 
1522ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
15231da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
15241da177e4SLinus Torvalds 
15251da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
15261da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15271da177e4SLinus Torvalds 		if (named++ == 0)
1528573bf470SThomas Graf 			goto skip;
152944344b2aSMark McLoughlin 		dot = strchr(old, ':');
153051456b29SIan Morris 		if (!dot) {
15311da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
15321da177e4SLinus Torvalds 			dot = old;
15331da177e4SLinus Torvalds 		}
15349f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
15351da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
15369f9354b9SEric Dumazet 		else
15371da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1538573bf470SThomas Graf skip:
1539573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15401da177e4SLinus Torvalds 	}
15411da177e4SLinus Torvalds }
15421da177e4SLinus Torvalds 
inetdev_send_gratuitous_arp(struct net_device * dev,struct in_device * in_dev)1543d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1544d11327adSIan Campbell 					struct in_device *in_dev)
1545d11327adSIan Campbell 
1546d11327adSIan Campbell {
1547ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1548d11327adSIan Campbell 
1549ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1550d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15516c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15526c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1553d11327adSIan Campbell 			 dev->dev_addr, NULL);
1554d11327adSIan Campbell 	}
1555b76d0789SZoltan Kiss }
1556d11327adSIan Campbell 
15571da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15581da177e4SLinus Torvalds 
inetdev_event(struct notifier_block * this,unsigned long event,void * ptr)15591da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15601da177e4SLinus Torvalds 			 void *ptr)
15611da177e4SLinus Torvalds {
1562351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1563748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15641da177e4SLinus Torvalds 
15651da177e4SLinus Torvalds 	ASSERT_RTNL();
15661da177e4SLinus Torvalds 
15671da177e4SLinus Torvalds 	if (!in_dev) {
15688030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15691da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
157020e61da7SWANG Cong 			if (IS_ERR(in_dev))
157120e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15720cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
157342f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
157442f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15751da177e4SLinus Torvalds 			}
157606770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
157706770843SBreno Leitao 			/* Re-enabling IP */
157806770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
157906770843SBreno Leitao 				in_dev = inetdev_init(dev);
15808030f544SHerbert Xu 		}
15811da177e4SLinus Torvalds 		goto out;
15821da177e4SLinus Torvalds 	}
15831da177e4SLinus Torvalds 
15841da177e4SLinus Torvalds 	switch (event) {
15851da177e4SLinus Torvalds 	case NETDEV_REGISTER:
158691df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1587a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15881da177e4SLinus Torvalds 		break;
15891da177e4SLinus Torvalds 	case NETDEV_UP:
159006770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15911da177e4SLinus Torvalds 			break;
15920cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15939f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15949f9354b9SEric Dumazet 
15959f9354b9SEric Dumazet 			if (ifa) {
1596fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15971da177e4SLinus Torvalds 				ifa->ifa_local =
15981da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15991da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
16001da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
16011da177e4SLinus Torvalds 				in_dev_hold(in_dev);
16021da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
16031da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
16041da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
16055c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
16065c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1607dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1608dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
16091da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
16101da177e4SLinus Torvalds 			}
16111da177e4SLinus Torvalds 		}
16121da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1613a8eceea8SJoe Perches 		fallthrough;
1614eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1615d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1616d11327adSIan Campbell 			break;
1617a8eceea8SJoe Perches 		fallthrough;
1618d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1619a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1620d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
16211da177e4SLinus Torvalds 		break;
16221da177e4SLinus Torvalds 	case NETDEV_DOWN:
16231da177e4SLinus Torvalds 		ip_mc_down(in_dev);
16241da177e4SLinus Torvalds 		break;
162593d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
162675c78500SMoni Shoua 		ip_mc_unmap(in_dev);
162775c78500SMoni Shoua 		break;
162893d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
162975c78500SMoni Shoua 		ip_mc_remap(in_dev);
163075c78500SMoni Shoua 		break;
16311da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
163206770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
16331da177e4SLinus Torvalds 			break;
163406770843SBreno Leitao 		/* disable IP when MTU is not enough */
1635a8eceea8SJoe Perches 		fallthrough;
16361da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
16371da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
16381da177e4SLinus Torvalds 		break;
16391da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16401da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16411da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16421da177e4SLinus Torvalds 		 */
16431da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16441da177e4SLinus Torvalds 
164551602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
164666f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16471da177e4SLinus Torvalds 		break;
16481da177e4SLinus Torvalds 	}
16491da177e4SLinus Torvalds out:
16501da177e4SLinus Torvalds 	return NOTIFY_DONE;
16511da177e4SLinus Torvalds }
16521da177e4SLinus Torvalds 
16531da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16541da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16551da177e4SLinus Torvalds };
16561da177e4SLinus Torvalds 
inet_nlmsg_size(void)165740384999SEric Dumazet static size_t inet_nlmsg_size(void)
1658339bf98fSThomas Graf {
1659339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1660339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1661339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1662339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1663ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
166463b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
166547f0bd50SJacques de Laval 	       + nla_total_size(1)  /* IFA_PROTO */
1666af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
166763b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1668339bf98fSThomas Graf }
1669339bf98fSThomas Graf 
cstamp_delta(unsigned long cstamp)16705c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16715c766d64SJiri Pirko {
16725c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16735c766d64SJiri Pirko }
16745c766d64SJiri Pirko 
put_cacheinfo(struct sk_buff * skb,unsigned long cstamp,unsigned long tstamp,u32 preferred,u32 valid)16755c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16765c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16775c766d64SJiri Pirko {
16785c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16795c766d64SJiri Pirko 
16805c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16815c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16825c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16835c766d64SJiri Pirko 	ci.ifa_valid = valid;
16845c766d64SJiri Pirko 
16855c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16865c766d64SJiri Pirko }
16875c766d64SJiri Pirko 
inet_fill_ifaddr(struct sk_buff * skb,struct in_ifaddr * ifa,struct inet_fill_args * args)16881da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1689978a46faSChristian Brauner 			    struct inet_fill_args *args)
16901da177e4SLinus Torvalds {
16911da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16921da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16935c766d64SJiri Pirko 	u32 preferred, valid;
16941da177e4SLinus Torvalds 
1695978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1696978a46faSChristian Brauner 			args->flags);
169751456b29SIan Morris 	if (!nlh)
169826932566SPatrick McHardy 		return -EMSGSIZE;
169947f68512SThomas Graf 
170047f68512SThomas Graf 	ifm = nlmsg_data(nlh);
17011da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
17021da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
17035c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
17041da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
17051da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
17061da177e4SLinus Torvalds 
1707978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1708978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1709d3807145SChristian Brauner 		goto nla_put_failure;
1710d3807145SChristian Brauner 
17115c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
17125c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
17135c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
17145c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
17155c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
17165c766d64SJiri Pirko 
17175c766d64SJiri Pirko 			if (preferred > tval)
17185c766d64SJiri Pirko 				preferred -= tval;
17195c766d64SJiri Pirko 			else
17205c766d64SJiri Pirko 				preferred = 0;
17215c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
17225c766d64SJiri Pirko 				if (valid > tval)
17235c766d64SJiri Pirko 					valid -= tval;
17245c766d64SJiri Pirko 				else
17255c766d64SJiri Pirko 					valid = 0;
17265c766d64SJiri Pirko 			}
17275c766d64SJiri Pirko 		}
17285c766d64SJiri Pirko 	} else {
17295c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
17305c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
17315c766d64SJiri Pirko 	}
1732f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1733930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1734f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1735930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1736f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1737930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1738f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
17395c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
174047f0bd50SJacques de Laval 	    (ifa->ifa_proto &&
174147f0bd50SJacques de Laval 	     nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
1742ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1743af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1744af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
17455c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
17465c766d64SJiri Pirko 			  preferred, valid))
1747f3756b79SDavid S. Miller 		goto nla_put_failure;
174847f68512SThomas Graf 
1749053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1750053c095aSJohannes Berg 	return 0;
175147f68512SThomas Graf 
175247f68512SThomas Graf nla_put_failure:
175326932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
175426932566SPatrick McHardy 	return -EMSGSIZE;
17551da177e4SLinus Torvalds }
17561da177e4SLinus Torvalds 
inet_valid_dump_ifaddr_req(const struct nlmsghdr * nlh,struct inet_fill_args * fillargs,struct net ** tgt_net,struct sock * sk,struct netlink_callback * cb)1757c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1758c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1759c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17605fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1761c33078e3SDavid Ahern {
17625fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1763c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1764c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1765c33078e3SDavid Ahern 	int err, i;
1766c33078e3SDavid Ahern 
1767c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1768c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1769c33078e3SDavid Ahern 		return -EINVAL;
1770c33078e3SDavid Ahern 	}
1771c33078e3SDavid Ahern 
1772c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1773c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1774c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1775c33078e3SDavid Ahern 		return -EINVAL;
1776c33078e3SDavid Ahern 	}
17775fcd266aSDavid Ahern 
17785fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17795fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17805fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17815fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1782c33078e3SDavid Ahern 	}
1783c33078e3SDavid Ahern 
17848cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1785c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1786c33078e3SDavid Ahern 	if (err < 0)
1787c33078e3SDavid Ahern 		return err;
1788c33078e3SDavid Ahern 
1789c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1790c33078e3SDavid Ahern 		if (!tb[i])
1791c33078e3SDavid Ahern 			continue;
1792c33078e3SDavid Ahern 
1793c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1794c33078e3SDavid Ahern 			struct net *net;
1795c33078e3SDavid Ahern 
1796c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1797c33078e3SDavid Ahern 
1798c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1799c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1800bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1801c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1802c33078e3SDavid Ahern 				return PTR_ERR(net);
1803c33078e3SDavid Ahern 			}
1804c33078e3SDavid Ahern 			*tgt_net = net;
1805c33078e3SDavid Ahern 		} else {
1806c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1807c33078e3SDavid Ahern 			return -EINVAL;
1808c33078e3SDavid Ahern 		}
1809c33078e3SDavid Ahern 	}
1810c33078e3SDavid Ahern 
1811c33078e3SDavid Ahern 	return 0;
1812c33078e3SDavid Ahern }
1813c33078e3SDavid Ahern 
in_dev_dump_addr(struct in_device * in_dev,struct sk_buff * skb,struct netlink_callback * cb,int s_ip_idx,struct inet_fill_args * fillargs)18141c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
18151c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
18161c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
18171c98eca4SDavid Ahern {
18181c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
18191c98eca4SDavid Ahern 	int ip_idx = 0;
18201c98eca4SDavid Ahern 	int err;
18211c98eca4SDavid Ahern 
1822d3e6e285SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1823ef11db33SFlorian Westphal 		if (ip_idx < s_ip_idx) {
1824ef11db33SFlorian Westphal 			ip_idx++;
18251c98eca4SDavid Ahern 			continue;
1826ef11db33SFlorian Westphal 		}
18271c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
18281c98eca4SDavid Ahern 		if (err < 0)
18291c98eca4SDavid Ahern 			goto done;
18301c98eca4SDavid Ahern 
18311c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1832ef11db33SFlorian Westphal 		ip_idx++;
18331c98eca4SDavid Ahern 	}
18341c98eca4SDavid Ahern 	err = 0;
18351c98eca4SDavid Ahern 
18361c98eca4SDavid Ahern done:
18371c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
18381c98eca4SDavid Ahern 
18391c98eca4SDavid Ahern 	return err;
18401c98eca4SDavid Ahern }
18411c98eca4SDavid Ahern 
18426634a8ecSEric Dumazet /* Combine dev_addr_genid and dev_base_seq to detect changes.
18436634a8ecSEric Dumazet  */
inet_base_seq(const struct net * net)18446634a8ecSEric Dumazet static u32 inet_base_seq(const struct net *net)
18456634a8ecSEric Dumazet {
18466634a8ecSEric Dumazet 	u32 res = atomic_read(&net->ipv4.dev_addr_genid) +
18476634a8ecSEric Dumazet 		  net->dev_base_seq;
18486634a8ecSEric Dumazet 
18496634a8ecSEric Dumazet 	/* Must not return 0 (see nl_dump_check_consistent()).
18506634a8ecSEric Dumazet 	 * Chose a value far away from 0.
18516634a8ecSEric Dumazet 	 */
18526634a8ecSEric Dumazet 	if (!res)
18536634a8ecSEric Dumazet 		res = 0x80000000;
18546634a8ecSEric Dumazet 	return res;
18556634a8ecSEric Dumazet }
18566634a8ecSEric Dumazet 
inet_dump_ifaddr(struct sk_buff * skb,struct netlink_callback * cb)18571da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18581da177e4SLinus Torvalds {
1859c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1860978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1861978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1862c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1863978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1864978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1865978a46faSChristian Brauner 		.netnsid = -1,
1866978a46faSChristian Brauner 	};
18673b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1868d3807145SChristian Brauner 	struct net *tgt_net = net;
1869eec4df98SEric Dumazet 	int h, s_h;
1870eec4df98SEric Dumazet 	int idx, s_idx;
18711c98eca4SDavid Ahern 	int s_ip_idx;
18721da177e4SLinus Torvalds 	struct net_device *dev;
18731da177e4SLinus Torvalds 	struct in_device *in_dev;
1874eec4df98SEric Dumazet 	struct hlist_head *head;
1875d7e38611SDavid Ahern 	int err = 0;
18761da177e4SLinus Torvalds 
1877eec4df98SEric Dumazet 	s_h = cb->args[0];
1878eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
18791c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1880eec4df98SEric Dumazet 
1881c33078e3SDavid Ahern 	if (cb->strict_check) {
1882c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18835fcd266aSDavid Ahern 						 skb->sk, cb);
1884c33078e3SDavid Ahern 		if (err < 0)
1885d7e38611SDavid Ahern 			goto put_tgt_net;
18865fcd266aSDavid Ahern 
1887d7e38611SDavid Ahern 		err = 0;
18885fcd266aSDavid Ahern 		if (fillargs.ifindex) {
18895fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1890d7e38611SDavid Ahern 			if (!dev) {
1891d7e38611SDavid Ahern 				err = -ENODEV;
1892d7e38611SDavid Ahern 				goto put_tgt_net;
1893d7e38611SDavid Ahern 			}
18945fcd266aSDavid Ahern 
18955fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
18965fcd266aSDavid Ahern 			if (in_dev) {
18975fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18985fcd266aSDavid Ahern 						       &fillargs);
18995fcd266aSDavid Ahern 			}
19005fcd266aSDavid Ahern 			goto put_tgt_net;
19015fcd266aSDavid Ahern 		}
1902d3807145SChristian Brauner 	}
1903d3807145SChristian Brauner 
1904eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
19057562f876SPavel Emelianov 		idx = 0;
1906d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1907eec4df98SEric Dumazet 		rcu_read_lock();
19086634a8ecSEric Dumazet 		cb->seq = inet_base_seq(tgt_net);
1909b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
19101da177e4SLinus Torvalds 			if (idx < s_idx)
19117562f876SPavel Emelianov 				goto cont;
19124b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
19131da177e4SLinus Torvalds 				s_ip_idx = 0;
1914eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
19159f9354b9SEric Dumazet 			if (!in_dev)
19167562f876SPavel Emelianov 				goto cont;
19171da177e4SLinus Torvalds 
19181c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
19191c98eca4SDavid Ahern 					       &fillargs);
19201c98eca4SDavid Ahern 			if (err < 0) {
1921eec4df98SEric Dumazet 				rcu_read_unlock();
19221da177e4SLinus Torvalds 				goto done;
19231da177e4SLinus Torvalds 			}
19247562f876SPavel Emelianov cont:
19257562f876SPavel Emelianov 			idx++;
19261da177e4SLinus Torvalds 		}
1927eec4df98SEric Dumazet 		rcu_read_unlock();
1928eec4df98SEric Dumazet 	}
19291da177e4SLinus Torvalds 
19301da177e4SLinus Torvalds done:
1931eec4df98SEric Dumazet 	cb->args[0] = h;
1932eec4df98SEric Dumazet 	cb->args[1] = idx;
19335fcd266aSDavid Ahern put_tgt_net:
1934978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1935d3807145SChristian Brauner 		put_net(tgt_net);
19361da177e4SLinus Torvalds 
19377c1e8a38SArthur Gautier 	return skb->len ? : err;
19381da177e4SLinus Torvalds }
19391da177e4SLinus Torvalds 
rtmsg_ifa(int event,struct in_ifaddr * ifa,struct nlmsghdr * nlh,u32 portid)1940d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
194115e47304SEric W. Biederman 		      u32 portid)
19421da177e4SLinus Torvalds {
1943978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1944978a46faSChristian Brauner 		.portid = portid,
1945978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1946978a46faSChristian Brauner 		.event = event,
1947978a46faSChristian Brauner 		.flags = 0,
1948978a46faSChristian Brauner 		.netnsid = -1,
1949978a46faSChristian Brauner 	};
195047f68512SThomas Graf 	struct sk_buff *skb;
1951d6062cbbSThomas Graf 	int err = -ENOBUFS;
19524b8aa9abSDenis V. Lunev 	struct net *net;
19531da177e4SLinus Torvalds 
1954c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1955339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
195651456b29SIan Morris 	if (!skb)
1957d6062cbbSThomas Graf 		goto errout;
1958d6062cbbSThomas Graf 
1959978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
196026932566SPatrick McHardy 	if (err < 0) {
196126932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
196226932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
196326932566SPatrick McHardy 		kfree_skb(skb);
196426932566SPatrick McHardy 		goto errout;
196526932566SPatrick McHardy 	}
196615e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19671ce85fe4SPablo Neira Ayuso 	return;
1968d6062cbbSThomas Graf errout:
1969d6062cbbSThomas Graf 	if (err < 0)
19704b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19711da177e4SLinus Torvalds }
19721da177e4SLinus Torvalds 
inet_get_link_af_size(const struct net_device * dev,u32 ext_filter_mask)1973b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1974b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19759f0f7272SThomas Graf {
19761fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19779f0f7272SThomas Graf 
19789f0f7272SThomas Graf 	if (!in_dev)
19799f0f7272SThomas Graf 		return 0;
19809f0f7272SThomas Graf 
19819f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19829f0f7272SThomas Graf }
19839f0f7272SThomas Graf 
inet_fill_link_af(struct sk_buff * skb,const struct net_device * dev,u32 ext_filter_mask)1984d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1985d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19869f0f7272SThomas Graf {
19871fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19889f0f7272SThomas Graf 	struct nlattr *nla;
19899f0f7272SThomas Graf 	int i;
19909f0f7272SThomas Graf 
19919f0f7272SThomas Graf 	if (!in_dev)
19929f0f7272SThomas Graf 		return -ENODATA;
19939f0f7272SThomas Graf 
19949f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
199551456b29SIan Morris 	if (!nla)
19969f0f7272SThomas Graf 		return -EMSGSIZE;
19979f0f7272SThomas Graf 
19989f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19999f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
20009f0f7272SThomas Graf 
20019f0f7272SThomas Graf 	return 0;
20029f0f7272SThomas Graf }
20039f0f7272SThomas Graf 
20049f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
20059f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
20069f0f7272SThomas Graf };
20079f0f7272SThomas Graf 
inet_validate_link_af(const struct net_device * dev,const struct nlattr * nla,struct netlink_ext_ack * extack)2008cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
20098679c31eSRocco Yue 				 const struct nlattr *nla,
20108679c31eSRocco Yue 				 struct netlink_ext_ack *extack)
20119f0f7272SThomas Graf {
20129f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
20139f0f7272SThomas Graf 	int err, rem;
20149f0f7272SThomas Graf 
2015a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
2016cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
20179f0f7272SThomas Graf 
20188cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
20198679c31eSRocco Yue 					  inet_af_policy, extack);
20209f0f7272SThomas Graf 	if (err < 0)
20219f0f7272SThomas Graf 		return err;
20229f0f7272SThomas Graf 
20239f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20249f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
20259f0f7272SThomas Graf 			int cfgid = nla_type(a);
20269f0f7272SThomas Graf 
20279f0f7272SThomas Graf 			if (nla_len(a) < 4)
20289f0f7272SThomas Graf 				return -EINVAL;
20299f0f7272SThomas Graf 
20309f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
20319f0f7272SThomas Graf 				return -EINVAL;
20329f0f7272SThomas Graf 		}
20339f0f7272SThomas Graf 	}
20349f0f7272SThomas Graf 
2035cf7afbfeSThomas Graf 	return 0;
2036cf7afbfeSThomas Graf }
2037cf7afbfeSThomas Graf 
inet_set_link_af(struct net_device * dev,const struct nlattr * nla,struct netlink_ext_ack * extack)20383583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
20393583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
2040cf7afbfeSThomas Graf {
2041a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
2042cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
2043cf7afbfeSThomas Graf 	int rem;
2044cf7afbfeSThomas Graf 
2045cf7afbfeSThomas Graf 	if (!in_dev)
2046cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
2047cf7afbfeSThomas Graf 
20488cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
20495ac6b198SZheng Yongjun 		return -EINVAL;
2050cf7afbfeSThomas Graf 
20519f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20529f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
20539f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
20549f0f7272SThomas Graf 	}
20559f0f7272SThomas Graf 
20569f0f7272SThomas Graf 	return 0;
20579f0f7272SThomas Graf }
20589f0f7272SThomas Graf 
inet_netconf_msgsize_devconf(int type)2059edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2060edc9e748SNicolas Dichtel {
2061edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2062edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2063136ba622SZhang Shengju 	bool all = false;
2064edc9e748SNicolas Dichtel 
2065136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2066136ba622SZhang Shengju 		all = true;
2067136ba622SZhang Shengju 
2068136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2069edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2070136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2071cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2072136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2073d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20745cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20755cbf777cSXin Long 		size += nla_total_size(4);
2076136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2077f085ff1cSstephen hemminger 		size += nla_total_size(4);
2078136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2079974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2080edc9e748SNicolas Dichtel 
2081edc9e748SNicolas Dichtel 	return size;
2082edc9e748SNicolas Dichtel }
2083edc9e748SNicolas Dichtel 
inet_netconf_fill_devconf(struct sk_buff * skb,int ifindex,struct ipv4_devconf * devconf,u32 portid,u32 seq,int event,unsigned int flags,int type)2084edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
2085edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
2086edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
2087edc9e748SNicolas Dichtel 				     int type)
2088edc9e748SNicolas Dichtel {
2089edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2090edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2091136ba622SZhang Shengju 	bool all = false;
2092edc9e748SNicolas Dichtel 
2093edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2094edc9e748SNicolas Dichtel 			flags);
209551456b29SIan Morris 	if (!nlh)
2096edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2097edc9e748SNicolas Dichtel 
2098136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2099136ba622SZhang Shengju 		all = true;
2100136ba622SZhang Shengju 
2101edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2102edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2103edc9e748SNicolas Dichtel 
2104edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2105edc9e748SNicolas Dichtel 		goto nla_put_failure;
2106edc9e748SNicolas Dichtel 
2107b5c9641dSDavid Ahern 	if (!devconf)
2108b5c9641dSDavid Ahern 		goto out;
2109b5c9641dSDavid Ahern 
2110136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2111edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2112edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2113edc9e748SNicolas Dichtel 		goto nla_put_failure;
2114136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2115cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2116cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2117cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2118136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2119d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2120d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2121d67b8c61SNicolas Dichtel 		goto nla_put_failure;
21225cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
21235cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
21245cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
21255cbf777cSXin Long 		goto nla_put_failure;
2126136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
212709aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2128f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2129f085ff1cSstephen hemminger 		goto nla_put_failure;
2130136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2131974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2132974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2133974d7af5SAndy Gospodarek 		goto nla_put_failure;
2134edc9e748SNicolas Dichtel 
2135b5c9641dSDavid Ahern out:
2136053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2137053c095aSJohannes Berg 	return 0;
2138edc9e748SNicolas Dichtel 
2139edc9e748SNicolas Dichtel nla_put_failure:
2140edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2141edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2142edc9e748SNicolas Dichtel }
2143edc9e748SNicolas Dichtel 
inet_netconf_notify_devconf(struct net * net,int event,int type,int ifindex,struct ipv4_devconf * devconf)21443b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
21453b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2146edc9e748SNicolas Dichtel {
2147edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2148edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2149edc9e748SNicolas Dichtel 
2150fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
215151456b29SIan Morris 	if (!skb)
2152edc9e748SNicolas Dichtel 		goto errout;
2153edc9e748SNicolas Dichtel 
2154edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
21553b022865SDavid Ahern 					event, 0, type);
2156edc9e748SNicolas Dichtel 	if (err < 0) {
2157edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2158edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2159edc9e748SNicolas Dichtel 		kfree_skb(skb);
2160edc9e748SNicolas Dichtel 		goto errout;
2161edc9e748SNicolas Dichtel 	}
2162fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2163edc9e748SNicolas Dichtel 	return;
2164edc9e748SNicolas Dichtel errout:
2165edc9e748SNicolas Dichtel 	if (err < 0)
2166edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2167edc9e748SNicolas Dichtel }
2168edc9e748SNicolas Dichtel 
21699e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21709e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21719e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2172cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
217309aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2174974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21759e551110SNicolas Dichtel };
21769e551110SNicolas Dichtel 
inet_netconf_valid_get_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)2177eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2178eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2179eede370dSJakub Kicinski 				      struct nlattr **tb,
2180eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2181eede370dSJakub Kicinski {
2182eede370dSJakub Kicinski 	int i, err;
2183eede370dSJakub Kicinski 
2184eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2185eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2186eede370dSJakub Kicinski 		return -EINVAL;
2187eede370dSJakub Kicinski 	}
2188eede370dSJakub Kicinski 
2189eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21908cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21918cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21928cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2193eede370dSJakub Kicinski 
21948cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21958cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21968cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2197eede370dSJakub Kicinski 	if (err)
2198eede370dSJakub Kicinski 		return err;
2199eede370dSJakub Kicinski 
2200eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2201eede370dSJakub Kicinski 		if (!tb[i])
2202eede370dSJakub Kicinski 			continue;
2203eede370dSJakub Kicinski 
2204eede370dSJakub Kicinski 		switch (i) {
2205eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2206eede370dSJakub Kicinski 			break;
2207eede370dSJakub Kicinski 		default:
2208eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2209eede370dSJakub Kicinski 			return -EINVAL;
2210eede370dSJakub Kicinski 		}
2211eede370dSJakub Kicinski 	}
2212eede370dSJakub Kicinski 
2213eede370dSJakub Kicinski 	return 0;
2214eede370dSJakub Kicinski }
2215eede370dSJakub Kicinski 
inet_netconf_get_devconf(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)22169e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2217c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2218c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
22199e551110SNicolas Dichtel {
22209e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
22219e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
22229e551110SNicolas Dichtel 	struct sk_buff *skb;
22239e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
22249e551110SNicolas Dichtel 	struct in_device *in_dev;
22259e551110SNicolas Dichtel 	struct net_device *dev;
22269e551110SNicolas Dichtel 	int ifindex;
22279e551110SNicolas Dichtel 	int err;
22289e551110SNicolas Dichtel 
2229eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2230eede370dSJakub Kicinski 	if (err)
22319e551110SNicolas Dichtel 		goto errout;
22329e551110SNicolas Dichtel 
2233a97eb33fSAnton Protopopov 	err = -EINVAL;
22349e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
22359e551110SNicolas Dichtel 		goto errout;
22369e551110SNicolas Dichtel 
22379e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
22389e551110SNicolas Dichtel 	switch (ifindex) {
22399e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
22409e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
22419e551110SNicolas Dichtel 		break;
22429e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
22439e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
22449e551110SNicolas Dichtel 		break;
22459e551110SNicolas Dichtel 	default:
22469e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
224751456b29SIan Morris 		if (!dev)
22489e551110SNicolas Dichtel 			goto errout;
22499e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
225051456b29SIan Morris 		if (!in_dev)
22519e551110SNicolas Dichtel 			goto errout;
22529e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
22539e551110SNicolas Dichtel 		break;
22549e551110SNicolas Dichtel 	}
22559e551110SNicolas Dichtel 
22569e551110SNicolas Dichtel 	err = -ENOBUFS;
2257fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
225851456b29SIan Morris 	if (!skb)
22599e551110SNicolas Dichtel 		goto errout;
22609e551110SNicolas Dichtel 
22619e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22629e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22639e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2264136ba622SZhang Shengju 					NETCONFA_ALL);
22659e551110SNicolas Dichtel 	if (err < 0) {
22669e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22679e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22689e551110SNicolas Dichtel 		kfree_skb(skb);
22699e551110SNicolas Dichtel 		goto errout;
22709e551110SNicolas Dichtel 	}
22719e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22729e551110SNicolas Dichtel errout:
22739e551110SNicolas Dichtel 	return err;
22749e551110SNicolas Dichtel }
22759e551110SNicolas Dichtel 
inet_netconf_dump_devconf(struct sk_buff * skb,struct netlink_callback * cb)22767a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22777a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22787a674200SNicolas Dichtel {
2279addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22807a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
22817a674200SNicolas Dichtel 	int h, s_h;
22827a674200SNicolas Dichtel 	int idx, s_idx;
22837a674200SNicolas Dichtel 	struct net_device *dev;
22847a674200SNicolas Dichtel 	struct in_device *in_dev;
22857a674200SNicolas Dichtel 	struct hlist_head *head;
22867a674200SNicolas Dichtel 
2287addd383fSDavid Ahern 	if (cb->strict_check) {
2288addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2289addd383fSDavid Ahern 		struct netconfmsg *ncm;
2290addd383fSDavid Ahern 
2291addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2292addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2293addd383fSDavid Ahern 			return -EINVAL;
2294addd383fSDavid Ahern 		}
2295addd383fSDavid Ahern 
2296addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2297addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2298addd383fSDavid Ahern 			return -EINVAL;
2299addd383fSDavid Ahern 		}
2300addd383fSDavid Ahern 	}
2301addd383fSDavid Ahern 
23027a674200SNicolas Dichtel 	s_h = cb->args[0];
23037a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
23047a674200SNicolas Dichtel 
23057a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
23067a674200SNicolas Dichtel 		idx = 0;
23077a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
23087a674200SNicolas Dichtel 		rcu_read_lock();
23096634a8ecSEric Dumazet 		cb->seq = inet_base_seq(net);
23107a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
23117a674200SNicolas Dichtel 			if (idx < s_idx)
23127a674200SNicolas Dichtel 				goto cont;
23137a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
23147a674200SNicolas Dichtel 			if (!in_dev)
23157a674200SNicolas Dichtel 				goto cont;
23167a674200SNicolas Dichtel 
23177a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
23187a674200SNicolas Dichtel 						      &in_dev->cnf,
23197a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2320addd383fSDavid Ahern 						      nlh->nlmsg_seq,
23217a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
23227a674200SNicolas Dichtel 						      NLM_F_MULTI,
2323136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
23247a674200SNicolas Dichtel 				rcu_read_unlock();
23257a674200SNicolas Dichtel 				goto done;
23267a674200SNicolas Dichtel 			}
23270465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
23287a674200SNicolas Dichtel cont:
23297a674200SNicolas Dichtel 			idx++;
23307a674200SNicolas Dichtel 		}
23317a674200SNicolas Dichtel 		rcu_read_unlock();
23327a674200SNicolas Dichtel 	}
23337a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
23347a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
23357a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
23367a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2337addd383fSDavid Ahern 					      nlh->nlmsg_seq,
23387a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2339136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
23407a674200SNicolas Dichtel 			goto done;
23417a674200SNicolas Dichtel 		else
23427a674200SNicolas Dichtel 			h++;
23437a674200SNicolas Dichtel 	}
23447a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
23457a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
23467a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
23477a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2348addd383fSDavid Ahern 					      nlh->nlmsg_seq,
23497a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2350136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
23517a674200SNicolas Dichtel 			goto done;
23527a674200SNicolas Dichtel 		else
23537a674200SNicolas Dichtel 			h++;
23547a674200SNicolas Dichtel 	}
23557a674200SNicolas Dichtel done:
23567a674200SNicolas Dichtel 	cb->args[0] = h;
23577a674200SNicolas Dichtel 	cb->args[1] = idx;
23587a674200SNicolas Dichtel 
23597a674200SNicolas Dichtel 	return skb->len;
23607a674200SNicolas Dichtel }
23617a674200SNicolas Dichtel 
23621da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23631da177e4SLinus Torvalds 
devinet_copy_dflt_conf(struct net * net,int i)2364c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
236531be3085SHerbert Xu {
236631be3085SHerbert Xu 	struct net_device *dev;
236731be3085SHerbert Xu 
236831be3085SHerbert Xu 	rcu_read_lock();
2369c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2370c6d14c84SEric Dumazet 		struct in_device *in_dev;
2371c6d14c84SEric Dumazet 
237231be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
237331be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23749355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2375c6d14c84SEric Dumazet 	}
237631be3085SHerbert Xu 	rcu_read_unlock();
237731be3085SHerbert Xu }
237831be3085SHerbert Xu 
2379c6d14c84SEric Dumazet /* called with RTNL locked */
inet_forward_change(struct net * net)2380c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
238168dd299bSPavel Emelyanov {
238268dd299bSPavel Emelyanov 	struct net_device *dev;
2383586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
238468dd299bSPavel Emelyanov 
2385586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23869355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23873b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23883b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2389edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2390edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23913b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23923b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2393edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2394edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
239568dd299bSPavel Emelyanov 
2396c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
239768dd299bSPavel Emelyanov 		struct in_device *in_dev;
2398fa17806cSEric Dumazet 
23990187bdfbSBen Hutchings 		if (on)
24000187bdfbSBen Hutchings 			dev_disable_lro(dev);
2401fa17806cSEric Dumazet 
2402fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2403edc9e748SNicolas Dichtel 		if (in_dev) {
240468dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
24053b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24063b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2407edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2408edc9e748SNicolas Dichtel 		}
240968dd299bSPavel Emelyanov 	}
241068dd299bSPavel Emelyanov }
241168dd299bSPavel Emelyanov 
devinet_conf_ifindex(struct net * net,struct ipv4_devconf * cnf)2412f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2413f085ff1cSstephen hemminger {
2414f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2415f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2416f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2417f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2418f085ff1cSstephen hemminger 	else {
2419f085ff1cSstephen hemminger 		struct in_device *idev
2420f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2421f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2422f085ff1cSstephen hemminger 	}
2423f085ff1cSstephen hemminger }
2424f085ff1cSstephen hemminger 
devinet_conf_proc(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)2425fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
242632927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
242731be3085SHerbert Xu {
2428d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
24298d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2430d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
243131be3085SHerbert Xu 
243231be3085SHerbert Xu 	if (write) {
243331be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2434c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
243531be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2436f085ff1cSstephen hemminger 		int ifindex;
243731be3085SHerbert Xu 
243831be3085SHerbert Xu 		set_bit(i, cnf->state);
243931be3085SHerbert Xu 
24409355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2441c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2442d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2443d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2444d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
24454ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2446f085ff1cSstephen hemminger 
24475cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
24485cbf777cSXin Long 		    new_value != old_value)
24495cbf777cSXin Long 			rt_cache_flush(net);
24505cbf777cSXin Long 
2451cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2452cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2453f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24543b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24553b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2456cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2457cc535dfbSNicolas Dichtel 		}
2458f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2459f085ff1cSstephen hemminger 		    new_value != old_value) {
2460f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24613b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24623b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2463f085ff1cSstephen hemminger 						    ifindex, cnf);
2464f085ff1cSstephen hemminger 		}
2465974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2466974d7af5SAndy Gospodarek 		    new_value != old_value) {
2467974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24683b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24693b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2470974d7af5SAndy Gospodarek 						    ifindex, cnf);
2471974d7af5SAndy Gospodarek 		}
247231be3085SHerbert Xu 	}
247331be3085SHerbert Xu 
247431be3085SHerbert Xu 	return ret;
247531be3085SHerbert Xu }
247631be3085SHerbert Xu 
devinet_sysctl_forward(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)2477fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
247832927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24791da177e4SLinus Torvalds {
24801da177e4SLinus Torvalds 	int *valp = ctl->data;
24811da177e4SLinus Torvalds 	int val = *valp;
248288af182eSEric W. Biederman 	loff_t pos = *ppos;
24838292d7f6SYang Yang 	struct net *net = ctl->extra2;
24848292d7f6SYang Yang 	int ret;
24858292d7f6SYang Yang 
24868292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24878292d7f6SYang Yang 		return -EPERM;
24888292d7f6SYang Yang 
24898292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24901da177e4SLinus Torvalds 
24911da177e4SLinus Torvalds 	if (write && *valp != val) {
24920187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
249388af182eSEric W. Biederman 			if (!rtnl_trylock()) {
249488af182eSEric W. Biederman 				/* Restore the original values before restarting */
249588af182eSEric W. Biederman 				*valp = val;
249688af182eSEric W. Biederman 				*ppos = pos;
24979b8adb5eSEric W. Biederman 				return restart_syscall();
249888af182eSEric W. Biederman 			}
24990187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2500c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2501edc9e748SNicolas Dichtel 			} else {
25020187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
25030187bdfbSBen Hutchings 				struct in_device *idev =
25040187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2505edc9e748SNicolas Dichtel 				if (*valp)
25060187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
25073b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2508edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2509edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2510edc9e748SNicolas Dichtel 							    cnf);
25110187bdfbSBen Hutchings 			}
25120187bdfbSBen Hutchings 			rtnl_unlock();
25134ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2514edc9e748SNicolas Dichtel 		} else
25153b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
25163b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2517edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2518edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
25190187bdfbSBen Hutchings 	}
25201da177e4SLinus Torvalds 
25211da177e4SLinus Torvalds 	return ret;
25221da177e4SLinus Torvalds }
25231da177e4SLinus Torvalds 
ipv4_doint_and_flush(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)2524fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
252532927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
25261da177e4SLinus Torvalds {
25271da177e4SLinus Torvalds 	int *valp = ctl->data;
25281da177e4SLinus Torvalds 	int val = *valp;
25298d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
253076e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
25311da177e4SLinus Torvalds 
25321da177e4SLinus Torvalds 	if (write && *valp != val)
25334ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
25341da177e4SLinus Torvalds 
25351da177e4SLinus Torvalds 	return ret;
25361da177e4SLinus Torvalds }
25371da177e4SLinus Torvalds 
2538f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
253942f811b8SHerbert Xu 	{ \
254042f811b8SHerbert Xu 		.procname	= name, \
254142f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
254202291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
254342f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
254442f811b8SHerbert Xu 		.mode		= mval, \
254542f811b8SHerbert Xu 		.proc_handler	= proc, \
254631be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
254742f811b8SHerbert Xu 	}
254842f811b8SHerbert Xu 
254942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2550f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
255142f811b8SHerbert Xu 
255242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2553f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
255442f811b8SHerbert Xu 
2555f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2556f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
255742f811b8SHerbert Xu 
255842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2559f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
256042f811b8SHerbert Xu 
25611da177e4SLinus Torvalds static struct devinet_sysctl_table {
25621da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
256302291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
25641da177e4SLinus Torvalds } devinet_sysctl = {
25651da177e4SLinus Torvalds 	.devinet_vars = {
256642f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2567f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
256842f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25695cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
257042f811b8SHerbert Xu 
257142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
257242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
257342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
257442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
257542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
257642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
257742f811b8SHerbert Xu 					"accept_source_route"),
25788153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
257928f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
258042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
258142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
258242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
258342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
258442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
258542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
258642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
258742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
258842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2589eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
2590fcdb44d0SJames Prestwood 		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
2591fcdb44d0SJames Prestwood 					"arp_evict_nocarrier"),
259265324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25935c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25945c6fe01cSWilliam Manley 					"force_igmp_version"),
25952690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25962690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25972690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25982690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25990eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
26000eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
260197daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
260297daf331SJohannes Berg 					"drop_gratuitous_arp"),
260342f811b8SHerbert Xu 
260442f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
260542f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
260642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
260742f811b8SHerbert Xu 					      "promote_secondaries"),
2608d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2609d0daebc3SThomas Graf 					      "route_localnet"),
261012b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
261112b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
26121da177e4SLinus Torvalds 	},
26131da177e4SLinus Torvalds };
26141da177e4SLinus Torvalds 
__devinet_sysctl_register(struct net * net,char * dev_name,int ifindex,struct ipv4_devconf * p)2615ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
261629c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
26171da177e4SLinus Torvalds {
26181da177e4SLinus Torvalds 	int i;
26199fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
26208607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2621bfada697SPavel Emelyanov 
2622425b9c7fSVasily Averin 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT);
26231da177e4SLinus Torvalds 	if (!t)
26249fa89642SPavel Emelyanov 		goto out;
26259fa89642SPavel Emelyanov 
26261da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
26271da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
262831be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2629c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
26301da177e4SLinus Torvalds 	}
26311da177e4SLinus Torvalds 
26328607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
26331da177e4SLinus Torvalds 
26348607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
26351da177e4SLinus Torvalds 	if (!t->sysctl_header)
26368607ddb8SEric W. Biederman 		goto free;
26371da177e4SLinus Torvalds 
26381da177e4SLinus Torvalds 	p->sysctl = t;
263929c994e3SNicolas Dichtel 
26403b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
26413b022865SDavid Ahern 				    ifindex, p);
2642ea40b324SPavel Emelyanov 	return 0;
26431da177e4SLinus Torvalds 
26441da177e4SLinus Torvalds free:
26451da177e4SLinus Torvalds 	kfree(t);
26469fa89642SPavel Emelyanov out:
26476def4801Sliuguoqiang 	return -ENOMEM;
26481da177e4SLinus Torvalds }
26491da177e4SLinus Torvalds 
__devinet_sysctl_unregister(struct net * net,struct ipv4_devconf * cnf,int ifindex)2650b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2651b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
265266f27a52SPavel Emelyanov {
265351602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
265466f27a52SPavel Emelyanov 
2655b5c9641dSDavid Ahern 	if (t) {
265651602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2657ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26581da177e4SLinus Torvalds 		kfree(t);
26591da177e4SLinus Torvalds 	}
266051602b2aSPavel Emelyanov 
2661b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2662b5c9641dSDavid Ahern }
2663b5c9641dSDavid Ahern 
devinet_sysctl_register(struct in_device * idev)266420e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
266551602b2aSPavel Emelyanov {
266620e61da7SWANG Cong 	int err;
266720e61da7SWANG Cong 
266820e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
266920e61da7SWANG Cong 		return -EINVAL;
267020e61da7SWANG Cong 
267120e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
267220e61da7SWANG Cong 	if (err)
267320e61da7SWANG Cong 		return err;
267420e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
267529c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
267620e61da7SWANG Cong 	if (err)
267720e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
267820e61da7SWANG Cong 	return err;
267951602b2aSPavel Emelyanov }
268051602b2aSPavel Emelyanov 
devinet_sysctl_unregister(struct in_device * idev)268151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
268251602b2aSPavel Emelyanov {
2683b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2684b5c9641dSDavid Ahern 
2685b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
268651602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26871da177e4SLinus Torvalds }
26881da177e4SLinus Torvalds 
268968dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
269068dd299bSPavel Emelyanov 	{
269168dd299bSPavel Emelyanov 		.procname	= "ip_forward",
269268dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
269302291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
269468dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
269568dd299bSPavel Emelyanov 		.mode		= 0644,
269668dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
269768dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2698c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
269968dd299bSPavel Emelyanov 	},
270068dd299bSPavel Emelyanov 	{ },
270168dd299bSPavel Emelyanov };
27022a75de0cSEric Dumazet #endif
270368dd299bSPavel Emelyanov 
devinet_init_net(struct net * net)2704752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2705752d14dcSPavel Emelyanov {
2706752d14dcSPavel Emelyanov 	int err;
2707752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
27082a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2709856c395cSCong Wang 	struct ctl_table *tbl;
2710752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
27112a75de0cSEric Dumazet #endif
2712752d14dcSPavel Emelyanov 
2713752d14dcSPavel Emelyanov 	err = -ENOMEM;
2714856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
271551456b29SIan Morris 	if (!all)
2716752d14dcSPavel Emelyanov 		goto err_alloc_all;
2717752d14dcSPavel Emelyanov 
2718856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
271951456b29SIan Morris 	if (!dflt)
2720752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2721752d14dcSPavel Emelyanov 
27222a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2723856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
272451456b29SIan Morris 	if (!tbl)
2725752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2726752d14dcSPavel Emelyanov 
272702291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2728752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2729752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
27302a75de0cSEric Dumazet #endif
2731856c395cSCong Wang 
27329efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
2733a5612ca1SKuniyuki Iwashima 		switch (net_inherit_devconf()) {
2734a5612ca1SKuniyuki Iwashima 		case 3:
27359efd6a3cSNicolas Dichtel 			/* copy from the current netns */
27369efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
27379efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27389efd6a3cSNicolas Dichtel 			memcpy(dflt,
27399efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
27409efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2741a5612ca1SKuniyuki Iwashima 			break;
2742a5612ca1SKuniyuki Iwashima 		case 0:
2743a5612ca1SKuniyuki Iwashima 		case 1:
2744a5612ca1SKuniyuki Iwashima 			/* copy from init_net */
27459efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
27469efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27479efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
27489efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2749a5612ca1SKuniyuki Iwashima 			break;
2750a5612ca1SKuniyuki Iwashima 		case 2:
2751a5612ca1SKuniyuki Iwashima 			/* use compiled values */
2752a5612ca1SKuniyuki Iwashima 			break;
27539efd6a3cSNicolas Dichtel 		}
2754752d14dcSPavel Emelyanov 	}
2755752d14dcSPavel Emelyanov 
2756752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
275729c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2758752d14dcSPavel Emelyanov 	if (err < 0)
2759752d14dcSPavel Emelyanov 		goto err_reg_all;
2760752d14dcSPavel Emelyanov 
276129c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
276229c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2763752d14dcSPavel Emelyanov 	if (err < 0)
2764752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2765752d14dcSPavel Emelyanov 
2766752d14dcSPavel Emelyanov 	err = -ENOMEM;
2767c899710fSJoel Granados 	forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl,
2768c899710fSJoel Granados 					  ARRAY_SIZE(ctl_forward_entry));
276951456b29SIan Morris 	if (!forw_hdr)
2770752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27712a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2772752d14dcSPavel Emelyanov #endif
2773752d14dcSPavel Emelyanov 
2774752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2775752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2776752d14dcSPavel Emelyanov 	return 0;
2777752d14dcSPavel Emelyanov 
2778752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2779752d14dcSPavel Emelyanov err_reg_ctl:
2780b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2781752d14dcSPavel Emelyanov err_reg_dflt:
2782b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2783752d14dcSPavel Emelyanov err_reg_all:
2784752d14dcSPavel Emelyanov 	kfree(tbl);
2785752d14dcSPavel Emelyanov err_alloc_ctl:
27862a75de0cSEric Dumazet #endif
2787752d14dcSPavel Emelyanov 	kfree(dflt);
2788752d14dcSPavel Emelyanov err_alloc_dflt:
2789752d14dcSPavel Emelyanov 	kfree(all);
2790752d14dcSPavel Emelyanov err_alloc_all:
2791752d14dcSPavel Emelyanov 	return err;
2792752d14dcSPavel Emelyanov }
2793752d14dcSPavel Emelyanov 
devinet_exit_net(struct net * net)2794752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2795752d14dcSPavel Emelyanov {
27962a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2797752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2798752d14dcSPavel Emelyanov 
2799752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2800752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2801b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2802b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2803b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2804b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2805752d14dcSPavel Emelyanov 	kfree(tbl);
28062a75de0cSEric Dumazet #endif
2807752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2808752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2809752d14dcSPavel Emelyanov }
2810752d14dcSPavel Emelyanov 
2811752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2812752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2813752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2814752d14dcSPavel Emelyanov };
2815752d14dcSPavel Emelyanov 
2816207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
28179f0f7272SThomas Graf 	.family		  = AF_INET,
28189f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
28199f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2820cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2821cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
28229f0f7272SThomas Graf };
28239f0f7272SThomas Graf 
devinet_init(void)28241da177e4SLinus Torvalds void __init devinet_init(void)
28251da177e4SLinus Torvalds {
2826fd23c3b3SDavid S. Miller 	int i;
2827fd23c3b3SDavid S. Miller 
2828fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2829fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2830fd23c3b3SDavid S. Miller 
2831752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
28321da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
283363f3444fSThomas Graf 
2834906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
28355c766d64SJiri Pirko 
28369f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
28379f0f7272SThomas Graf 
2838b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2839b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2840b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
28419e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2842b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
28431da177e4SLinus Torvalds }
2844