xref: /openbmc/linux/net/ipv4/devinet.c (revision d4150779)
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 
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 
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 
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  */
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 */
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
20820e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20951602b2aSPavel Emelyanov {
21020e61da7SWANG Cong 	return 0;
21151602b2aSPavel Emelyanov }
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 
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 
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 
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 
2371da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2381da177e4SLinus Torvalds {
2391da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2401da177e4SLinus Torvalds 
241547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
242547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
243e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2441da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
24591df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2461da177e4SLinus Torvalds #endif
247c04438f5SEric Dumazet 	dev_put_track(dev, &idev->dev_tracker);
2481da177e4SLinus Torvalds 	if (!idev->dead)
2499f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2509f9354b9SEric Dumazet 	else
2511da177e4SLinus Torvalds 		kfree(idev);
2521da177e4SLinus Torvalds }
2539f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2541da177e4SLinus Torvalds 
25571e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2561da177e4SLinus Torvalds {
2571da177e4SLinus Torvalds 	struct in_device *in_dev;
25820e61da7SWANG Cong 	int err = -ENOMEM;
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 	ASSERT_RTNL();
2611da177e4SLinus Torvalds 
2620da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2631da177e4SLinus Torvalds 	if (!in_dev)
2641da177e4SLinus Torvalds 		goto out;
265c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2669355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2671da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2681da177e4SLinus Torvalds 	in_dev->dev = dev;
2699f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2709f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2711da177e4SLinus Torvalds 		goto out_kfree;
2720187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2730187bdfbSBen Hutchings 		dev_disable_lro(dev);
2741da177e4SLinus Torvalds 	/* Reference in_dev->dev */
275c04438f5SEric Dumazet 	dev_hold_track(dev, &in_dev->dev_tracker, GFP_KERNEL);
27630c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2777658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2781da177e4SLinus Torvalds 
27920e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
28020e61da7SWANG Cong 	if (err) {
28120e61da7SWANG Cong 		in_dev->dead = 1;
2821b49cd71SYang Yingliang 		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
28320e61da7SWANG Cong 		in_dev_put(in_dev);
28420e61da7SWANG Cong 		in_dev = NULL;
28520e61da7SWANG Cong 		goto out;
28620e61da7SWANG Cong 	}
2871da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2881da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2891da177e4SLinus Torvalds 		ip_mc_up(in_dev);
290483479ecSJarek Poplawski 
29130c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
292cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
293483479ecSJarek Poplawski out:
29420e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2951da177e4SLinus Torvalds out_kfree:
2961da177e4SLinus Torvalds 	kfree(in_dev);
2971da177e4SLinus Torvalds 	in_dev = NULL;
2981da177e4SLinus Torvalds 	goto out;
2991da177e4SLinus Torvalds }
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
3041da177e4SLinus Torvalds 	in_dev_put(idev);
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	struct net_device *dev;
3102638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	ASSERT_RTNL();
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	dev = in_dev->dev;
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	in_dev->dead = 1;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3191da177e4SLinus Torvalds 
3202638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3211da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3221da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3231da177e4SLinus Torvalds 	}
3241da177e4SLinus Torvalds 
325a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3261da177e4SLinus Torvalds 
32751602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3281da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3291da177e4SLinus Torvalds 	arp_ifdown(dev);
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3321da177e4SLinus Torvalds }
3331da177e4SLinus Torvalds 
334ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3351da177e4SLinus Torvalds {
336d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
337d519e870SFlorian Westphal 
3381da177e4SLinus Torvalds 	rcu_read_lock();
339d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3401da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3411da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3421da177e4SLinus Torvalds 				rcu_read_unlock();
3431da177e4SLinus Torvalds 				return 1;
3441da177e4SLinus Torvalds 			}
3451da177e4SLinus Torvalds 		}
346d519e870SFlorian Westphal 	}
3471da177e4SLinus Torvalds 	rcu_read_unlock();
3481da177e4SLinus Torvalds 	return 0;
3491da177e4SLinus Torvalds }
3501da177e4SLinus Torvalds 
3512638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3522638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
35315e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3541da177e4SLinus Torvalds {
3558f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3562638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
3572638eb8bSFlorian Westphal 	struct in_ifaddr *last_prim;
3580ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3590ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	ASSERT_RTNL();
3621da177e4SLinus Torvalds 
3632638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
3642638eb8bSFlorian Westphal 	last_prim = rtnl_dereference(in_dev->ifa_list);
365fbd40ea0SDavid S. Miller 	if (in_dev->dead)
366fbd40ea0SDavid S. Miller 		goto no_promotions;
367fbd40ea0SDavid S. Miller 
3688f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3698f937c60SHarald Welte 	 * unless alias promotion is set
3708f937c60SHarald Welte 	 **/
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3732638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3741da177e4SLinus Torvalds 
3752638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3760ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3770ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3780ff60a45SJamal Hadi Salim 				last_prim = ifa;
3790ff60a45SJamal Hadi Salim 
3801da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3811da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3821da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3831da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3840ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3851da177e4SLinus Torvalds 				continue;
3861da177e4SLinus Torvalds 			}
3871da177e4SLinus Torvalds 
3880ff60a45SJamal Hadi Salim 			if (!do_promote) {
389fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3901da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3911da177e4SLinus Torvalds 
39215e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
393e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
394e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3951da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3968f937c60SHarald Welte 			} else {
3978f937c60SHarald Welte 				promote = ifa;
3988f937c60SHarald Welte 				break;
3998f937c60SHarald Welte 			}
4001da177e4SLinus Torvalds 		}
4011da177e4SLinus Torvalds 	}
4021da177e4SLinus Torvalds 
4032d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4042d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4052d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4062d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4072d230e2bSJulian Anastasov 	 */
4082638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4092d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4102d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4112d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4122d230e2bSJulian Anastasov 	}
4132d230e2bSJulian Anastasov 
414fbd40ea0SDavid S. Miller no_promotions:
4151da177e4SLinus Torvalds 	/* 2. Unlink it */
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
418fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4231da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4241da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4251da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4261da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4271da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4281da177e4SLinus Torvalds 	   So that, this order is correct.
4291da177e4SLinus Torvalds 	 */
43015e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
431e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4320ff60a45SJamal Hadi Salim 
4330ff60a45SJamal Hadi Salim 	if (promote) {
4342638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4350ff60a45SJamal Hadi Salim 
4362638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4370ff60a45SJamal Hadi Salim 		if (prev_prom) {
4382638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4392638eb8bSFlorian Westphal 
4402638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4416a9e9ceaSFlorian Westphal 
4426a9e9ceaSFlorian Westphal 			last_sec = rtnl_dereference(last_prim->ifa_next);
4432638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
4442638eb8bSFlorian Westphal 			rcu_assign_pointer(last_prim->ifa_next, promote);
4450ff60a45SJamal Hadi Salim 		}
4460ff60a45SJamal Hadi Salim 
4470ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
44815e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
449e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
450e041c683SAlan Stern 				NETDEV_UP, promote);
4512638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4522638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4530ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4540ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4550ff60a45SJamal Hadi Salim 					continue;
4560ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4570ff60a45SJamal Hadi Salim 		}
4580ff60a45SJamal Hadi Salim 
4590ff60a45SJamal Hadi Salim 	}
4606363097cSHerbert Xu 	if (destroy)
4611da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds 
4642638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4652638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
466d6062cbbSThomas Graf 			 int destroy)
467d6062cbbSThomas Graf {
468d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
469d6062cbbSThomas Graf }
470d6062cbbSThomas Graf 
4715c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4725c766d64SJiri Pirko 
4735c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4745c766d64SJiri Pirko 
475d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
476de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4771da177e4SLinus Torvalds {
4782638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4791da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4803ad7d246SKrister Johansen 	struct in_validator_info ivi;
4812638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4823ad7d246SKrister Johansen 	int ret;
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds 	ASSERT_RTNL();
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4871da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4881da177e4SLinus Torvalds 		return 0;
4891da177e4SLinus Torvalds 	}
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4921da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4931da177e4SLinus Torvalds 
4942e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
4952e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
4962e605463SMatteo Croce 
4972638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
4982638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
4992638eb8bSFlorian Westphal 
5002638eb8bSFlorian Westphal 	while (ifa1) {
5011da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
5021da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5031da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5041da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5051da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5061da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5071da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5081da177e4SLinus Torvalds 				return -EEXIST;
5091da177e4SLinus Torvalds 			}
5101da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
5111da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5121da177e4SLinus Torvalds 				return -EINVAL;
5131da177e4SLinus Torvalds 			}
5141da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5151da177e4SLinus Torvalds 		}
5162638eb8bSFlorian Westphal 
5172638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5182638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5191da177e4SLinus Torvalds 	}
5201da177e4SLinus Torvalds 
5213ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5223ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5233ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5243ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5253ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5263ad7d246SKrister Johansen 	 * not the flags.
5273ad7d246SKrister Johansen 	 */
5283ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5293ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
530de95e047SDavid Ahern 	ivi.extack = extack;
5313ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5323ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5333ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5343ad7d246SKrister Johansen 	if (ret) {
5353ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5363ad7d246SKrister Johansen 		return ret;
5373ad7d246SKrister Johansen 	}
5383ad7d246SKrister Johansen 
539*d4150779SJason A. Donenfeld 	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
5401da177e4SLinus Torvalds 		ifap = last_primary;
5411da177e4SLinus Torvalds 
5422638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5432638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5441da177e4SLinus Torvalds 
545fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
546fd23c3b3SDavid S. Miller 
5475c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
548906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5495c766d64SJiri Pirko 
5501da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5511da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5521da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
55315e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
554e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds 	return 0;
5571da177e4SLinus Torvalds }
5581da177e4SLinus Torvalds 
559d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
560d6062cbbSThomas Graf {
561de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
562d6062cbbSThomas Graf }
563d6062cbbSThomas Graf 
5641da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5651da177e4SLinus Torvalds {
566e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	ASSERT_RTNL();
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 	if (!in_dev) {
5711da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5721da177e4SLinus Torvalds 		return -ENOBUFS;
5731da177e4SLinus Torvalds 	}
57471e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5751d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5761da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
577547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5781da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5791da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5801da177e4SLinus Torvalds 	}
581f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5821da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5831da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds 
5868723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5878723e1b4SEric Dumazet  * We dont take a reference on found in_device
5888723e1b4SEric Dumazet  */
5897fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5901da177e4SLinus Torvalds {
5911da177e4SLinus Torvalds 	struct net_device *dev;
5921da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
593c148fc2eSEric Dumazet 
594c148fc2eSEric Dumazet 	rcu_read_lock();
595c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5961da177e4SLinus Torvalds 	if (dev)
5978723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
598c148fc2eSEric Dumazet 	rcu_read_unlock();
5991da177e4SLinus Torvalds 	return in_dev;
6001da177e4SLinus Torvalds }
6019f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6041da177e4SLinus Torvalds 
60560cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
60660cad5daSAl Viro 				    __be32 mask)
6071da177e4SLinus Torvalds {
608d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
609d519e870SFlorian Westphal 
6101da177e4SLinus Torvalds 	ASSERT_RTNL();
6111da177e4SLinus Torvalds 
612d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6131da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6141da177e4SLinus Torvalds 			return ifa;
615d519e870SFlorian Westphal 	}
6161da177e4SLinus Torvalds 	return NULL;
6171da177e4SLinus Torvalds }
6181da177e4SLinus Torvalds 
619690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
620690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
62193a714d6SMadhu Challa {
622690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
62393a714d6SMadhu Challa 	struct ip_mreqn mreq = {
62493a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
62593a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
62693a714d6SMadhu Challa 	};
627690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
62893a714d6SMadhu Challa 	int ret;
62993a714d6SMadhu Challa 
63093a714d6SMadhu Challa 	ASSERT_RTNL();
63193a714d6SMadhu Challa 
63293a714d6SMadhu Challa 	lock_sock(sk);
63393a714d6SMadhu Challa 	if (join)
63454ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
63593a714d6SMadhu Challa 	else
63654ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
63793a714d6SMadhu Challa 	release_sock(sk);
63893a714d6SMadhu Challa 
63993a714d6SMadhu Challa 	return ret;
640690cc863STaras Chornyi #else
641690cc863STaras Chornyi 	return -EOPNOTSUPP;
642690cc863STaras Chornyi #endif
64393a714d6SMadhu Challa }
64493a714d6SMadhu Challa 
645c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
646c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6471da177e4SLinus Torvalds {
6483b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6492638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
650dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6511da177e4SLinus Torvalds 	struct in_device *in_dev;
652dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6532638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
65430e2379eSMenglong Dong 	int err;
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 	ASSERT_RTNL();
6571da177e4SLinus Torvalds 
6588cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6598cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
660dfdd5fd4SThomas Graf 	if (err < 0)
661dfdd5fd4SThomas Graf 		goto errout;
662dfdd5fd4SThomas Graf 
663dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6647fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
66551456b29SIan Morris 	if (!in_dev) {
666dfdd5fd4SThomas Graf 		err = -ENODEV;
667dfdd5fd4SThomas Graf 		goto errout;
668dfdd5fd4SThomas Graf 	}
669dfdd5fd4SThomas Graf 
6702638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6711da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
672dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
67367b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6741da177e4SLinus Torvalds 			continue;
675dfdd5fd4SThomas Graf 
676dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
677dfdd5fd4SThomas Graf 			continue;
678dfdd5fd4SThomas Graf 
679dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
680dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
68167b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
682dfdd5fd4SThomas Graf 			continue;
683dfdd5fd4SThomas Graf 
68493a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
685690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
68615e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6871da177e4SLinus Torvalds 		return 0;
6881da177e4SLinus Torvalds 	}
689dfdd5fd4SThomas Graf 
690dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
691dfdd5fd4SThomas Graf errout:
692dfdd5fd4SThomas Graf 	return err;
6931da177e4SLinus Torvalds }
6941da177e4SLinus Torvalds 
6955c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6965c766d64SJiri Pirko 
6975c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6985c766d64SJiri Pirko {
6995c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
7005c766d64SJiri Pirko 	struct in_ifaddr *ifa;
701c988d1e8SJiri Pirko 	struct hlist_node *n;
7025c766d64SJiri Pirko 	int i;
7035c766d64SJiri Pirko 
7045c766d64SJiri Pirko 	now = jiffies;
7055c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
7065c766d64SJiri Pirko 
7075c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
708c988d1e8SJiri Pirko 		bool change_needed = false;
709c988d1e8SJiri Pirko 
710c988d1e8SJiri Pirko 		rcu_read_lock();
711b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
7125c766d64SJiri Pirko 			unsigned long age;
7135c766d64SJiri Pirko 
7145c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
7155c766d64SJiri Pirko 				continue;
7165c766d64SJiri Pirko 
7175c766d64SJiri Pirko 			/* We try to batch several events at once. */
7185c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
7195c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7205c766d64SJiri Pirko 
7215c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
7225c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
723c988d1e8SJiri Pirko 				change_needed = true;
724c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
725c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
726c988d1e8SJiri Pirko 				continue;
727c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
728c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
729c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
730c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
731c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
732c988d1e8SJiri Pirko 
733c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
734c988d1e8SJiri Pirko 					change_needed = true;
735c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
736c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
737c988d1e8SJiri Pirko 					       next)) {
738c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
739c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
740c988d1e8SJiri Pirko 			}
741c988d1e8SJiri Pirko 		}
742c988d1e8SJiri Pirko 		rcu_read_unlock();
743c988d1e8SJiri Pirko 		if (!change_needed)
744c988d1e8SJiri Pirko 			continue;
745c988d1e8SJiri Pirko 		rtnl_lock();
746c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
747c988d1e8SJiri Pirko 			unsigned long age;
748c988d1e8SJiri Pirko 
749c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
750c988d1e8SJiri Pirko 				continue;
751c988d1e8SJiri Pirko 
752c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
753c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
754c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
755c988d1e8SJiri Pirko 
756c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
757c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7582638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7592638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7605c766d64SJiri Pirko 
7612638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7622638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7632638eb8bSFlorian Westphal 				while (tmp) {
76440008e92SFlorian Westphal 					if (tmp == ifa) {
7655c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7665c766d64SJiri Pirko 							     ifap, 1);
767c988d1e8SJiri Pirko 						break;
7685c766d64SJiri Pirko 					}
7692638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7702638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
771c988d1e8SJiri Pirko 				}
772c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
773c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
774c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
775c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7765c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7775c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7785c766d64SJiri Pirko 			}
7795c766d64SJiri Pirko 		}
780c988d1e8SJiri Pirko 		rtnl_unlock();
7815c766d64SJiri Pirko 	}
7825c766d64SJiri Pirko 
7835c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7845c766d64SJiri Pirko 	next_sched = next;
7855c766d64SJiri Pirko 
7865c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7875c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7885c766d64SJiri Pirko 		next_sched = next_sec;
7895c766d64SJiri Pirko 
7905c766d64SJiri Pirko 	now = jiffies;
7915c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7925c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7935c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7945c766d64SJiri Pirko 
795906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
796906e073fSviresh kumar 			next_sched - now);
7975c766d64SJiri Pirko }
7985c766d64SJiri Pirko 
7995c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
8005c766d64SJiri Pirko 			     __u32 prefered_lft)
8015c766d64SJiri Pirko {
8025c766d64SJiri Pirko 	unsigned long timeout;
8035c766d64SJiri Pirko 
8045c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
8055c766d64SJiri Pirko 
8065c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
8075c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
8085c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
8095c766d64SJiri Pirko 	else
8105c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
8115c766d64SJiri Pirko 
8125c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8135c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8145c766d64SJiri Pirko 		if (timeout == 0)
8155c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
8165c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
8175c766d64SJiri Pirko 	}
8185c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
8195c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
8205c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
8215c766d64SJiri Pirko }
8225c766d64SJiri Pirko 
8235c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
824dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
825dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8261da177e4SLinus Torvalds {
8275c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8285c753978SThomas Graf 	struct in_ifaddr *ifa;
8295c753978SThomas Graf 	struct ifaddrmsg *ifm;
8301da177e4SLinus Torvalds 	struct net_device *dev;
8311da177e4SLinus Torvalds 	struct in_device *in_dev;
8327b218574SDenis V. Lunev 	int err;
8331da177e4SLinus Torvalds 
8348cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8358cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8365c753978SThomas Graf 	if (err < 0)
8375c753978SThomas Graf 		goto errout;
8381da177e4SLinus Torvalds 
8395c753978SThomas Graf 	ifm = nlmsg_data(nlh);
840c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
84151456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8425c753978SThomas Graf 		goto errout;
8431da177e4SLinus Torvalds 
8444b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8455c753978SThomas Graf 	err = -ENODEV;
84651456b29SIan Morris 	if (!dev)
8475c753978SThomas Graf 		goto errout;
8481da177e4SLinus Torvalds 
8495c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8505c753978SThomas Graf 	err = -ENOBUFS;
85151456b29SIan Morris 	if (!in_dev)
8525c753978SThomas Graf 		goto errout;
85371e27da9SHerbert Xu 
8545c753978SThomas Graf 	ifa = inet_alloc_ifa();
85551456b29SIan Morris 	if (!ifa)
8565c753978SThomas Graf 		/*
8575c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8585c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8595c753978SThomas Graf 		 */
8605c753978SThomas Graf 		goto errout;
8615c753978SThomas Graf 
862a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8631d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8645c753978SThomas Graf 	in_dev_hold(in_dev);
8655c753978SThomas Graf 
86651456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8675c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8685c753978SThomas Graf 
869fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8701da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8711da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
872ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
873ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8741da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8751da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8765c753978SThomas Graf 
87767b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
87867b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8795c753978SThomas Graf 
8805c753978SThomas Graf 	if (tb[IFA_BROADCAST])
88167b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8825c753978SThomas Graf 
8835c753978SThomas Graf 	if (tb[IFA_LABEL])
884872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8851da177e4SLinus Torvalds 	else
8861da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8871da177e4SLinus Torvalds 
888af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
889af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
890af4d768aSDavid Ahern 
89147f0bd50SJacques de Laval 	if (tb[IFA_PROTO])
89247f0bd50SJacques de Laval 		ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
89347f0bd50SJacques de Laval 
8945c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8955c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8965c766d64SJiri Pirko 
8975c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8985c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8995c766d64SJiri Pirko 			err = -EINVAL;
900446266b0SDaniel Borkmann 			goto errout_free;
9015c766d64SJiri Pirko 		}
9025c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
9035c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9045c766d64SJiri Pirko 	}
9055c766d64SJiri Pirko 
9065c753978SThomas Graf 	return ifa;
9075c753978SThomas Graf 
908446266b0SDaniel Borkmann errout_free:
909446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9105c753978SThomas Graf errout:
9115c753978SThomas Graf 	return ERR_PTR(err);
9125c753978SThomas Graf }
9135c753978SThomas Graf 
9145c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9155c766d64SJiri Pirko {
9165c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
917ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9185c766d64SJiri Pirko 
9195c766d64SJiri Pirko 	if (!ifa->ifa_local)
9205c766d64SJiri Pirko 		return NULL;
9215c766d64SJiri Pirko 
922ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9235c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9245c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9255c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9265c766d64SJiri Pirko 			return ifa1;
9275c766d64SJiri Pirko 	}
9285c766d64SJiri Pirko 	return NULL;
9295c766d64SJiri Pirko }
9305c766d64SJiri Pirko 
931c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
932c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9335c753978SThomas Graf {
9343b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9355c753978SThomas Graf 	struct in_ifaddr *ifa;
9365c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9375c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9385c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9395c753978SThomas Graf 
9405c753978SThomas Graf 	ASSERT_RTNL();
9415c753978SThomas Graf 
942dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9435c753978SThomas Graf 	if (IS_ERR(ifa))
9445c753978SThomas Graf 		return PTR_ERR(ifa);
9455c753978SThomas Graf 
9465c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9475c766d64SJiri Pirko 	if (!ifa_existing) {
9485c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
949614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9505c766d64SJiri Pirko 		 */
9515c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
95293a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
953690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
95493a714d6SMadhu Challa 
95593a714d6SMadhu Challa 			if (ret < 0) {
95693a714d6SMadhu Challa 				inet_free_ifa(ifa);
95793a714d6SMadhu Challa 				return ret;
95893a714d6SMadhu Challa 			}
95993a714d6SMadhu Challa 		}
960de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
961de95e047SDavid Ahern 					 extack);
9625c766d64SJiri Pirko 	} else {
963af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
964af4d768aSDavid Ahern 
9655c766d64SJiri Pirko 		inet_free_ifa(ifa);
9665c766d64SJiri Pirko 
9675c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9685c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9695c766d64SJiri Pirko 			return -EEXIST;
97034e2ed34SJiri Pirko 		ifa = ifa_existing;
971af4d768aSDavid Ahern 
972af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
973af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
974af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
975af4d768aSDavid Ahern 		}
976af4d768aSDavid Ahern 
97734e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
97805a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
979906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
980906e073fSviresh kumar 				&check_lifetime_work, 0);
98134e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9825c766d64SJiri Pirko 	}
9835c766d64SJiri Pirko 	return 0;
9841da177e4SLinus Torvalds }
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds /*
9871da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9881da177e4SLinus Torvalds  */
9891da177e4SLinus Torvalds 
99040384999SEric Dumazet static int inet_abc_len(__be32 addr)
9911da177e4SLinus Torvalds {
9921da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9931da177e4SLinus Torvalds 
99465cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
9951da177e4SLinus Torvalds 		rc = 0;
9961da177e4SLinus Torvalds 	else {
997714e85beSAl Viro 		__u32 haddr = ntohl(addr);
998714e85beSAl Viro 		if (IN_CLASSA(haddr))
9991da177e4SLinus Torvalds 			rc = 8;
1000714e85beSAl Viro 		else if (IN_CLASSB(haddr))
10011da177e4SLinus Torvalds 			rc = 16;
1002714e85beSAl Viro 		else if (IN_CLASSC(haddr))
10031da177e4SLinus Torvalds 			rc = 24;
100465cab850SDave Taht 		else if (IN_CLASSE(haddr))
100565cab850SDave Taht 			rc = 32;
10061da177e4SLinus Torvalds 	}
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds 	return rc;
10091da177e4SLinus Torvalds }
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds 
101203aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10131da177e4SLinus Torvalds {
10141da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
101503aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10162638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10171da177e4SLinus Torvalds 	struct in_device *in_dev;
10181da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10191da177e4SLinus Torvalds 	struct net_device *dev;
10201da177e4SLinus Torvalds 	char *colon;
10211da177e4SLinus Torvalds 	int ret = -EFAULT;
10221da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10231da177e4SLinus Torvalds 
102403aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10251da177e4SLinus Torvalds 
10261da177e4SLinus Torvalds 	/* save original address for comparison */
10271da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10281da177e4SLinus Torvalds 
102903aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10301da177e4SLinus Torvalds 	if (colon)
10311da177e4SLinus Torvalds 		*colon = 0;
10321da177e4SLinus Torvalds 
103303aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	switch (cmd) {
10361da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10371da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10381da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10391da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10401da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10411da177e4SLinus Torvalds 		   so that we do not impose a lock.
10421da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10431da177e4SLinus Torvalds 		 */
10441da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10451da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10461da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10471da177e4SLinus Torvalds 		break;
10481da177e4SLinus Torvalds 
10491da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1050bf5b30b8SZhao Hongjiang 		ret = -EPERM;
105152e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10521da177e4SLinus Torvalds 			goto out;
10531da177e4SLinus Torvalds 		break;
10541da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10551da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10561da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10571da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1058bf5b30b8SZhao Hongjiang 		ret = -EPERM;
105952e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10601da177e4SLinus Torvalds 			goto out;
10611da177e4SLinus Torvalds 		ret = -EINVAL;
10621da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10631da177e4SLinus Torvalds 			goto out;
10641da177e4SLinus Torvalds 		break;
10651da177e4SLinus Torvalds 	default:
10661da177e4SLinus Torvalds 		ret = -EINVAL;
10671da177e4SLinus Torvalds 		goto out;
10681da177e4SLinus Torvalds 	}
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	rtnl_lock();
10711da177e4SLinus Torvalds 
10721da177e4SLinus Torvalds 	ret = -ENODEV;
107303aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10749f9354b9SEric Dumazet 	if (!dev)
10751da177e4SLinus Torvalds 		goto done;
10761da177e4SLinus Torvalds 
10771da177e4SLinus Torvalds 	if (colon)
10781da177e4SLinus Torvalds 		*colon = ':';
10791da177e4SLinus Torvalds 
10809f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10819f9354b9SEric Dumazet 	if (in_dev) {
10821da177e4SLinus Torvalds 		if (tryaddrmatch) {
10831da177e4SLinus Torvalds 			/* Matthias Andree */
10841da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10851da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10861da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10871da177e4SLinus Torvalds 			   This is checked above. */
10882638eb8bSFlorian Westphal 
10892638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
10902638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
10911da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
109203aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10931da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10946c91afe1SDavid S. Miller 							ifa->ifa_local) {
10951da177e4SLinus Torvalds 					break; /* found */
10961da177e4SLinus Torvalds 				}
10971da177e4SLinus Torvalds 			}
10981da177e4SLinus Torvalds 		}
10991da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
11001da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
11011da177e4SLinus Torvalds 		   comparing just the label */
11021da177e4SLinus Torvalds 		if (!ifa) {
11032638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11042638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11051da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
110603aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11071da177e4SLinus Torvalds 					break;
11081da177e4SLinus Torvalds 		}
11091da177e4SLinus Torvalds 	}
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11121da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11131da177e4SLinus Torvalds 		goto done;
11141da177e4SLinus Torvalds 
11151da177e4SLinus Torvalds 	switch (cmd) {
11161da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
111730e948a3STonghao Zhang 		ret = 0;
11181da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
111903aef17bSAl Viro 		break;
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
112230e948a3STonghao Zhang 		ret = 0;
11231da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
112403aef17bSAl Viro 		break;
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
112730e948a3STonghao Zhang 		ret = 0;
11281da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
112903aef17bSAl Viro 		break;
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
113230e948a3STonghao Zhang 		ret = 0;
11331da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
113403aef17bSAl Viro 		break;
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11371da177e4SLinus Torvalds 		if (colon) {
11381da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11391da177e4SLinus Torvalds 			if (!ifa)
11401da177e4SLinus Torvalds 				break;
11411da177e4SLinus Torvalds 			ret = 0;
114203aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11431da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11441da177e4SLinus Torvalds 			break;
11451da177e4SLinus Torvalds 		}
1146567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11471da177e4SLinus Torvalds 		break;
11481da177e4SLinus Torvalds 
11491da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11501da177e4SLinus Torvalds 		ret = -EINVAL;
11511da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11521da177e4SLinus Torvalds 			break;
11531da177e4SLinus Torvalds 
11541da177e4SLinus Torvalds 		if (!ifa) {
11551da177e4SLinus Torvalds 			ret = -ENOBUFS;
11569f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11579f9354b9SEric Dumazet 			if (!ifa)
11581da177e4SLinus Torvalds 				break;
1159c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11601da177e4SLinus Torvalds 			if (colon)
116103aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11621da177e4SLinus Torvalds 			else
11631da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11641da177e4SLinus Torvalds 		} else {
11651da177e4SLinus Torvalds 			ret = 0;
11661da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11671da177e4SLinus Torvalds 				break;
11681da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11691da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1170148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11711da177e4SLinus Torvalds 		}
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11741da177e4SLinus Torvalds 
11751da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11761da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11771da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11781da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11791da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11801da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11811da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11821da177e4SLinus Torvalds 		} else {
11831da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11841da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11851da177e4SLinus Torvalds 		}
11865c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11871da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11881da177e4SLinus Torvalds 		break;
11891da177e4SLinus Torvalds 
11901da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11911da177e4SLinus Torvalds 		ret = 0;
11921da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11931da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11941da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11951da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11961da177e4SLinus Torvalds 		}
11971da177e4SLinus Torvalds 		break;
11981da177e4SLinus Torvalds 
11991da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
12001da177e4SLinus Torvalds 		ret = 0;
12011da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
12021da177e4SLinus Torvalds 			break;
12031da177e4SLinus Torvalds 		ret = -EINVAL;
12041da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12051da177e4SLinus Torvalds 			break;
12061da177e4SLinus Torvalds 		ret = 0;
12071da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12081da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12091da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12101da177e4SLinus Torvalds 		break;
12111da177e4SLinus Torvalds 
12121da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 		/*
12151da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12161da177e4SLinus Torvalds 		 */
12171da177e4SLinus Torvalds 		ret = -EINVAL;
12181da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12191da177e4SLinus Torvalds 			break;
12201da177e4SLinus Torvalds 		ret = 0;
12211da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1222a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12231da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12241da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12251da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds 			/* See if current broadcast address matches
12281da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12291da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12301da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12311da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12321da177e4SLinus Torvalds 			 */
12331da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12341da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12351da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1236dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12371da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12381da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12391da177e4SLinus Torvalds 			}
12401da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12411da177e4SLinus Torvalds 		}
12421da177e4SLinus Torvalds 		break;
12431da177e4SLinus Torvalds 	}
12441da177e4SLinus Torvalds done:
12451da177e4SLinus Torvalds 	rtnl_unlock();
12461da177e4SLinus Torvalds out:
12471da177e4SLinus Torvalds 	return ret;
12481da177e4SLinus Torvalds }
12491da177e4SLinus Torvalds 
1250b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12511da177e4SLinus Torvalds {
1252e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1253ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12541da177e4SLinus Torvalds 	struct ifreq ifr;
12551da177e4SLinus Torvalds 	int done = 0;
12561da177e4SLinus Torvalds 
125736fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
125836fd633eSAl Viro 		goto out;
125936fd633eSAl Viro 
12609f9354b9SEric Dumazet 	if (!in_dev)
12611da177e4SLinus Torvalds 		goto out;
12621da177e4SLinus Torvalds 
1263ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12641da177e4SLinus Torvalds 		if (!buf) {
126536fd633eSAl Viro 			done += size;
12661da177e4SLinus Torvalds 			continue;
12671da177e4SLinus Torvalds 		}
126836fd633eSAl Viro 		if (len < size)
12691da177e4SLinus Torvalds 			break;
12701da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12711da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12721da177e4SLinus Torvalds 
12731da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12741da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12751da177e4SLinus Torvalds 								ifa->ifa_local;
12761da177e4SLinus Torvalds 
127736fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12781da177e4SLinus Torvalds 			done = -EFAULT;
12791da177e4SLinus Torvalds 			break;
12801da177e4SLinus Torvalds 		}
128136fd633eSAl Viro 		len  -= size;
128236fd633eSAl Viro 		done += size;
12831da177e4SLinus Torvalds 	}
12841da177e4SLinus Torvalds out:
12851da177e4SLinus Torvalds 	return done;
12861da177e4SLinus Torvalds }
12871da177e4SLinus Torvalds 
12888b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12898b57fd1eSGao Feng 				 int scope)
12908b57fd1eSGao Feng {
1291d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1292d519e870SFlorian Westphal 
1293d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1294d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1295d519e870SFlorian Westphal 			continue;
12968b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12978b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12988b57fd1eSGao Feng 			return ifa->ifa_local;
1299d519e870SFlorian Westphal 	}
13008b57fd1eSGao Feng 
13018b57fd1eSGao Feng 	return 0;
13028b57fd1eSGao Feng }
13038b57fd1eSGao Feng 
1304a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13051da177e4SLinus Torvalds {
1306d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1307a61ced5dSAl Viro 	__be32 addr = 0;
1308d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13091da177e4SLinus Torvalds 	struct in_device *in_dev;
1310c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13113f2fb9a8SDavid Ahern 	int master_idx;
13121da177e4SLinus Torvalds 
13131da177e4SLinus Torvalds 	rcu_read_lock();
1314e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13151da177e4SLinus Torvalds 	if (!in_dev)
13161da177e4SLinus Torvalds 		goto no_in_dev;
13171da177e4SLinus Torvalds 
1318d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1319d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1320d8c444d5SShijie Luo 
1321d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1322d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1323d519e870SFlorian Westphal 			continue;
1324d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13251da177e4SLinus Torvalds 			continue;
13261da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13271da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13281da177e4SLinus Torvalds 			break;
13291da177e4SLinus Torvalds 		}
13301da177e4SLinus Torvalds 		if (!addr)
13311da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1332d519e870SFlorian Westphal 	}
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds 	if (addr)
1335c6d14c84SEric Dumazet 		goto out_unlock;
13369f9354b9SEric Dumazet no_in_dev:
13373f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13381da177e4SLinus Torvalds 
133917b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
134017b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
134117b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
134217b693cdSDavid Lamparter 	 * equality check in the loop below.
134317b693cdSDavid Lamparter 	 */
134417b693cdSDavid Lamparter 	if (master_idx &&
134517b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
134617b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13478b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13488b57fd1eSGao Feng 		if (addr)
134917b693cdSDavid Lamparter 			goto out_unlock;
135017b693cdSDavid Lamparter 	}
135117b693cdSDavid Lamparter 
13521da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1353ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13541da177e4SLinus Torvalds 	   in dev_base list.
13551da177e4SLinus Torvalds 	 */
1356c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13573f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13583f2fb9a8SDavid Ahern 			continue;
13593f2fb9a8SDavid Ahern 
13609f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13619f9354b9SEric Dumazet 		if (!in_dev)
13621da177e4SLinus Torvalds 			continue;
13631da177e4SLinus Torvalds 
13648b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13658b57fd1eSGao Feng 		if (addr)
1366c6d14c84SEric Dumazet 			goto out_unlock;
13671da177e4SLinus Torvalds 	}
1368c6d14c84SEric Dumazet out_unlock:
13691da177e4SLinus Torvalds 	rcu_read_unlock();
13701da177e4SLinus Torvalds 	return addr;
13711da177e4SLinus Torvalds }
13729f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13731da177e4SLinus Torvalds 
137460cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
137560cad5daSAl Viro 			      __be32 local, int scope)
13761da177e4SLinus Torvalds {
1377650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1378ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1379a144ea4bSAl Viro 	__be32 addr = 0;
1380ef11db33SFlorian Westphal 	int same = 0;
13811da177e4SLinus Torvalds 
1382650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1383650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1384650638a7SShijie Luo 
1385ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1386650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1387650638a7SShijie Luo 
13881da177e4SLinus Torvalds 		if (!addr &&
13891da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1390650638a7SShijie Luo 		    min_scope <= scope) {
13911da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13921da177e4SLinus Torvalds 			if (same)
13931da177e4SLinus Torvalds 				break;
13941da177e4SLinus Torvalds 		}
13951da177e4SLinus Torvalds 		if (!same) {
13961da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13971da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13981da177e4SLinus Torvalds 			if (same && addr) {
13991da177e4SLinus Torvalds 				if (local || !dst)
14001da177e4SLinus Torvalds 					break;
14011da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
14021da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
14031da177e4SLinus Torvalds 					break;
14041da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1405650638a7SShijie Luo 				if (min_scope <= scope) {
14061da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14071da177e4SLinus Torvalds 					break;
14081da177e4SLinus Torvalds 				}
14091da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14101da177e4SLinus Torvalds 				same = 0;
14111da177e4SLinus Torvalds 			}
14121da177e4SLinus Torvalds 		}
1413ef11db33SFlorian Westphal 	}
14141da177e4SLinus Torvalds 
14151da177e4SLinus Torvalds 	return same ? addr : 0;
14161da177e4SLinus Torvalds }
14171da177e4SLinus Torvalds 
14181da177e4SLinus Torvalds /*
14191da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1420b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1421b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14221da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14231da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14241da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14251da177e4SLinus Torvalds  */
1426b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14279bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14281da177e4SLinus Torvalds {
142960cad5daSAl Viro 	__be32 addr = 0;
14309bd85e32SDenis V. Lunev 	struct net_device *dev;
14311da177e4SLinus Torvalds 
143200db4124SIan Morris 	if (in_dev)
14339bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds 	rcu_read_lock();
1436c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14379f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14389f9354b9SEric Dumazet 		if (in_dev) {
14391da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14401da177e4SLinus Torvalds 			if (addr)
14411da177e4SLinus Torvalds 				break;
14421da177e4SLinus Torvalds 		}
14431da177e4SLinus Torvalds 	}
14441da177e4SLinus Torvalds 	rcu_read_unlock();
14451da177e4SLinus Torvalds 
14461da177e4SLinus Torvalds 	return addr;
14471da177e4SLinus Torvalds }
1448eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14491da177e4SLinus Torvalds 
14501da177e4SLinus Torvalds /*
14511da177e4SLinus Torvalds  *	Device notifier
14521da177e4SLinus Torvalds  */
14531da177e4SLinus Torvalds 
14541da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14551da177e4SLinus Torvalds {
1456e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14571da177e4SLinus Torvalds }
14589f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14591da177e4SLinus Torvalds 
14601da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14611da177e4SLinus Torvalds {
1462e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14631da177e4SLinus Torvalds }
14649f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14651da177e4SLinus Torvalds 
14663ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14673ad7d246SKrister Johansen {
14683ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14693ad7d246SKrister Johansen }
14703ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14713ad7d246SKrister Johansen 
14723ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14733ad7d246SKrister Johansen {
14743ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14753ad7d246SKrister Johansen 	    nb);
14763ad7d246SKrister Johansen }
14773ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14783ad7d246SKrister Johansen 
14799f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14809f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14811da177e4SLinus Torvalds */
14821da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14831da177e4SLinus Torvalds {
14841da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14851da177e4SLinus Torvalds 	int named = 0;
14861da177e4SLinus Torvalds 
1487ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
14881da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14891da177e4SLinus Torvalds 
14901da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14911da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14921da177e4SLinus Torvalds 		if (named++ == 0)
1493573bf470SThomas Graf 			goto skip;
149444344b2aSMark McLoughlin 		dot = strchr(old, ':');
149551456b29SIan Morris 		if (!dot) {
14961da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14971da177e4SLinus Torvalds 			dot = old;
14981da177e4SLinus Torvalds 		}
14999f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
15001da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
15019f9354b9SEric Dumazet 		else
15021da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1503573bf470SThomas Graf skip:
1504573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15051da177e4SLinus Torvalds 	}
15061da177e4SLinus Torvalds }
15071da177e4SLinus Torvalds 
1508d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1509d11327adSIan Campbell 					struct in_device *in_dev)
1510d11327adSIan Campbell 
1511d11327adSIan Campbell {
1512ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1513d11327adSIan Campbell 
1514ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1515d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15166c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15176c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1518d11327adSIan Campbell 			 dev->dev_addr, NULL);
1519d11327adSIan Campbell 	}
1520b76d0789SZoltan Kiss }
1521d11327adSIan Campbell 
15221da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15231da177e4SLinus Torvalds 
15241da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15251da177e4SLinus Torvalds 			 void *ptr)
15261da177e4SLinus Torvalds {
1527351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1528748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15291da177e4SLinus Torvalds 
15301da177e4SLinus Torvalds 	ASSERT_RTNL();
15311da177e4SLinus Torvalds 
15321da177e4SLinus Torvalds 	if (!in_dev) {
15338030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15341da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
153520e61da7SWANG Cong 			if (IS_ERR(in_dev))
153620e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15370cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
153842f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
153942f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15401da177e4SLinus Torvalds 			}
154106770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
154206770843SBreno Leitao 			/* Re-enabling IP */
154306770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
154406770843SBreno Leitao 				in_dev = inetdev_init(dev);
15458030f544SHerbert Xu 		}
15461da177e4SLinus Torvalds 		goto out;
15471da177e4SLinus Torvalds 	}
15481da177e4SLinus Torvalds 
15491da177e4SLinus Torvalds 	switch (event) {
15501da177e4SLinus Torvalds 	case NETDEV_REGISTER:
155191df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1552a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15531da177e4SLinus Torvalds 		break;
15541da177e4SLinus Torvalds 	case NETDEV_UP:
155506770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15561da177e4SLinus Torvalds 			break;
15570cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15589f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15599f9354b9SEric Dumazet 
15609f9354b9SEric Dumazet 			if (ifa) {
1561fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15621da177e4SLinus Torvalds 				ifa->ifa_local =
15631da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15641da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15651da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15661da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15671da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15681da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15691da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15705c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15715c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1572dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1573dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15741da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15751da177e4SLinus Torvalds 			}
15761da177e4SLinus Torvalds 		}
15771da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1578a8eceea8SJoe Perches 		fallthrough;
1579eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1580d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1581d11327adSIan Campbell 			break;
1582a8eceea8SJoe Perches 		fallthrough;
1583d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1584a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1585d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15861da177e4SLinus Torvalds 		break;
15871da177e4SLinus Torvalds 	case NETDEV_DOWN:
15881da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15891da177e4SLinus Torvalds 		break;
159093d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
159175c78500SMoni Shoua 		ip_mc_unmap(in_dev);
159275c78500SMoni Shoua 		break;
159393d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
159475c78500SMoni Shoua 		ip_mc_remap(in_dev);
159575c78500SMoni Shoua 		break;
15961da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
159706770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15981da177e4SLinus Torvalds 			break;
159906770843SBreno Leitao 		/* disable IP when MTU is not enough */
1600a8eceea8SJoe Perches 		fallthrough;
16011da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
16021da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
16031da177e4SLinus Torvalds 		break;
16041da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16051da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16061da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16071da177e4SLinus Torvalds 		 */
16081da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16091da177e4SLinus Torvalds 
161051602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
161166f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16121da177e4SLinus Torvalds 		break;
16131da177e4SLinus Torvalds 	}
16141da177e4SLinus Torvalds out:
16151da177e4SLinus Torvalds 	return NOTIFY_DONE;
16161da177e4SLinus Torvalds }
16171da177e4SLinus Torvalds 
16181da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16191da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16201da177e4SLinus Torvalds };
16211da177e4SLinus Torvalds 
162240384999SEric Dumazet static size_t inet_nlmsg_size(void)
1623339bf98fSThomas Graf {
1624339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1625339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1626339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1627339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1628ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
162963b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
163047f0bd50SJacques de Laval 	       + nla_total_size(1)  /* IFA_PROTO */
1631af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
163263b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1633339bf98fSThomas Graf }
1634339bf98fSThomas Graf 
16355c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16365c766d64SJiri Pirko {
16375c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16385c766d64SJiri Pirko }
16395c766d64SJiri Pirko 
16405c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16415c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16425c766d64SJiri Pirko {
16435c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16445c766d64SJiri Pirko 
16455c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16465c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16475c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16485c766d64SJiri Pirko 	ci.ifa_valid = valid;
16495c766d64SJiri Pirko 
16505c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16515c766d64SJiri Pirko }
16525c766d64SJiri Pirko 
16531da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1654978a46faSChristian Brauner 			    struct inet_fill_args *args)
16551da177e4SLinus Torvalds {
16561da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16571da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16585c766d64SJiri Pirko 	u32 preferred, valid;
16591da177e4SLinus Torvalds 
1660978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1661978a46faSChristian Brauner 			args->flags);
166251456b29SIan Morris 	if (!nlh)
166326932566SPatrick McHardy 		return -EMSGSIZE;
166447f68512SThomas Graf 
166547f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16661da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16671da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16685c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16691da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16701da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16711da177e4SLinus Torvalds 
1672978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1673978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1674d3807145SChristian Brauner 		goto nla_put_failure;
1675d3807145SChristian Brauner 
16765c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16775c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16785c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16795c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16805c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16815c766d64SJiri Pirko 
16825c766d64SJiri Pirko 			if (preferred > tval)
16835c766d64SJiri Pirko 				preferred -= tval;
16845c766d64SJiri Pirko 			else
16855c766d64SJiri Pirko 				preferred = 0;
16865c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16875c766d64SJiri Pirko 				if (valid > tval)
16885c766d64SJiri Pirko 					valid -= tval;
16895c766d64SJiri Pirko 				else
16905c766d64SJiri Pirko 					valid = 0;
16915c766d64SJiri Pirko 			}
16925c766d64SJiri Pirko 		}
16935c766d64SJiri Pirko 	} else {
16945c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16955c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16965c766d64SJiri Pirko 	}
1697f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1698930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1699f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1700930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1701f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1702930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1703f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
17045c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
170547f0bd50SJacques de Laval 	    (ifa->ifa_proto &&
170647f0bd50SJacques de Laval 	     nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
1707ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1708af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1709af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
17105c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
17115c766d64SJiri Pirko 			  preferred, valid))
1712f3756b79SDavid S. Miller 		goto nla_put_failure;
171347f68512SThomas Graf 
1714053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1715053c095aSJohannes Berg 	return 0;
171647f68512SThomas Graf 
171747f68512SThomas Graf nla_put_failure:
171826932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
171926932566SPatrick McHardy 	return -EMSGSIZE;
17201da177e4SLinus Torvalds }
17211da177e4SLinus Torvalds 
1722c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1723c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1724c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17255fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1726c33078e3SDavid Ahern {
17275fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1728c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1729c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1730c33078e3SDavid Ahern 	int err, i;
1731c33078e3SDavid Ahern 
1732c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1733c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1734c33078e3SDavid Ahern 		return -EINVAL;
1735c33078e3SDavid Ahern 	}
1736c33078e3SDavid Ahern 
1737c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1738c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1739c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1740c33078e3SDavid Ahern 		return -EINVAL;
1741c33078e3SDavid Ahern 	}
17425fcd266aSDavid Ahern 
17435fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17445fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17455fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17465fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1747c33078e3SDavid Ahern 	}
1748c33078e3SDavid Ahern 
17498cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1750c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1751c33078e3SDavid Ahern 	if (err < 0)
1752c33078e3SDavid Ahern 		return err;
1753c33078e3SDavid Ahern 
1754c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1755c33078e3SDavid Ahern 		if (!tb[i])
1756c33078e3SDavid Ahern 			continue;
1757c33078e3SDavid Ahern 
1758c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1759c33078e3SDavid Ahern 			struct net *net;
1760c33078e3SDavid Ahern 
1761c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1762c33078e3SDavid Ahern 
1763c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1764c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1765bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1766c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1767c33078e3SDavid Ahern 				return PTR_ERR(net);
1768c33078e3SDavid Ahern 			}
1769c33078e3SDavid Ahern 			*tgt_net = net;
1770c33078e3SDavid Ahern 		} else {
1771c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1772c33078e3SDavid Ahern 			return -EINVAL;
1773c33078e3SDavid Ahern 		}
1774c33078e3SDavid Ahern 	}
1775c33078e3SDavid Ahern 
1776c33078e3SDavid Ahern 	return 0;
1777c33078e3SDavid Ahern }
1778c33078e3SDavid Ahern 
17791c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
17801c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
17811c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
17821c98eca4SDavid Ahern {
17831c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
17841c98eca4SDavid Ahern 	int ip_idx = 0;
17851c98eca4SDavid Ahern 	int err;
17861c98eca4SDavid Ahern 
1787d3e6e285SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1788ef11db33SFlorian Westphal 		if (ip_idx < s_ip_idx) {
1789ef11db33SFlorian Westphal 			ip_idx++;
17901c98eca4SDavid Ahern 			continue;
1791ef11db33SFlorian Westphal 		}
17921c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
17931c98eca4SDavid Ahern 		if (err < 0)
17941c98eca4SDavid Ahern 			goto done;
17951c98eca4SDavid Ahern 
17961c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1797ef11db33SFlorian Westphal 		ip_idx++;
17981c98eca4SDavid Ahern 	}
17991c98eca4SDavid Ahern 	err = 0;
18001c98eca4SDavid Ahern 
18011c98eca4SDavid Ahern done:
18021c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
18031c98eca4SDavid Ahern 
18041c98eca4SDavid Ahern 	return err;
18051c98eca4SDavid Ahern }
18061c98eca4SDavid Ahern 
18071da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18081da177e4SLinus Torvalds {
1809c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1810978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1811978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1812c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1813978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1814978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1815978a46faSChristian Brauner 		.netnsid = -1,
1816978a46faSChristian Brauner 	};
18173b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1818d3807145SChristian Brauner 	struct net *tgt_net = net;
1819eec4df98SEric Dumazet 	int h, s_h;
1820eec4df98SEric Dumazet 	int idx, s_idx;
18211c98eca4SDavid Ahern 	int s_ip_idx;
18221da177e4SLinus Torvalds 	struct net_device *dev;
18231da177e4SLinus Torvalds 	struct in_device *in_dev;
1824eec4df98SEric Dumazet 	struct hlist_head *head;
1825d7e38611SDavid Ahern 	int err = 0;
18261da177e4SLinus Torvalds 
1827eec4df98SEric Dumazet 	s_h = cb->args[0];
1828eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
18291c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1830eec4df98SEric Dumazet 
1831c33078e3SDavid Ahern 	if (cb->strict_check) {
1832c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18335fcd266aSDavid Ahern 						 skb->sk, cb);
1834c33078e3SDavid Ahern 		if (err < 0)
1835d7e38611SDavid Ahern 			goto put_tgt_net;
18365fcd266aSDavid Ahern 
1837d7e38611SDavid Ahern 		err = 0;
18385fcd266aSDavid Ahern 		if (fillargs.ifindex) {
18395fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1840d7e38611SDavid Ahern 			if (!dev) {
1841d7e38611SDavid Ahern 				err = -ENODEV;
1842d7e38611SDavid Ahern 				goto put_tgt_net;
1843d7e38611SDavid Ahern 			}
18445fcd266aSDavid Ahern 
18455fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
18465fcd266aSDavid Ahern 			if (in_dev) {
18475fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18485fcd266aSDavid Ahern 						       &fillargs);
18495fcd266aSDavid Ahern 			}
18505fcd266aSDavid Ahern 			goto put_tgt_net;
18515fcd266aSDavid Ahern 		}
1852d3807145SChristian Brauner 	}
1853d3807145SChristian Brauner 
1854eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18557562f876SPavel Emelianov 		idx = 0;
1856d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1857eec4df98SEric Dumazet 		rcu_read_lock();
1858d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1859d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1860b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18611da177e4SLinus Torvalds 			if (idx < s_idx)
18627562f876SPavel Emelianov 				goto cont;
18634b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
18641da177e4SLinus Torvalds 				s_ip_idx = 0;
1865eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18669f9354b9SEric Dumazet 			if (!in_dev)
18677562f876SPavel Emelianov 				goto cont;
18681da177e4SLinus Torvalds 
18691c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18701c98eca4SDavid Ahern 					       &fillargs);
18711c98eca4SDavid Ahern 			if (err < 0) {
1872eec4df98SEric Dumazet 				rcu_read_unlock();
18731da177e4SLinus Torvalds 				goto done;
18741da177e4SLinus Torvalds 			}
18757562f876SPavel Emelianov cont:
18767562f876SPavel Emelianov 			idx++;
18771da177e4SLinus Torvalds 		}
1878eec4df98SEric Dumazet 		rcu_read_unlock();
1879eec4df98SEric Dumazet 	}
18801da177e4SLinus Torvalds 
18811da177e4SLinus Torvalds done:
1882eec4df98SEric Dumazet 	cb->args[0] = h;
1883eec4df98SEric Dumazet 	cb->args[1] = idx;
18845fcd266aSDavid Ahern put_tgt_net:
1885978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1886d3807145SChristian Brauner 		put_net(tgt_net);
18871da177e4SLinus Torvalds 
18887c1e8a38SArthur Gautier 	return skb->len ? : err;
18891da177e4SLinus Torvalds }
18901da177e4SLinus Torvalds 
1891d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
189215e47304SEric W. Biederman 		      u32 portid)
18931da177e4SLinus Torvalds {
1894978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1895978a46faSChristian Brauner 		.portid = portid,
1896978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1897978a46faSChristian Brauner 		.event = event,
1898978a46faSChristian Brauner 		.flags = 0,
1899978a46faSChristian Brauner 		.netnsid = -1,
1900978a46faSChristian Brauner 	};
190147f68512SThomas Graf 	struct sk_buff *skb;
1902d6062cbbSThomas Graf 	int err = -ENOBUFS;
19034b8aa9abSDenis V. Lunev 	struct net *net;
19041da177e4SLinus Torvalds 
1905c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1906339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
190751456b29SIan Morris 	if (!skb)
1908d6062cbbSThomas Graf 		goto errout;
1909d6062cbbSThomas Graf 
1910978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
191126932566SPatrick McHardy 	if (err < 0) {
191226932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
191326932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
191426932566SPatrick McHardy 		kfree_skb(skb);
191526932566SPatrick McHardy 		goto errout;
191626932566SPatrick McHardy 	}
191715e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19181ce85fe4SPablo Neira Ayuso 	return;
1919d6062cbbSThomas Graf errout:
1920d6062cbbSThomas Graf 	if (err < 0)
19214b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19221da177e4SLinus Torvalds }
19231da177e4SLinus Torvalds 
1924b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1925b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19269f0f7272SThomas Graf {
19271fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19289f0f7272SThomas Graf 
19299f0f7272SThomas Graf 	if (!in_dev)
19309f0f7272SThomas Graf 		return 0;
19319f0f7272SThomas Graf 
19329f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19339f0f7272SThomas Graf }
19349f0f7272SThomas Graf 
1935d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1936d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19379f0f7272SThomas Graf {
19381fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19399f0f7272SThomas Graf 	struct nlattr *nla;
19409f0f7272SThomas Graf 	int i;
19419f0f7272SThomas Graf 
19429f0f7272SThomas Graf 	if (!in_dev)
19439f0f7272SThomas Graf 		return -ENODATA;
19449f0f7272SThomas Graf 
19459f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
194651456b29SIan Morris 	if (!nla)
19479f0f7272SThomas Graf 		return -EMSGSIZE;
19489f0f7272SThomas Graf 
19499f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19509f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
19519f0f7272SThomas Graf 
19529f0f7272SThomas Graf 	return 0;
19539f0f7272SThomas Graf }
19549f0f7272SThomas Graf 
19559f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19569f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19579f0f7272SThomas Graf };
19589f0f7272SThomas Graf 
1959cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
19608679c31eSRocco Yue 				 const struct nlattr *nla,
19618679c31eSRocco Yue 				 struct netlink_ext_ack *extack)
19629f0f7272SThomas Graf {
19639f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19649f0f7272SThomas Graf 	int err, rem;
19659f0f7272SThomas Graf 
1966a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
1967cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19689f0f7272SThomas Graf 
19698cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
19708679c31eSRocco Yue 					  inet_af_policy, extack);
19719f0f7272SThomas Graf 	if (err < 0)
19729f0f7272SThomas Graf 		return err;
19739f0f7272SThomas Graf 
19749f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19759f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
19769f0f7272SThomas Graf 			int cfgid = nla_type(a);
19779f0f7272SThomas Graf 
19789f0f7272SThomas Graf 			if (nla_len(a) < 4)
19799f0f7272SThomas Graf 				return -EINVAL;
19809f0f7272SThomas Graf 
19819f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
19829f0f7272SThomas Graf 				return -EINVAL;
19839f0f7272SThomas Graf 		}
19849f0f7272SThomas Graf 	}
19859f0f7272SThomas Graf 
1986cf7afbfeSThomas Graf 	return 0;
1987cf7afbfeSThomas Graf }
1988cf7afbfeSThomas Graf 
19893583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
19903583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
1991cf7afbfeSThomas Graf {
1992a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1993cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1994cf7afbfeSThomas Graf 	int rem;
1995cf7afbfeSThomas Graf 
1996cf7afbfeSThomas Graf 	if (!in_dev)
1997cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1998cf7afbfeSThomas Graf 
19998cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
20005ac6b198SZheng Yongjun 		return -EINVAL;
2001cf7afbfeSThomas Graf 
20029f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20039f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
20049f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
20059f0f7272SThomas Graf 	}
20069f0f7272SThomas Graf 
20079f0f7272SThomas Graf 	return 0;
20089f0f7272SThomas Graf }
20099f0f7272SThomas Graf 
2010edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2011edc9e748SNicolas Dichtel {
2012edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2013edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2014136ba622SZhang Shengju 	bool all = false;
2015edc9e748SNicolas Dichtel 
2016136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2017136ba622SZhang Shengju 		all = true;
2018136ba622SZhang Shengju 
2019136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2020edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2021136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2022cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2023136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2024d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20255cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20265cbf777cSXin Long 		size += nla_total_size(4);
2027136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2028f085ff1cSstephen hemminger 		size += nla_total_size(4);
2029136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2030974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2031edc9e748SNicolas Dichtel 
2032edc9e748SNicolas Dichtel 	return size;
2033edc9e748SNicolas Dichtel }
2034edc9e748SNicolas Dichtel 
2035edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
2036edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
2037edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
2038edc9e748SNicolas Dichtel 				     int type)
2039edc9e748SNicolas Dichtel {
2040edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2041edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2042136ba622SZhang Shengju 	bool all = false;
2043edc9e748SNicolas Dichtel 
2044edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2045edc9e748SNicolas Dichtel 			flags);
204651456b29SIan Morris 	if (!nlh)
2047edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2048edc9e748SNicolas Dichtel 
2049136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2050136ba622SZhang Shengju 		all = true;
2051136ba622SZhang Shengju 
2052edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2053edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2054edc9e748SNicolas Dichtel 
2055edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2056edc9e748SNicolas Dichtel 		goto nla_put_failure;
2057edc9e748SNicolas Dichtel 
2058b5c9641dSDavid Ahern 	if (!devconf)
2059b5c9641dSDavid Ahern 		goto out;
2060b5c9641dSDavid Ahern 
2061136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2062edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2063edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2064edc9e748SNicolas Dichtel 		goto nla_put_failure;
2065136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2066cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2067cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2068cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2069136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2070d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2071d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2072d67b8c61SNicolas Dichtel 		goto nla_put_failure;
20735cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
20745cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
20755cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
20765cbf777cSXin Long 		goto nla_put_failure;
2077136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
207809aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2079f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2080f085ff1cSstephen hemminger 		goto nla_put_failure;
2081136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2082974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2083974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2084974d7af5SAndy Gospodarek 		goto nla_put_failure;
2085edc9e748SNicolas Dichtel 
2086b5c9641dSDavid Ahern out:
2087053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2088053c095aSJohannes Berg 	return 0;
2089edc9e748SNicolas Dichtel 
2090edc9e748SNicolas Dichtel nla_put_failure:
2091edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2092edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2093edc9e748SNicolas Dichtel }
2094edc9e748SNicolas Dichtel 
20953b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
20963b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2097edc9e748SNicolas Dichtel {
2098edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2099edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2100edc9e748SNicolas Dichtel 
2101fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
210251456b29SIan Morris 	if (!skb)
2103edc9e748SNicolas Dichtel 		goto errout;
2104edc9e748SNicolas Dichtel 
2105edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
21063b022865SDavid Ahern 					event, 0, type);
2107edc9e748SNicolas Dichtel 	if (err < 0) {
2108edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2109edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2110edc9e748SNicolas Dichtel 		kfree_skb(skb);
2111edc9e748SNicolas Dichtel 		goto errout;
2112edc9e748SNicolas Dichtel 	}
2113fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2114edc9e748SNicolas Dichtel 	return;
2115edc9e748SNicolas Dichtel errout:
2116edc9e748SNicolas Dichtel 	if (err < 0)
2117edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2118edc9e748SNicolas Dichtel }
2119edc9e748SNicolas Dichtel 
21209e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21219e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21229e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2123cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
212409aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2125974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21269e551110SNicolas Dichtel };
21279e551110SNicolas Dichtel 
2128eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2129eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2130eede370dSJakub Kicinski 				      struct nlattr **tb,
2131eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2132eede370dSJakub Kicinski {
2133eede370dSJakub Kicinski 	int i, err;
2134eede370dSJakub Kicinski 
2135eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2136eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2137eede370dSJakub Kicinski 		return -EINVAL;
2138eede370dSJakub Kicinski 	}
2139eede370dSJakub Kicinski 
2140eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21418cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21428cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21438cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2144eede370dSJakub Kicinski 
21458cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21468cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21478cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2148eede370dSJakub Kicinski 	if (err)
2149eede370dSJakub Kicinski 		return err;
2150eede370dSJakub Kicinski 
2151eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2152eede370dSJakub Kicinski 		if (!tb[i])
2153eede370dSJakub Kicinski 			continue;
2154eede370dSJakub Kicinski 
2155eede370dSJakub Kicinski 		switch (i) {
2156eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2157eede370dSJakub Kicinski 			break;
2158eede370dSJakub Kicinski 		default:
2159eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2160eede370dSJakub Kicinski 			return -EINVAL;
2161eede370dSJakub Kicinski 		}
2162eede370dSJakub Kicinski 	}
2163eede370dSJakub Kicinski 
2164eede370dSJakub Kicinski 	return 0;
2165eede370dSJakub Kicinski }
2166eede370dSJakub Kicinski 
21679e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2168c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2169c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
21709e551110SNicolas Dichtel {
21719e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
21729e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
21739e551110SNicolas Dichtel 	struct sk_buff *skb;
21749e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
21759e551110SNicolas Dichtel 	struct in_device *in_dev;
21769e551110SNicolas Dichtel 	struct net_device *dev;
21779e551110SNicolas Dichtel 	int ifindex;
21789e551110SNicolas Dichtel 	int err;
21799e551110SNicolas Dichtel 
2180eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2181eede370dSJakub Kicinski 	if (err)
21829e551110SNicolas Dichtel 		goto errout;
21839e551110SNicolas Dichtel 
2184a97eb33fSAnton Protopopov 	err = -EINVAL;
21859e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
21869e551110SNicolas Dichtel 		goto errout;
21879e551110SNicolas Dichtel 
21889e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
21899e551110SNicolas Dichtel 	switch (ifindex) {
21909e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
21919e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
21929e551110SNicolas Dichtel 		break;
21939e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
21949e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
21959e551110SNicolas Dichtel 		break;
21969e551110SNicolas Dichtel 	default:
21979e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
219851456b29SIan Morris 		if (!dev)
21999e551110SNicolas Dichtel 			goto errout;
22009e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
220151456b29SIan Morris 		if (!in_dev)
22029e551110SNicolas Dichtel 			goto errout;
22039e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
22049e551110SNicolas Dichtel 		break;
22059e551110SNicolas Dichtel 	}
22069e551110SNicolas Dichtel 
22079e551110SNicolas Dichtel 	err = -ENOBUFS;
2208fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
220951456b29SIan Morris 	if (!skb)
22109e551110SNicolas Dichtel 		goto errout;
22119e551110SNicolas Dichtel 
22129e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22139e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22149e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2215136ba622SZhang Shengju 					NETCONFA_ALL);
22169e551110SNicolas Dichtel 	if (err < 0) {
22179e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22189e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22199e551110SNicolas Dichtel 		kfree_skb(skb);
22209e551110SNicolas Dichtel 		goto errout;
22219e551110SNicolas Dichtel 	}
22229e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22239e551110SNicolas Dichtel errout:
22249e551110SNicolas Dichtel 	return err;
22259e551110SNicolas Dichtel }
22269e551110SNicolas Dichtel 
22277a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22287a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22297a674200SNicolas Dichtel {
2230addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22317a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
22327a674200SNicolas Dichtel 	int h, s_h;
22337a674200SNicolas Dichtel 	int idx, s_idx;
22347a674200SNicolas Dichtel 	struct net_device *dev;
22357a674200SNicolas Dichtel 	struct in_device *in_dev;
22367a674200SNicolas Dichtel 	struct hlist_head *head;
22377a674200SNicolas Dichtel 
2238addd383fSDavid Ahern 	if (cb->strict_check) {
2239addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2240addd383fSDavid Ahern 		struct netconfmsg *ncm;
2241addd383fSDavid Ahern 
2242addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2243addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2244addd383fSDavid Ahern 			return -EINVAL;
2245addd383fSDavid Ahern 		}
2246addd383fSDavid Ahern 
2247addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2248addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2249addd383fSDavid Ahern 			return -EINVAL;
2250addd383fSDavid Ahern 		}
2251addd383fSDavid Ahern 	}
2252addd383fSDavid Ahern 
22537a674200SNicolas Dichtel 	s_h = cb->args[0];
22547a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
22557a674200SNicolas Dichtel 
22567a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
22577a674200SNicolas Dichtel 		idx = 0;
22587a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
22597a674200SNicolas Dichtel 		rcu_read_lock();
22600465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
22610465277fSNicolas Dichtel 			  net->dev_base_seq;
22627a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
22637a674200SNicolas Dichtel 			if (idx < s_idx)
22647a674200SNicolas Dichtel 				goto cont;
22657a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
22667a674200SNicolas Dichtel 			if (!in_dev)
22677a674200SNicolas Dichtel 				goto cont;
22687a674200SNicolas Dichtel 
22697a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
22707a674200SNicolas Dichtel 						      &in_dev->cnf,
22717a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2272addd383fSDavid Ahern 						      nlh->nlmsg_seq,
22737a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
22747a674200SNicolas Dichtel 						      NLM_F_MULTI,
2275136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
22767a674200SNicolas Dichtel 				rcu_read_unlock();
22777a674200SNicolas Dichtel 				goto done;
22787a674200SNicolas Dichtel 			}
22790465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
22807a674200SNicolas Dichtel cont:
22817a674200SNicolas Dichtel 			idx++;
22827a674200SNicolas Dichtel 		}
22837a674200SNicolas Dichtel 		rcu_read_unlock();
22847a674200SNicolas Dichtel 	}
22857a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
22867a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
22877a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
22887a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2289addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22907a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2291136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22927a674200SNicolas Dichtel 			goto done;
22937a674200SNicolas Dichtel 		else
22947a674200SNicolas Dichtel 			h++;
22957a674200SNicolas Dichtel 	}
22967a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
22977a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
22987a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
22997a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2300addd383fSDavid Ahern 					      nlh->nlmsg_seq,
23017a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2302136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
23037a674200SNicolas Dichtel 			goto done;
23047a674200SNicolas Dichtel 		else
23057a674200SNicolas Dichtel 			h++;
23067a674200SNicolas Dichtel 	}
23077a674200SNicolas Dichtel done:
23087a674200SNicolas Dichtel 	cb->args[0] = h;
23097a674200SNicolas Dichtel 	cb->args[1] = idx;
23107a674200SNicolas Dichtel 
23117a674200SNicolas Dichtel 	return skb->len;
23127a674200SNicolas Dichtel }
23137a674200SNicolas Dichtel 
23141da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23151da177e4SLinus Torvalds 
2316c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
231731be3085SHerbert Xu {
231831be3085SHerbert Xu 	struct net_device *dev;
231931be3085SHerbert Xu 
232031be3085SHerbert Xu 	rcu_read_lock();
2321c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2322c6d14c84SEric Dumazet 		struct in_device *in_dev;
2323c6d14c84SEric Dumazet 
232431be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
232531be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23269355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2327c6d14c84SEric Dumazet 	}
232831be3085SHerbert Xu 	rcu_read_unlock();
232931be3085SHerbert Xu }
233031be3085SHerbert Xu 
2331c6d14c84SEric Dumazet /* called with RTNL locked */
2332c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
233368dd299bSPavel Emelyanov {
233468dd299bSPavel Emelyanov 	struct net_device *dev;
2335586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
233668dd299bSPavel Emelyanov 
2337586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23389355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23393b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23403b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2341edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2342edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23433b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23443b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2345edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2346edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
234768dd299bSPavel Emelyanov 
2348c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
234968dd299bSPavel Emelyanov 		struct in_device *in_dev;
2350fa17806cSEric Dumazet 
23510187bdfbSBen Hutchings 		if (on)
23520187bdfbSBen Hutchings 			dev_disable_lro(dev);
2353fa17806cSEric Dumazet 
2354fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2355edc9e748SNicolas Dichtel 		if (in_dev) {
235668dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23573b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23583b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2359edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2360edc9e748SNicolas Dichtel 		}
236168dd299bSPavel Emelyanov 	}
236268dd299bSPavel Emelyanov }
236368dd299bSPavel Emelyanov 
2364f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2365f085ff1cSstephen hemminger {
2366f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2367f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2368f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2369f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2370f085ff1cSstephen hemminger 	else {
2371f085ff1cSstephen hemminger 		struct in_device *idev
2372f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2373f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2374f085ff1cSstephen hemminger 	}
2375f085ff1cSstephen hemminger }
2376f085ff1cSstephen hemminger 
2377fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
237832927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
237931be3085SHerbert Xu {
2380d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23818d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2382d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
238331be3085SHerbert Xu 
238431be3085SHerbert Xu 	if (write) {
238531be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2386c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
238731be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2388f085ff1cSstephen hemminger 		int ifindex;
238931be3085SHerbert Xu 
239031be3085SHerbert Xu 		set_bit(i, cnf->state);
239131be3085SHerbert Xu 
23929355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2393c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2394d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2395d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2396d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
23974ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2398f085ff1cSstephen hemminger 
23995cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
24005cbf777cSXin Long 		    new_value != old_value)
24015cbf777cSXin Long 			rt_cache_flush(net);
24025cbf777cSXin Long 
2403cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2404cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2405f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24063b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24073b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2408cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2409cc535dfbSNicolas Dichtel 		}
2410f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2411f085ff1cSstephen hemminger 		    new_value != old_value) {
2412f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24133b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24143b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2415f085ff1cSstephen hemminger 						    ifindex, cnf);
2416f085ff1cSstephen hemminger 		}
2417974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2418974d7af5SAndy Gospodarek 		    new_value != old_value) {
2419974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24203b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24213b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2422974d7af5SAndy Gospodarek 						    ifindex, cnf);
2423974d7af5SAndy Gospodarek 		}
242431be3085SHerbert Xu 	}
242531be3085SHerbert Xu 
242631be3085SHerbert Xu 	return ret;
242731be3085SHerbert Xu }
242831be3085SHerbert Xu 
2429fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
243032927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24311da177e4SLinus Torvalds {
24321da177e4SLinus Torvalds 	int *valp = ctl->data;
24331da177e4SLinus Torvalds 	int val = *valp;
243488af182eSEric W. Biederman 	loff_t pos = *ppos;
24358292d7f6SYang Yang 	struct net *net = ctl->extra2;
24368292d7f6SYang Yang 	int ret;
24378292d7f6SYang Yang 
24388292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24398292d7f6SYang Yang 		return -EPERM;
24408292d7f6SYang Yang 
24418292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24421da177e4SLinus Torvalds 
24431da177e4SLinus Torvalds 	if (write && *valp != val) {
24440187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
244588af182eSEric W. Biederman 			if (!rtnl_trylock()) {
244688af182eSEric W. Biederman 				/* Restore the original values before restarting */
244788af182eSEric W. Biederman 				*valp = val;
244888af182eSEric W. Biederman 				*ppos = pos;
24499b8adb5eSEric W. Biederman 				return restart_syscall();
245088af182eSEric W. Biederman 			}
24510187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2452c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2453edc9e748SNicolas Dichtel 			} else {
24540187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24550187bdfbSBen Hutchings 				struct in_device *idev =
24560187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2457edc9e748SNicolas Dichtel 				if (*valp)
24580187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24593b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2460edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2461edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2462edc9e748SNicolas Dichtel 							    cnf);
24630187bdfbSBen Hutchings 			}
24640187bdfbSBen Hutchings 			rtnl_unlock();
24654ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2466edc9e748SNicolas Dichtel 		} else
24673b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24683b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2469edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2470edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24710187bdfbSBen Hutchings 	}
24721da177e4SLinus Torvalds 
24731da177e4SLinus Torvalds 	return ret;
24741da177e4SLinus Torvalds }
24751da177e4SLinus Torvalds 
2476fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
247732927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
24781da177e4SLinus Torvalds {
24791da177e4SLinus Torvalds 	int *valp = ctl->data;
24801da177e4SLinus Torvalds 	int val = *valp;
24818d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
248276e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24831da177e4SLinus Torvalds 
24841da177e4SLinus Torvalds 	if (write && *valp != val)
24854ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
24861da177e4SLinus Torvalds 
24871da177e4SLinus Torvalds 	return ret;
24881da177e4SLinus Torvalds }
24891da177e4SLinus Torvalds 
2490f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
249142f811b8SHerbert Xu 	{ \
249242f811b8SHerbert Xu 		.procname	= name, \
249342f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
249402291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
249542f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
249642f811b8SHerbert Xu 		.mode		= mval, \
249742f811b8SHerbert Xu 		.proc_handler	= proc, \
249831be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
249942f811b8SHerbert Xu 	}
250042f811b8SHerbert Xu 
250142f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2502f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
250342f811b8SHerbert Xu 
250442f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2505f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
250642f811b8SHerbert Xu 
2507f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2508f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
250942f811b8SHerbert Xu 
251042f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2511f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
251242f811b8SHerbert Xu 
25131da177e4SLinus Torvalds static struct devinet_sysctl_table {
25141da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
251502291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
25161da177e4SLinus Torvalds } devinet_sysctl = {
25171da177e4SLinus Torvalds 	.devinet_vars = {
251842f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2519f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
252042f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25215cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
252242f811b8SHerbert Xu 
252342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
252442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
252542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
252642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
252742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
252842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
252942f811b8SHerbert Xu 					"accept_source_route"),
25308153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
253128f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
253242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
253342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
253442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
253542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
253642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
253742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
253842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
253942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
254042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2541eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
2542fcdb44d0SJames Prestwood 		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
2543fcdb44d0SJames Prestwood 					"arp_evict_nocarrier"),
254465324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25455c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25465c6fe01cSWilliam Manley 					"force_igmp_version"),
25472690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25482690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25492690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25502690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25510eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25520eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
255397daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
255497daf331SJohannes Berg 					"drop_gratuitous_arp"),
255542f811b8SHerbert Xu 
255642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
255742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
255842f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
255942f811b8SHerbert Xu 					      "promote_secondaries"),
2560d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2561d0daebc3SThomas Graf 					      "route_localnet"),
256212b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
256312b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25641da177e4SLinus Torvalds 	},
25651da177e4SLinus Torvalds };
25661da177e4SLinus Torvalds 
2567ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
256829c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25691da177e4SLinus Torvalds {
25701da177e4SLinus Torvalds 	int i;
25719fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25728607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2573bfada697SPavel Emelyanov 
25749fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
25751da177e4SLinus Torvalds 	if (!t)
25769fa89642SPavel Emelyanov 		goto out;
25779fa89642SPavel Emelyanov 
25781da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
25791da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
258031be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2581c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25821da177e4SLinus Torvalds 	}
25831da177e4SLinus Torvalds 
25848607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
25851da177e4SLinus Torvalds 
25868607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
25871da177e4SLinus Torvalds 	if (!t->sysctl_header)
25888607ddb8SEric W. Biederman 		goto free;
25891da177e4SLinus Torvalds 
25901da177e4SLinus Torvalds 	p->sysctl = t;
259129c994e3SNicolas Dichtel 
25923b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
25933b022865SDavid Ahern 				    ifindex, p);
2594ea40b324SPavel Emelyanov 	return 0;
25951da177e4SLinus Torvalds 
25961da177e4SLinus Torvalds free:
25971da177e4SLinus Torvalds 	kfree(t);
25989fa89642SPavel Emelyanov out:
25996def4801Sliuguoqiang 	return -ENOMEM;
26001da177e4SLinus Torvalds }
26011da177e4SLinus Torvalds 
2602b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2603b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
260466f27a52SPavel Emelyanov {
260551602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
260666f27a52SPavel Emelyanov 
2607b5c9641dSDavid Ahern 	if (t) {
260851602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2609ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26101da177e4SLinus Torvalds 		kfree(t);
26111da177e4SLinus Torvalds 	}
261251602b2aSPavel Emelyanov 
2613b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2614b5c9641dSDavid Ahern }
2615b5c9641dSDavid Ahern 
261620e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
261751602b2aSPavel Emelyanov {
261820e61da7SWANG Cong 	int err;
261920e61da7SWANG Cong 
262020e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
262120e61da7SWANG Cong 		return -EINVAL;
262220e61da7SWANG Cong 
262320e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
262420e61da7SWANG Cong 	if (err)
262520e61da7SWANG Cong 		return err;
262620e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
262729c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
262820e61da7SWANG Cong 	if (err)
262920e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
263020e61da7SWANG Cong 	return err;
263151602b2aSPavel Emelyanov }
263251602b2aSPavel Emelyanov 
263351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
263451602b2aSPavel Emelyanov {
2635b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2636b5c9641dSDavid Ahern 
2637b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
263851602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26391da177e4SLinus Torvalds }
26401da177e4SLinus Torvalds 
264168dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
264268dd299bSPavel Emelyanov 	{
264368dd299bSPavel Emelyanov 		.procname	= "ip_forward",
264468dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
264502291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
264668dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
264768dd299bSPavel Emelyanov 		.mode		= 0644,
264868dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
264968dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2650c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
265168dd299bSPavel Emelyanov 	},
265268dd299bSPavel Emelyanov 	{ },
265368dd299bSPavel Emelyanov };
26542a75de0cSEric Dumazet #endif
265568dd299bSPavel Emelyanov 
2656752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2657752d14dcSPavel Emelyanov {
2658752d14dcSPavel Emelyanov 	int err;
2659752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26602a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2661856c395cSCong Wang 	struct ctl_table *tbl;
2662752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26632a75de0cSEric Dumazet #endif
2664752d14dcSPavel Emelyanov 
2665752d14dcSPavel Emelyanov 	err = -ENOMEM;
2666856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
266751456b29SIan Morris 	if (!all)
2668752d14dcSPavel Emelyanov 		goto err_alloc_all;
2669752d14dcSPavel Emelyanov 
2670856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
267151456b29SIan Morris 	if (!dflt)
2672752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2673752d14dcSPavel Emelyanov 
26742a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2675856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
267651456b29SIan Morris 	if (!tbl)
2677752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2678752d14dcSPavel Emelyanov 
267902291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2680752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2681752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26822a75de0cSEric Dumazet #endif
2683856c395cSCong Wang 
26849efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
26859efd6a3cSNicolas Dichtel 		if (IS_ENABLED(CONFIG_SYSCTL) &&
26869efd6a3cSNicolas Dichtel 		    sysctl_devconf_inherit_init_net == 3) {
26879efd6a3cSNicolas Dichtel 			/* copy from the current netns */
26889efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
26899efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
26909efd6a3cSNicolas Dichtel 			memcpy(dflt,
26919efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
26929efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
26939efd6a3cSNicolas Dichtel 		} else if (!IS_ENABLED(CONFIG_SYSCTL) ||
26949efd6a3cSNicolas Dichtel 			   sysctl_devconf_inherit_init_net != 2) {
26959efd6a3cSNicolas Dichtel 			/* inherit == 0 or 1: copy from init_net */
26969efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
26979efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
26989efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
26999efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
27009efd6a3cSNicolas Dichtel 		}
27019efd6a3cSNicolas Dichtel 		/* else inherit == 2: use compiled values */
2702752d14dcSPavel Emelyanov 	}
2703752d14dcSPavel Emelyanov 
2704752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
270529c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2706752d14dcSPavel Emelyanov 	if (err < 0)
2707752d14dcSPavel Emelyanov 		goto err_reg_all;
2708752d14dcSPavel Emelyanov 
270929c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
271029c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2711752d14dcSPavel Emelyanov 	if (err < 0)
2712752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2713752d14dcSPavel Emelyanov 
2714752d14dcSPavel Emelyanov 	err = -ENOMEM;
27158607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
271651456b29SIan Morris 	if (!forw_hdr)
2717752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27182a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2719752d14dcSPavel Emelyanov #endif
2720752d14dcSPavel Emelyanov 
2721752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2722752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2723752d14dcSPavel Emelyanov 	return 0;
2724752d14dcSPavel Emelyanov 
2725752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2726752d14dcSPavel Emelyanov err_reg_ctl:
2727b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2728752d14dcSPavel Emelyanov err_reg_dflt:
2729b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2730752d14dcSPavel Emelyanov err_reg_all:
2731752d14dcSPavel Emelyanov 	kfree(tbl);
2732752d14dcSPavel Emelyanov err_alloc_ctl:
27332a75de0cSEric Dumazet #endif
2734752d14dcSPavel Emelyanov 	kfree(dflt);
2735752d14dcSPavel Emelyanov err_alloc_dflt:
2736752d14dcSPavel Emelyanov 	kfree(all);
2737752d14dcSPavel Emelyanov err_alloc_all:
2738752d14dcSPavel Emelyanov 	return err;
2739752d14dcSPavel Emelyanov }
2740752d14dcSPavel Emelyanov 
2741752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2742752d14dcSPavel Emelyanov {
27432a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2744752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2745752d14dcSPavel Emelyanov 
2746752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2747752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2748b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2749b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2750b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2751b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2752752d14dcSPavel Emelyanov 	kfree(tbl);
27532a75de0cSEric Dumazet #endif
2754752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2755752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2756752d14dcSPavel Emelyanov }
2757752d14dcSPavel Emelyanov 
2758752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2759752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2760752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2761752d14dcSPavel Emelyanov };
2762752d14dcSPavel Emelyanov 
2763207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
27649f0f7272SThomas Graf 	.family		  = AF_INET,
27659f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
27669f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2767cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2768cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
27699f0f7272SThomas Graf };
27709f0f7272SThomas Graf 
27711da177e4SLinus Torvalds void __init devinet_init(void)
27721da177e4SLinus Torvalds {
2773fd23c3b3SDavid S. Miller 	int i;
2774fd23c3b3SDavid S. Miller 
2775fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2776fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2777fd23c3b3SDavid S. Miller 
2778752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
27791da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
278063f3444fSThomas Graf 
2781906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
27825c766d64SJiri Pirko 
27839f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
27849f0f7272SThomas Graf 
2785b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2786b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2787b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
27889e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2789b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
27901da177e4SLinus Torvalds }
2791