xref: /openbmc/linux/net/ipv4/devinet.c (revision d3e6e285)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	NET3	IP device support routines.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
1002c30a84SJesper Juhl  * 		Authors:	Ross Biro
111da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
121da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Additional Authors:
151da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
161da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *	Changes:
191da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
201da177e4SLinus Torvalds  *					lists.
211da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
221da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
231da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
241da177e4SLinus Torvalds  *					fall back to comparing just the label
251da177e4SLinus Torvalds  *					if no match found.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds 
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
301da177e4SLinus Torvalds #include <linux/bitops.h>
314fc268d2SRandy Dunlap #include <linux/capability.h>
321da177e4SLinus Torvalds #include <linux/module.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/kernel.h>
35174cd4b1SIngo Molnar #include <linux/sched/signal.h>
361da177e4SLinus Torvalds #include <linux/string.h>
371da177e4SLinus Torvalds #include <linux/mm.h>
381da177e4SLinus Torvalds #include <linux/socket.h>
391da177e4SLinus Torvalds #include <linux/sockios.h>
401da177e4SLinus Torvalds #include <linux/in.h>
411da177e4SLinus Torvalds #include <linux/errno.h>
421da177e4SLinus Torvalds #include <linux/interrupt.h>
431823730fSThomas Graf #include <linux/if_addr.h>
441da177e4SLinus Torvalds #include <linux/if_ether.h>
451da177e4SLinus Torvalds #include <linux/inet.h>
461da177e4SLinus Torvalds #include <linux/netdevice.h>
471da177e4SLinus Torvalds #include <linux/etherdevice.h>
481da177e4SLinus Torvalds #include <linux/skbuff.h>
491da177e4SLinus Torvalds #include <linux/init.h>
501da177e4SLinus Torvalds #include <linux/notifier.h>
511da177e4SLinus Torvalds #include <linux/inetdevice.h>
521da177e4SLinus Torvalds #include <linux/igmp.h>
535a0e3ad6STejun Heo #include <linux/slab.h>
54fd23c3b3SDavid S. Miller #include <linux/hash.h>
551da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
561da177e4SLinus Torvalds #include <linux/sysctl.h>
571da177e4SLinus Torvalds #endif
581da177e4SLinus Torvalds #include <linux/kmod.h>
59edc9e748SNicolas Dichtel #include <linux/netconf.h>
601da177e4SLinus Torvalds 
6114c85021SArnaldo Carvalho de Melo #include <net/arp.h>
621da177e4SLinus Torvalds #include <net/ip.h>
631da177e4SLinus Torvalds #include <net/route.h>
641da177e4SLinus Torvalds #include <net/ip_fib.h>
6563f3444fSThomas Graf #include <net/rtnetlink.h>
66752d14dcSPavel Emelyanov #include <net/net_namespace.h>
675c766d64SJiri Pirko #include <net/addrconf.h>
681da177e4SLinus Torvalds 
690027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7042f811b8SHerbert Xu 	.data = {
7102291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7202291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
752690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
7742f811b8SHerbert Xu 	},
781da177e4SLinus Torvalds };
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8142f811b8SHerbert Xu 	.data = {
8202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
872690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
882690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
8942f811b8SHerbert Xu 	},
901da177e4SLinus Torvalds };
911da177e4SLinus Torvalds 
929355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
939355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9442f811b8SHerbert Xu 
95ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
965c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
975c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
985c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
995176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1005c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
101ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
102af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
103d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
1045c753978SThomas Graf };
1055c753978SThomas Graf 
106978a46faSChristian Brauner struct inet_fill_args {
107978a46faSChristian Brauner 	u32 portid;
108978a46faSChristian Brauner 	u32 seq;
109978a46faSChristian Brauner 	int event;
110978a46faSChristian Brauner 	unsigned int flags;
111978a46faSChristian Brauner 	int netnsid;
1125fcd266aSDavid Ahern 	int ifindex;
113978a46faSChristian Brauner };
114978a46faSChristian Brauner 
11540384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
11640384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
11740384999SEric Dumazet 
118fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
119fd23c3b3SDavid S. Miller 
1206eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
121fd23c3b3SDavid S. Miller {
12240384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
123fd23c3b3SDavid S. Miller 
12440384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
125fd23c3b3SDavid S. Miller }
126fd23c3b3SDavid S. Miller 
127fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
128fd23c3b3SDavid S. Miller {
12940384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
130fd23c3b3SDavid S. Miller 
13132a4be48SWANG Cong 	ASSERT_RTNL();
132fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
133fd23c3b3SDavid S. Miller }
134fd23c3b3SDavid S. Miller 
135fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
136fd23c3b3SDavid S. Miller {
13732a4be48SWANG Cong 	ASSERT_RTNL();
138fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
139fd23c3b3SDavid S. Miller }
140fd23c3b3SDavid S. Miller 
1419435eb1cSDavid S. Miller /**
1429435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1439435eb1cSDavid S. Miller  * @net: the net namespace
1449435eb1cSDavid S. Miller  * @addr: the source address
1459435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1469435eb1cSDavid S. Miller  *
1479435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1489435eb1cSDavid S. Miller  */
1499435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1509435eb1cSDavid S. Miller {
1519435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1529435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1539435eb1cSDavid S. Miller 
1549435eb1cSDavid S. Miller 	rcu_read_lock();
1556e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1566e617de8SPaolo Abeni 	if (!ifa) {
157406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
158406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
159406b6f97SDavid S. Miller 		struct fib_table *local;
160406b6f97SDavid S. Miller 
161406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
162406b6f97SDavid S. Miller 		 * over loopback subnets work.
163406b6f97SDavid S. Miller 		 */
164406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
165406b6f97SDavid S. Miller 		if (local &&
166406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
167406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
168406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1696e617de8SPaolo Abeni 	} else {
1706e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
171406b6f97SDavid S. Miller 	}
1729435eb1cSDavid S. Miller 	if (result && devref)
1739435eb1cSDavid S. Miller 		dev_hold(result);
1749435eb1cSDavid S. Miller 	rcu_read_unlock();
1759435eb1cSDavid S. Miller 	return result;
1769435eb1cSDavid S. Miller }
1779435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1789435eb1cSDavid S. Miller 
1796e617de8SPaolo Abeni /* called under RCU lock */
1806e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1816e617de8SPaolo Abeni {
1826e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1836e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1846e617de8SPaolo Abeni 
1856e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1866e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1876e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1886e617de8SPaolo Abeni 			return ifa;
1896e617de8SPaolo Abeni 
1906e617de8SPaolo Abeni 	return NULL;
1916e617de8SPaolo Abeni }
1926e617de8SPaolo Abeni 
193d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1941da177e4SLinus Torvalds 
195e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1963ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
1972638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
1982638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
1991da177e4SLinus Torvalds 			 int destroy);
2001da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20120e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20351602b2aSPavel Emelyanov #else
20420e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20551602b2aSPavel Emelyanov {
20620e61da7SWANG Cong 	return 0;
20751602b2aSPavel Emelyanov }
20840384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
20951602b2aSPavel Emelyanov {
21051602b2aSPavel Emelyanov }
2111da177e4SLinus Torvalds #endif
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds /* Locks all the inet devices. */
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2161da177e4SLinus Torvalds {
21793adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2211da177e4SLinus Torvalds {
2221da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2231da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2241da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2251da177e4SLinus Torvalds 	kfree(ifa);
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
22840384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2341da177e4SLinus Torvalds {
2351da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2361da177e4SLinus Torvalds 
237547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
238547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
239e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2401da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
24191df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2421da177e4SLinus Torvalds #endif
2431da177e4SLinus Torvalds 	dev_put(dev);
2441da177e4SLinus Torvalds 	if (!idev->dead)
2459f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2469f9354b9SEric Dumazet 	else
2471da177e4SLinus Torvalds 		kfree(idev);
2481da177e4SLinus Torvalds }
2499f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2501da177e4SLinus Torvalds 
25171e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2521da177e4SLinus Torvalds {
2531da177e4SLinus Torvalds 	struct in_device *in_dev;
25420e61da7SWANG Cong 	int err = -ENOMEM;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	ASSERT_RTNL();
2571da177e4SLinus Torvalds 
2580da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2591da177e4SLinus Torvalds 	if (!in_dev)
2601da177e4SLinus Torvalds 		goto out;
261c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2629355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2631da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2641da177e4SLinus Torvalds 	in_dev->dev = dev;
2659f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2669f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2671da177e4SLinus Torvalds 		goto out_kfree;
2680187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2690187bdfbSBen Hutchings 		dev_disable_lro(dev);
2701da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2711da177e4SLinus Torvalds 	dev_hold(dev);
27230c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2737658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2741da177e4SLinus Torvalds 
27520e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
27620e61da7SWANG Cong 	if (err) {
27720e61da7SWANG Cong 		in_dev->dead = 1;
27820e61da7SWANG Cong 		in_dev_put(in_dev);
27920e61da7SWANG Cong 		in_dev = NULL;
28020e61da7SWANG Cong 		goto out;
28120e61da7SWANG Cong 	}
2821da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2831da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2841da177e4SLinus Torvalds 		ip_mc_up(in_dev);
285483479ecSJarek Poplawski 
28630c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
287cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
288483479ecSJarek Poplawski out:
28920e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2901da177e4SLinus Torvalds out_kfree:
2911da177e4SLinus Torvalds 	kfree(in_dev);
2921da177e4SLinus Torvalds 	in_dev = NULL;
2931da177e4SLinus Torvalds 	goto out;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2991da177e4SLinus Torvalds 	in_dev_put(idev);
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	struct net_device *dev;
3052638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	ASSERT_RTNL();
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	dev = in_dev->dev;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	in_dev->dead = 1;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3141da177e4SLinus Torvalds 
3152638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3161da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3171da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3181da177e4SLinus Torvalds 	}
3191da177e4SLinus Torvalds 
320a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3211da177e4SLinus Torvalds 
32251602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3231da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3241da177e4SLinus Torvalds 	arp_ifdown(dev);
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds 
329ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3301da177e4SLinus Torvalds {
331d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
332d519e870SFlorian Westphal 
3331da177e4SLinus Torvalds 	rcu_read_lock();
334d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3351da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3361da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3371da177e4SLinus Torvalds 				rcu_read_unlock();
3381da177e4SLinus Torvalds 				return 1;
3391da177e4SLinus Torvalds 			}
3401da177e4SLinus Torvalds 		}
341d519e870SFlorian Westphal 	}
3421da177e4SLinus Torvalds 	rcu_read_unlock();
3431da177e4SLinus Torvalds 	return 0;
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds 
3462638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3472638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
34815e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3491da177e4SLinus Torvalds {
3508f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3512638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
3522638eb8bSFlorian Westphal 	struct in_ifaddr *last_prim;
3530ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3540ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 	ASSERT_RTNL();
3571da177e4SLinus Torvalds 
3582638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
3592638eb8bSFlorian Westphal 	last_prim = rtnl_dereference(in_dev->ifa_list);
360fbd40ea0SDavid S. Miller 	if (in_dev->dead)
361fbd40ea0SDavid S. Miller 		goto no_promotions;
362fbd40ea0SDavid S. Miller 
3638f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3648f937c60SHarald Welte 	 * unless alias promotion is set
3658f937c60SHarald Welte 	 **/
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3682638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3691da177e4SLinus Torvalds 
3702638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3710ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3720ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3730ff60a45SJamal Hadi Salim 				last_prim = ifa;
3740ff60a45SJamal Hadi Salim 
3751da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3761da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3771da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3781da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3790ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3801da177e4SLinus Torvalds 				continue;
3811da177e4SLinus Torvalds 			}
3821da177e4SLinus Torvalds 
3830ff60a45SJamal Hadi Salim 			if (!do_promote) {
384fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3851da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3861da177e4SLinus Torvalds 
38715e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
388e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
389e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3901da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3918f937c60SHarald Welte 			} else {
3928f937c60SHarald Welte 				promote = ifa;
3938f937c60SHarald Welte 				break;
3948f937c60SHarald Welte 			}
3951da177e4SLinus Torvalds 		}
3961da177e4SLinus Torvalds 	}
3971da177e4SLinus Torvalds 
3982d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3992d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4002d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4012d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4022d230e2bSJulian Anastasov 	 */
4032638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4042d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4052d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4062d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4072d230e2bSJulian Anastasov 	}
4082d230e2bSJulian Anastasov 
409fbd40ea0SDavid S. Miller no_promotions:
4101da177e4SLinus Torvalds 	/* 2. Unlink it */
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
413fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4181da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4191da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4201da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4211da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4221da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4231da177e4SLinus Torvalds 	   So that, this order is correct.
4241da177e4SLinus Torvalds 	 */
42515e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
426e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4270ff60a45SJamal Hadi Salim 
4280ff60a45SJamal Hadi Salim 	if (promote) {
4292638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4300ff60a45SJamal Hadi Salim 
4312638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4320ff60a45SJamal Hadi Salim 		if (prev_prom) {
4332638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4342638eb8bSFlorian Westphal 
4352638eb8bSFlorian Westphal 			last_sec = rtnl_dereference(last_prim->ifa_next);
4362638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4372638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
4382638eb8bSFlorian Westphal 			rcu_assign_pointer(last_prim->ifa_next, promote);
4390ff60a45SJamal Hadi Salim 		}
4400ff60a45SJamal Hadi Salim 
4410ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
44215e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
443e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
444e041c683SAlan Stern 				NETDEV_UP, promote);
4452638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4462638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4470ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4480ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4490ff60a45SJamal Hadi Salim 					continue;
4500ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4510ff60a45SJamal Hadi Salim 		}
4520ff60a45SJamal Hadi Salim 
4530ff60a45SJamal Hadi Salim 	}
4546363097cSHerbert Xu 	if (destroy)
4551da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds 
4582638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4592638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
460d6062cbbSThomas Graf 			 int destroy)
461d6062cbbSThomas Graf {
462d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
463d6062cbbSThomas Graf }
464d6062cbbSThomas Graf 
4655c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4665c766d64SJiri Pirko 
4675c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4685c766d64SJiri Pirko 
469d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
470de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4711da177e4SLinus Torvalds {
4722638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4731da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4743ad7d246SKrister Johansen 	struct in_validator_info ivi;
4752638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4763ad7d246SKrister Johansen 	int ret;
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	ASSERT_RTNL();
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4811da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4821da177e4SLinus Torvalds 		return 0;
4831da177e4SLinus Torvalds 	}
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4861da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4871da177e4SLinus Torvalds 
4882638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
4892638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
4902638eb8bSFlorian Westphal 
4912638eb8bSFlorian Westphal 	while (ifa1) {
4921da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4931da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4941da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4951da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4961da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4971da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4981da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4991da177e4SLinus Torvalds 				return -EEXIST;
5001da177e4SLinus Torvalds 			}
5011da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
5021da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5031da177e4SLinus Torvalds 				return -EINVAL;
5041da177e4SLinus Torvalds 			}
5051da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5061da177e4SLinus Torvalds 		}
5072638eb8bSFlorian Westphal 
5082638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5092638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5101da177e4SLinus Torvalds 	}
5111da177e4SLinus Torvalds 
5123ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5133ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5143ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5153ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5163ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5173ad7d246SKrister Johansen 	 * not the flags.
5183ad7d246SKrister Johansen 	 */
5193ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5203ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
521de95e047SDavid Ahern 	ivi.extack = extack;
5223ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5233ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5243ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5253ad7d246SKrister Johansen 	if (ret) {
5263ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5273ad7d246SKrister Johansen 		return ret;
5283ad7d246SKrister Johansen 	}
5293ad7d246SKrister Johansen 
5301da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
53163862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
5321da177e4SLinus Torvalds 		ifap = last_primary;
5331da177e4SLinus Torvalds 	}
5341da177e4SLinus Torvalds 
5352638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5362638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5371da177e4SLinus Torvalds 
538fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
539fd23c3b3SDavid S. Miller 
5405c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
541906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5425c766d64SJiri Pirko 
5431da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5441da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5451da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
54615e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
547e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds 	return 0;
5501da177e4SLinus Torvalds }
5511da177e4SLinus Torvalds 
552d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
553d6062cbbSThomas Graf {
554de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
555d6062cbbSThomas Graf }
556d6062cbbSThomas Graf 
5571da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5581da177e4SLinus Torvalds {
559e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	ASSERT_RTNL();
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	if (!in_dev) {
5641da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5651da177e4SLinus Torvalds 		return -ENOBUFS;
5661da177e4SLinus Torvalds 	}
56771e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5681d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5691da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
570547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5711da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5721da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5731da177e4SLinus Torvalds 	}
574f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5751da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5761da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5771da177e4SLinus Torvalds }
5781da177e4SLinus Torvalds 
5798723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5808723e1b4SEric Dumazet  * We dont take a reference on found in_device
5818723e1b4SEric Dumazet  */
5827fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5831da177e4SLinus Torvalds {
5841da177e4SLinus Torvalds 	struct net_device *dev;
5851da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
586c148fc2eSEric Dumazet 
587c148fc2eSEric Dumazet 	rcu_read_lock();
588c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5891da177e4SLinus Torvalds 	if (dev)
5908723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
591c148fc2eSEric Dumazet 	rcu_read_unlock();
5921da177e4SLinus Torvalds 	return in_dev;
5931da177e4SLinus Torvalds }
5949f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5971da177e4SLinus Torvalds 
59860cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
59960cad5daSAl Viro 				    __be32 mask)
6001da177e4SLinus Torvalds {
601d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
602d519e870SFlorian Westphal 
6031da177e4SLinus Torvalds 	ASSERT_RTNL();
6041da177e4SLinus Torvalds 
605d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6061da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6071da177e4SLinus Torvalds 			return ifa;
608d519e870SFlorian Westphal 	}
6091da177e4SLinus Torvalds 	return NULL;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds 
61293a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
61393a714d6SMadhu Challa {
61493a714d6SMadhu Challa 	struct ip_mreqn mreq = {
61593a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
61693a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
61793a714d6SMadhu Challa 	};
61893a714d6SMadhu Challa 	int ret;
61993a714d6SMadhu Challa 
62093a714d6SMadhu Challa 	ASSERT_RTNL();
62193a714d6SMadhu Challa 
62293a714d6SMadhu Challa 	lock_sock(sk);
62393a714d6SMadhu Challa 	if (join)
62454ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
62593a714d6SMadhu Challa 	else
62654ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
62793a714d6SMadhu Challa 	release_sock(sk);
62893a714d6SMadhu Challa 
62993a714d6SMadhu Challa 	return ret;
63093a714d6SMadhu Challa }
63193a714d6SMadhu Challa 
632c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
633c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6341da177e4SLinus Torvalds {
6353b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6362638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
637dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6381da177e4SLinus Torvalds 	struct in_device *in_dev;
639dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6402638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
6412638eb8bSFlorian Westphal 
642dfdd5fd4SThomas Graf 	int err = -EINVAL;
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 	ASSERT_RTNL();
6451da177e4SLinus Torvalds 
6468cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6478cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
648dfdd5fd4SThomas Graf 	if (err < 0)
649dfdd5fd4SThomas Graf 		goto errout;
650dfdd5fd4SThomas Graf 
651dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6527fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
65351456b29SIan Morris 	if (!in_dev) {
654dfdd5fd4SThomas Graf 		err = -ENODEV;
655dfdd5fd4SThomas Graf 		goto errout;
656dfdd5fd4SThomas Graf 	}
657dfdd5fd4SThomas Graf 
6582638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6591da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
660dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
66167b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6621da177e4SLinus Torvalds 			continue;
663dfdd5fd4SThomas Graf 
664dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
665dfdd5fd4SThomas Graf 			continue;
666dfdd5fd4SThomas Graf 
667dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
668dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
66967b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
670dfdd5fd4SThomas Graf 			continue;
671dfdd5fd4SThomas Graf 
67293a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
67393a714d6SMadhu Challa 			ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
67415e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6751da177e4SLinus Torvalds 		return 0;
6761da177e4SLinus Torvalds 	}
677dfdd5fd4SThomas Graf 
678dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
679dfdd5fd4SThomas Graf errout:
680dfdd5fd4SThomas Graf 	return err;
6811da177e4SLinus Torvalds }
6821da177e4SLinus Torvalds 
6835c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6845c766d64SJiri Pirko 
6855c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6865c766d64SJiri Pirko {
6875c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6885c766d64SJiri Pirko 	struct in_ifaddr *ifa;
689c988d1e8SJiri Pirko 	struct hlist_node *n;
6905c766d64SJiri Pirko 	int i;
6915c766d64SJiri Pirko 
6925c766d64SJiri Pirko 	now = jiffies;
6935c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6945c766d64SJiri Pirko 
6955c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
696c988d1e8SJiri Pirko 		bool change_needed = false;
697c988d1e8SJiri Pirko 
698c988d1e8SJiri Pirko 		rcu_read_lock();
699b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
7005c766d64SJiri Pirko 			unsigned long age;
7015c766d64SJiri Pirko 
7025c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
7035c766d64SJiri Pirko 				continue;
7045c766d64SJiri Pirko 
7055c766d64SJiri Pirko 			/* We try to batch several events at once. */
7065c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
7075c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7085c766d64SJiri Pirko 
7095c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
7105c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
711c988d1e8SJiri Pirko 				change_needed = true;
712c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
713c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
714c988d1e8SJiri Pirko 				continue;
715c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
716c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
717c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
718c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
719c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
720c988d1e8SJiri Pirko 
721c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
722c988d1e8SJiri Pirko 					change_needed = true;
723c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
724c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
725c988d1e8SJiri Pirko 					       next)) {
726c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
727c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
728c988d1e8SJiri Pirko 			}
729c988d1e8SJiri Pirko 		}
730c988d1e8SJiri Pirko 		rcu_read_unlock();
731c988d1e8SJiri Pirko 		if (!change_needed)
732c988d1e8SJiri Pirko 			continue;
733c988d1e8SJiri Pirko 		rtnl_lock();
734c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
735c988d1e8SJiri Pirko 			unsigned long age;
736c988d1e8SJiri Pirko 
737c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
738c988d1e8SJiri Pirko 				continue;
739c988d1e8SJiri Pirko 
740c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
741c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
742c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
743c988d1e8SJiri Pirko 
744c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
745c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7462638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7472638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7485c766d64SJiri Pirko 
7492638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7502638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7512638eb8bSFlorian Westphal 				while (tmp) {
7522638eb8bSFlorian Westphal 					tmp = rtnl_dereference(tmp->ifa_next);
7532638eb8bSFlorian Westphal 					if (rtnl_dereference(*ifap) == ifa) {
7545c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7555c766d64SJiri Pirko 							     ifap, 1);
756c988d1e8SJiri Pirko 						break;
7575c766d64SJiri Pirko 					}
7582638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7592638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
760c988d1e8SJiri Pirko 				}
761c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
762c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
763c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
764c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7655c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7665c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7675c766d64SJiri Pirko 			}
7685c766d64SJiri Pirko 		}
769c988d1e8SJiri Pirko 		rtnl_unlock();
7705c766d64SJiri Pirko 	}
7715c766d64SJiri Pirko 
7725c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7735c766d64SJiri Pirko 	next_sched = next;
7745c766d64SJiri Pirko 
7755c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7765c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7775c766d64SJiri Pirko 		next_sched = next_sec;
7785c766d64SJiri Pirko 
7795c766d64SJiri Pirko 	now = jiffies;
7805c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7815c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7825c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7835c766d64SJiri Pirko 
784906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
785906e073fSviresh kumar 			next_sched - now);
7865c766d64SJiri Pirko }
7875c766d64SJiri Pirko 
7885c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7895c766d64SJiri Pirko 			     __u32 prefered_lft)
7905c766d64SJiri Pirko {
7915c766d64SJiri Pirko 	unsigned long timeout;
7925c766d64SJiri Pirko 
7935c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
7945c766d64SJiri Pirko 
7955c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
7965c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
7975c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7985c766d64SJiri Pirko 	else
7995c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
8005c766d64SJiri Pirko 
8015c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8025c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8035c766d64SJiri Pirko 		if (timeout == 0)
8045c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
8055c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
8065c766d64SJiri Pirko 	}
8075c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
8085c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
8095c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
8105c766d64SJiri Pirko }
8115c766d64SJiri Pirko 
8125c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
813dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
814dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8151da177e4SLinus Torvalds {
8165c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8175c753978SThomas Graf 	struct in_ifaddr *ifa;
8185c753978SThomas Graf 	struct ifaddrmsg *ifm;
8191da177e4SLinus Torvalds 	struct net_device *dev;
8201da177e4SLinus Torvalds 	struct in_device *in_dev;
8217b218574SDenis V. Lunev 	int err;
8221da177e4SLinus Torvalds 
8238cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8248cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8255c753978SThomas Graf 	if (err < 0)
8265c753978SThomas Graf 		goto errout;
8271da177e4SLinus Torvalds 
8285c753978SThomas Graf 	ifm = nlmsg_data(nlh);
829c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
83051456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8315c753978SThomas Graf 		goto errout;
8321da177e4SLinus Torvalds 
8334b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8345c753978SThomas Graf 	err = -ENODEV;
83551456b29SIan Morris 	if (!dev)
8365c753978SThomas Graf 		goto errout;
8371da177e4SLinus Torvalds 
8385c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8395c753978SThomas Graf 	err = -ENOBUFS;
84051456b29SIan Morris 	if (!in_dev)
8415c753978SThomas Graf 		goto errout;
84271e27da9SHerbert Xu 
8435c753978SThomas Graf 	ifa = inet_alloc_ifa();
84451456b29SIan Morris 	if (!ifa)
8455c753978SThomas Graf 		/*
8465c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8475c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8485c753978SThomas Graf 		 */
8495c753978SThomas Graf 		goto errout;
8505c753978SThomas Graf 
851a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8521d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8535c753978SThomas Graf 	in_dev_hold(in_dev);
8545c753978SThomas Graf 
85551456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8565c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8575c753978SThomas Graf 
858fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8591da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8601da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
861ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
862ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8631da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8641da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8655c753978SThomas Graf 
86667b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
86767b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8685c753978SThomas Graf 
8695c753978SThomas Graf 	if (tb[IFA_BROADCAST])
87067b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8715c753978SThomas Graf 
8725c753978SThomas Graf 	if (tb[IFA_LABEL])
8735c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8741da177e4SLinus Torvalds 	else
8751da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8761da177e4SLinus Torvalds 
877af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
878af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
879af4d768aSDavid Ahern 
8805c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8815c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8825c766d64SJiri Pirko 
8835c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8845c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8855c766d64SJiri Pirko 			err = -EINVAL;
886446266b0SDaniel Borkmann 			goto errout_free;
8875c766d64SJiri Pirko 		}
8885c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8895c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8905c766d64SJiri Pirko 	}
8915c766d64SJiri Pirko 
8925c753978SThomas Graf 	return ifa;
8935c753978SThomas Graf 
894446266b0SDaniel Borkmann errout_free:
895446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8965c753978SThomas Graf errout:
8975c753978SThomas Graf 	return ERR_PTR(err);
8985c753978SThomas Graf }
8995c753978SThomas Graf 
9005c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9015c766d64SJiri Pirko {
9025c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
903ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9045c766d64SJiri Pirko 
9055c766d64SJiri Pirko 	if (!ifa->ifa_local)
9065c766d64SJiri Pirko 		return NULL;
9075c766d64SJiri Pirko 
908ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9095c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9105c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9115c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9125c766d64SJiri Pirko 			return ifa1;
9135c766d64SJiri Pirko 	}
9145c766d64SJiri Pirko 	return NULL;
9155c766d64SJiri Pirko }
9165c766d64SJiri Pirko 
917c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
918c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9195c753978SThomas Graf {
9203b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9215c753978SThomas Graf 	struct in_ifaddr *ifa;
9225c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9235c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9245c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9255c753978SThomas Graf 
9265c753978SThomas Graf 	ASSERT_RTNL();
9275c753978SThomas Graf 
928dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9295c753978SThomas Graf 	if (IS_ERR(ifa))
9305c753978SThomas Graf 		return PTR_ERR(ifa);
9315c753978SThomas Graf 
9325c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9335c766d64SJiri Pirko 	if (!ifa_existing) {
9345c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
935614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9365c766d64SJiri Pirko 		 */
9375c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
93893a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
93993a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
94093a714d6SMadhu Challa 					       true, ifa);
94193a714d6SMadhu Challa 
94293a714d6SMadhu Challa 			if (ret < 0) {
94393a714d6SMadhu Challa 				inet_free_ifa(ifa);
94493a714d6SMadhu Challa 				return ret;
94593a714d6SMadhu Challa 			}
94693a714d6SMadhu Challa 		}
947de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
948de95e047SDavid Ahern 					 extack);
9495c766d64SJiri Pirko 	} else {
950af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
951af4d768aSDavid Ahern 
9525c766d64SJiri Pirko 		inet_free_ifa(ifa);
9535c766d64SJiri Pirko 
9545c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9555c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9565c766d64SJiri Pirko 			return -EEXIST;
95734e2ed34SJiri Pirko 		ifa = ifa_existing;
958af4d768aSDavid Ahern 
959af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
960af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
961af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
962af4d768aSDavid Ahern 		}
963af4d768aSDavid Ahern 
96434e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
96505a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
966906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
967906e073fSviresh kumar 				&check_lifetime_work, 0);
96834e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9695c766d64SJiri Pirko 	}
9705c766d64SJiri Pirko 	return 0;
9711da177e4SLinus Torvalds }
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds /*
9741da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9751da177e4SLinus Torvalds  */
9761da177e4SLinus Torvalds 
97740384999SEric Dumazet static int inet_abc_len(__be32 addr)
9781da177e4SLinus Torvalds {
9791da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9801da177e4SLinus Torvalds 
98165cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
9821da177e4SLinus Torvalds 		rc = 0;
9831da177e4SLinus Torvalds 	else {
984714e85beSAl Viro 		__u32 haddr = ntohl(addr);
985714e85beSAl Viro 		if (IN_CLASSA(haddr))
9861da177e4SLinus Torvalds 			rc = 8;
987714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9881da177e4SLinus Torvalds 			rc = 16;
989714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9901da177e4SLinus Torvalds 			rc = 24;
99165cab850SDave Taht 		else if (IN_CLASSE(haddr))
99265cab850SDave Taht 			rc = 32;
9931da177e4SLinus Torvalds 	}
9941da177e4SLinus Torvalds 
9951da177e4SLinus Torvalds 	return rc;
9961da177e4SLinus Torvalds }
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 
99903aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10001da177e4SLinus Torvalds {
10011da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
100203aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10032638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10041da177e4SLinus Torvalds 	struct in_device *in_dev;
10051da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10061da177e4SLinus Torvalds 	struct net_device *dev;
10071da177e4SLinus Torvalds 	char *colon;
10081da177e4SLinus Torvalds 	int ret = -EFAULT;
10091da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10101da177e4SLinus Torvalds 
101103aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10121da177e4SLinus Torvalds 
10131da177e4SLinus Torvalds 	/* save original address for comparison */
10141da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10151da177e4SLinus Torvalds 
101603aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10171da177e4SLinus Torvalds 	if (colon)
10181da177e4SLinus Torvalds 		*colon = 0;
10191da177e4SLinus Torvalds 
102003aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	switch (cmd) {
10231da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10241da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10251da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10261da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10271da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10281da177e4SLinus Torvalds 		   so that we do not impose a lock.
10291da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10301da177e4SLinus Torvalds 		 */
10311da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10321da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10331da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10341da177e4SLinus Torvalds 		break;
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1037bf5b30b8SZhao Hongjiang 		ret = -EPERM;
103852e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10391da177e4SLinus Torvalds 			goto out;
10401da177e4SLinus Torvalds 		break;
10411da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10421da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10431da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10441da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1045bf5b30b8SZhao Hongjiang 		ret = -EPERM;
104652e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10471da177e4SLinus Torvalds 			goto out;
10481da177e4SLinus Torvalds 		ret = -EINVAL;
10491da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10501da177e4SLinus Torvalds 			goto out;
10511da177e4SLinus Torvalds 		break;
10521da177e4SLinus Torvalds 	default:
10531da177e4SLinus Torvalds 		ret = -EINVAL;
10541da177e4SLinus Torvalds 		goto out;
10551da177e4SLinus Torvalds 	}
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	rtnl_lock();
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 	ret = -ENODEV;
106003aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10619f9354b9SEric Dumazet 	if (!dev)
10621da177e4SLinus Torvalds 		goto done;
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	if (colon)
10651da177e4SLinus Torvalds 		*colon = ':';
10661da177e4SLinus Torvalds 
10679f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10689f9354b9SEric Dumazet 	if (in_dev) {
10691da177e4SLinus Torvalds 		if (tryaddrmatch) {
10701da177e4SLinus Torvalds 			/* Matthias Andree */
10711da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10721da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10731da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10741da177e4SLinus Torvalds 			   This is checked above. */
10752638eb8bSFlorian Westphal 
10762638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
10772638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
10781da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
107903aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10801da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10816c91afe1SDavid S. Miller 							ifa->ifa_local) {
10821da177e4SLinus Torvalds 					break; /* found */
10831da177e4SLinus Torvalds 				}
10841da177e4SLinus Torvalds 			}
10851da177e4SLinus Torvalds 		}
10861da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10871da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10881da177e4SLinus Torvalds 		   comparing just the label */
10891da177e4SLinus Torvalds 		if (!ifa) {
10902638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
10912638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
10921da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
109303aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
10941da177e4SLinus Torvalds 					break;
10951da177e4SLinus Torvalds 		}
10961da177e4SLinus Torvalds 	}
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10991da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11001da177e4SLinus Torvalds 		goto done;
11011da177e4SLinus Torvalds 
11021da177e4SLinus Torvalds 	switch (cmd) {
11031da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
110430e948a3STonghao Zhang 		ret = 0;
11051da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
110603aef17bSAl Viro 		break;
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
110930e948a3STonghao Zhang 		ret = 0;
11101da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
111103aef17bSAl Viro 		break;
11121da177e4SLinus Torvalds 
11131da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
111430e948a3STonghao Zhang 		ret = 0;
11151da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
111603aef17bSAl Viro 		break;
11171da177e4SLinus Torvalds 
11181da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
111930e948a3STonghao Zhang 		ret = 0;
11201da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
112103aef17bSAl Viro 		break;
11221da177e4SLinus Torvalds 
11231da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11241da177e4SLinus Torvalds 		if (colon) {
11251da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11261da177e4SLinus Torvalds 			if (!ifa)
11271da177e4SLinus Torvalds 				break;
11281da177e4SLinus Torvalds 			ret = 0;
112903aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11301da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11311da177e4SLinus Torvalds 			break;
11321da177e4SLinus Torvalds 		}
1133567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11341da177e4SLinus Torvalds 		break;
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11371da177e4SLinus Torvalds 		ret = -EINVAL;
11381da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11391da177e4SLinus Torvalds 			break;
11401da177e4SLinus Torvalds 
11411da177e4SLinus Torvalds 		if (!ifa) {
11421da177e4SLinus Torvalds 			ret = -ENOBUFS;
11439f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11449f9354b9SEric Dumazet 			if (!ifa)
11451da177e4SLinus Torvalds 				break;
1146c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11471da177e4SLinus Torvalds 			if (colon)
114803aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11491da177e4SLinus Torvalds 			else
11501da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11511da177e4SLinus Torvalds 		} else {
11521da177e4SLinus Torvalds 			ret = 0;
11531da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11541da177e4SLinus Torvalds 				break;
11551da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11561da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1157148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11581da177e4SLinus Torvalds 		}
11591da177e4SLinus Torvalds 
11601da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11631da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11641da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11651da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11661da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11671da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11681da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11691da177e4SLinus Torvalds 		} else {
11701da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11711da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11721da177e4SLinus Torvalds 		}
11735c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11741da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11751da177e4SLinus Torvalds 		break;
11761da177e4SLinus Torvalds 
11771da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11781da177e4SLinus Torvalds 		ret = 0;
11791da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11801da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11811da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11821da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11831da177e4SLinus Torvalds 		}
11841da177e4SLinus Torvalds 		break;
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11871da177e4SLinus Torvalds 		ret = 0;
11881da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11891da177e4SLinus Torvalds 			break;
11901da177e4SLinus Torvalds 		ret = -EINVAL;
11911da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11921da177e4SLinus Torvalds 			break;
11931da177e4SLinus Torvalds 		ret = 0;
11941da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11951da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11961da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11971da177e4SLinus Torvalds 		break;
11981da177e4SLinus Torvalds 
11991da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12001da177e4SLinus Torvalds 
12011da177e4SLinus Torvalds 		/*
12021da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12031da177e4SLinus Torvalds 		 */
12041da177e4SLinus Torvalds 		ret = -EINVAL;
12051da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12061da177e4SLinus Torvalds 			break;
12071da177e4SLinus Torvalds 		ret = 0;
12081da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1209a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12101da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12111da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12121da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 			/* See if current broadcast address matches
12151da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12161da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12171da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12181da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12191da177e4SLinus Torvalds 			 */
12201da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12211da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12221da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1223dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12241da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12251da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12261da177e4SLinus Torvalds 			}
12271da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12281da177e4SLinus Torvalds 		}
12291da177e4SLinus Torvalds 		break;
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds done:
12321da177e4SLinus Torvalds 	rtnl_unlock();
12331da177e4SLinus Torvalds out:
12341da177e4SLinus Torvalds 	return ret;
12351da177e4SLinus Torvalds }
12361da177e4SLinus Torvalds 
123736fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12381da177e4SLinus Torvalds {
1239e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1240ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12411da177e4SLinus Torvalds 	struct ifreq ifr;
12421da177e4SLinus Torvalds 	int done = 0;
12431da177e4SLinus Torvalds 
124436fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
124536fd633eSAl Viro 		goto out;
124636fd633eSAl Viro 
12479f9354b9SEric Dumazet 	if (!in_dev)
12481da177e4SLinus Torvalds 		goto out;
12491da177e4SLinus Torvalds 
1250ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12511da177e4SLinus Torvalds 		if (!buf) {
125236fd633eSAl Viro 			done += size;
12531da177e4SLinus Torvalds 			continue;
12541da177e4SLinus Torvalds 		}
125536fd633eSAl Viro 		if (len < size)
12561da177e4SLinus Torvalds 			break;
12571da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12581da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12591da177e4SLinus Torvalds 
12601da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12611da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12621da177e4SLinus Torvalds 								ifa->ifa_local;
12631da177e4SLinus Torvalds 
126436fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12651da177e4SLinus Torvalds 			done = -EFAULT;
12661da177e4SLinus Torvalds 			break;
12671da177e4SLinus Torvalds 		}
126836fd633eSAl Viro 		len  -= size;
126936fd633eSAl Viro 		done += size;
12701da177e4SLinus Torvalds 	}
12711da177e4SLinus Torvalds out:
12721da177e4SLinus Torvalds 	return done;
12731da177e4SLinus Torvalds }
12741da177e4SLinus Torvalds 
12758b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12768b57fd1eSGao Feng 				 int scope)
12778b57fd1eSGao Feng {
1278d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1279d519e870SFlorian Westphal 
1280d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1281d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1282d519e870SFlorian Westphal 			continue;
12838b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12848b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12858b57fd1eSGao Feng 			return ifa->ifa_local;
1286d519e870SFlorian Westphal 	}
12878b57fd1eSGao Feng 
12888b57fd1eSGao Feng 	return 0;
12898b57fd1eSGao Feng }
12908b57fd1eSGao Feng 
1291a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
12921da177e4SLinus Torvalds {
1293d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1294a61ced5dSAl Viro 	__be32 addr = 0;
12951da177e4SLinus Torvalds 	struct in_device *in_dev;
1296c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12973f2fb9a8SDavid Ahern 	int master_idx;
12981da177e4SLinus Torvalds 
12991da177e4SLinus Torvalds 	rcu_read_lock();
1300e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13011da177e4SLinus Torvalds 	if (!in_dev)
13021da177e4SLinus Torvalds 		goto no_in_dev;
13031da177e4SLinus Torvalds 
1304d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1305d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1306d519e870SFlorian Westphal 			continue;
13071da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
13081da177e4SLinus Torvalds 			continue;
13091da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13101da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13111da177e4SLinus Torvalds 			break;
13121da177e4SLinus Torvalds 		}
13131da177e4SLinus Torvalds 		if (!addr)
13141da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1315d519e870SFlorian Westphal 	}
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds 	if (addr)
1318c6d14c84SEric Dumazet 		goto out_unlock;
13199f9354b9SEric Dumazet no_in_dev:
13203f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13211da177e4SLinus Torvalds 
132217b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
132317b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
132417b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
132517b693cdSDavid Lamparter 	 * equality check in the loop below.
132617b693cdSDavid Lamparter 	 */
132717b693cdSDavid Lamparter 	if (master_idx &&
132817b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
132917b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13308b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13318b57fd1eSGao Feng 		if (addr)
133217b693cdSDavid Lamparter 			goto out_unlock;
133317b693cdSDavid Lamparter 	}
133417b693cdSDavid Lamparter 
13351da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1336ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13371da177e4SLinus Torvalds 	   in dev_base list.
13381da177e4SLinus Torvalds 	 */
1339c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13403f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13413f2fb9a8SDavid Ahern 			continue;
13423f2fb9a8SDavid Ahern 
13439f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13449f9354b9SEric Dumazet 		if (!in_dev)
13451da177e4SLinus Torvalds 			continue;
13461da177e4SLinus Torvalds 
13478b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13488b57fd1eSGao Feng 		if (addr)
1349c6d14c84SEric Dumazet 			goto out_unlock;
13501da177e4SLinus Torvalds 	}
1351c6d14c84SEric Dumazet out_unlock:
13521da177e4SLinus Torvalds 	rcu_read_unlock();
13531da177e4SLinus Torvalds 	return addr;
13541da177e4SLinus Torvalds }
13559f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13561da177e4SLinus Torvalds 
135760cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
135860cad5daSAl Viro 			      __be32 local, int scope)
13591da177e4SLinus Torvalds {
1360ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1361a144ea4bSAl Viro 	__be32 addr = 0;
1362ef11db33SFlorian Westphal 	int same = 0;
13631da177e4SLinus Torvalds 
1364ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
13651da177e4SLinus Torvalds 		if (!addr &&
13661da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
13671da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
13681da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13691da177e4SLinus Torvalds 			if (same)
13701da177e4SLinus Torvalds 				break;
13711da177e4SLinus Torvalds 		}
13721da177e4SLinus Torvalds 		if (!same) {
13731da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13741da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13751da177e4SLinus Torvalds 			if (same && addr) {
13761da177e4SLinus Torvalds 				if (local || !dst)
13771da177e4SLinus Torvalds 					break;
13781da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
13791da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
13801da177e4SLinus Torvalds 					break;
13811da177e4SLinus Torvalds 				/* No, then can we use new local src? */
13821da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
13831da177e4SLinus Torvalds 					addr = ifa->ifa_local;
13841da177e4SLinus Torvalds 					break;
13851da177e4SLinus Torvalds 				}
13861da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
13871da177e4SLinus Torvalds 				same = 0;
13881da177e4SLinus Torvalds 			}
13891da177e4SLinus Torvalds 		}
1390ef11db33SFlorian Westphal 	}
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds 	return same ? addr : 0;
13931da177e4SLinus Torvalds }
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds /*
13961da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1397b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1398b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
13991da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14001da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14011da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14021da177e4SLinus Torvalds  */
1403b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14049bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14051da177e4SLinus Torvalds {
140660cad5daSAl Viro 	__be32 addr = 0;
14079bd85e32SDenis V. Lunev 	struct net_device *dev;
14081da177e4SLinus Torvalds 
140900db4124SIan Morris 	if (in_dev)
14109bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14111da177e4SLinus Torvalds 
14121da177e4SLinus Torvalds 	rcu_read_lock();
1413c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14149f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14159f9354b9SEric Dumazet 		if (in_dev) {
14161da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14171da177e4SLinus Torvalds 			if (addr)
14181da177e4SLinus Torvalds 				break;
14191da177e4SLinus Torvalds 		}
14201da177e4SLinus Torvalds 	}
14211da177e4SLinus Torvalds 	rcu_read_unlock();
14221da177e4SLinus Torvalds 
14231da177e4SLinus Torvalds 	return addr;
14241da177e4SLinus Torvalds }
1425eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14261da177e4SLinus Torvalds 
14271da177e4SLinus Torvalds /*
14281da177e4SLinus Torvalds  *	Device notifier
14291da177e4SLinus Torvalds  */
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14321da177e4SLinus Torvalds {
1433e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14341da177e4SLinus Torvalds }
14359f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14361da177e4SLinus Torvalds 
14371da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14381da177e4SLinus Torvalds {
1439e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14401da177e4SLinus Torvalds }
14419f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14421da177e4SLinus Torvalds 
14433ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14443ad7d246SKrister Johansen {
14453ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14463ad7d246SKrister Johansen }
14473ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14483ad7d246SKrister Johansen 
14493ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14503ad7d246SKrister Johansen {
14513ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14523ad7d246SKrister Johansen 	    nb);
14533ad7d246SKrister Johansen }
14543ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14553ad7d246SKrister Johansen 
14569f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14579f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14581da177e4SLinus Torvalds */
14591da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14601da177e4SLinus Torvalds {
14611da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14621da177e4SLinus Torvalds 	int named = 0;
14631da177e4SLinus Torvalds 
1464ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
14651da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14661da177e4SLinus Torvalds 
14671da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14681da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14691da177e4SLinus Torvalds 		if (named++ == 0)
1470573bf470SThomas Graf 			goto skip;
147144344b2aSMark McLoughlin 		dot = strchr(old, ':');
147251456b29SIan Morris 		if (!dot) {
14731da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14741da177e4SLinus Torvalds 			dot = old;
14751da177e4SLinus Torvalds 		}
14769f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14771da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14789f9354b9SEric Dumazet 		else
14791da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1480573bf470SThomas Graf skip:
1481573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
14821da177e4SLinus Torvalds 	}
14831da177e4SLinus Torvalds }
14841da177e4SLinus Torvalds 
148540384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
148606770843SBreno Leitao {
1487b5476022SEric Dumazet 	return mtu >= IPV4_MIN_MTU;
148806770843SBreno Leitao }
148906770843SBreno Leitao 
1490d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1491d11327adSIan Campbell 					struct in_device *in_dev)
1492d11327adSIan Campbell 
1493d11327adSIan Campbell {
1494ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1495d11327adSIan Campbell 
1496ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1497d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
14986c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
14996c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1500d11327adSIan Campbell 			 dev->dev_addr, NULL);
1501d11327adSIan Campbell 	}
1502b76d0789SZoltan Kiss }
1503d11327adSIan Campbell 
15041da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15051da177e4SLinus Torvalds 
15061da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15071da177e4SLinus Torvalds 			 void *ptr)
15081da177e4SLinus Torvalds {
1509351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1510748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15111da177e4SLinus Torvalds 
15121da177e4SLinus Torvalds 	ASSERT_RTNL();
15131da177e4SLinus Torvalds 
15141da177e4SLinus Torvalds 	if (!in_dev) {
15158030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15161da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
151720e61da7SWANG Cong 			if (IS_ERR(in_dev))
151820e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15190cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
152042f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
152142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15221da177e4SLinus Torvalds 			}
152306770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
152406770843SBreno Leitao 			/* Re-enabling IP */
152506770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
152606770843SBreno Leitao 				in_dev = inetdev_init(dev);
15278030f544SHerbert Xu 		}
15281da177e4SLinus Torvalds 		goto out;
15291da177e4SLinus Torvalds 	}
15301da177e4SLinus Torvalds 
15311da177e4SLinus Torvalds 	switch (event) {
15321da177e4SLinus Torvalds 	case NETDEV_REGISTER:
153391df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1534a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15351da177e4SLinus Torvalds 		break;
15361da177e4SLinus Torvalds 	case NETDEV_UP:
153706770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15381da177e4SLinus Torvalds 			break;
15390cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15409f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15419f9354b9SEric Dumazet 
15429f9354b9SEric Dumazet 			if (ifa) {
1543fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15441da177e4SLinus Torvalds 				ifa->ifa_local =
15451da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15461da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15471da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15481da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15491da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15501da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15511da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15525c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15535c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1554dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1555dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15561da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15571da177e4SLinus Torvalds 			}
15581da177e4SLinus Torvalds 		}
15591da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1560eefef1cfSStephen Hemminger 		/* fall through */
1561eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1562d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1563d11327adSIan Campbell 			break;
1564d11327adSIan Campbell 		/* fall through */
1565d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1566a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1567d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15681da177e4SLinus Torvalds 		break;
15691da177e4SLinus Torvalds 	case NETDEV_DOWN:
15701da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15711da177e4SLinus Torvalds 		break;
157293d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
157375c78500SMoni Shoua 		ip_mc_unmap(in_dev);
157475c78500SMoni Shoua 		break;
157593d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
157675c78500SMoni Shoua 		ip_mc_remap(in_dev);
157775c78500SMoni Shoua 		break;
15781da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
157906770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15801da177e4SLinus Torvalds 			break;
158106770843SBreno Leitao 		/* disable IP when MTU is not enough */
1582fcfd6dfaSGustavo A. R. Silva 		/* fall through */
15831da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
15841da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
15851da177e4SLinus Torvalds 		break;
15861da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
15871da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
15881da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
15891da177e4SLinus Torvalds 		 */
15901da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
15911da177e4SLinus Torvalds 
159251602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
159366f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
15941da177e4SLinus Torvalds 		break;
15951da177e4SLinus Torvalds 	}
15961da177e4SLinus Torvalds out:
15971da177e4SLinus Torvalds 	return NOTIFY_DONE;
15981da177e4SLinus Torvalds }
15991da177e4SLinus Torvalds 
16001da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16011da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16021da177e4SLinus Torvalds };
16031da177e4SLinus Torvalds 
160440384999SEric Dumazet static size_t inet_nlmsg_size(void)
1605339bf98fSThomas Graf {
1606339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1607339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1608339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1609339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1610ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
161163b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1612af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
161363b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1614339bf98fSThomas Graf }
1615339bf98fSThomas Graf 
16165c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16175c766d64SJiri Pirko {
16185c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16195c766d64SJiri Pirko }
16205c766d64SJiri Pirko 
16215c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16225c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16235c766d64SJiri Pirko {
16245c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16255c766d64SJiri Pirko 
16265c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16275c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16285c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16295c766d64SJiri Pirko 	ci.ifa_valid = valid;
16305c766d64SJiri Pirko 
16315c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16325c766d64SJiri Pirko }
16335c766d64SJiri Pirko 
16341da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1635978a46faSChristian Brauner 			    struct inet_fill_args *args)
16361da177e4SLinus Torvalds {
16371da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16381da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16395c766d64SJiri Pirko 	u32 preferred, valid;
16401da177e4SLinus Torvalds 
1641978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1642978a46faSChristian Brauner 			args->flags);
164351456b29SIan Morris 	if (!nlh)
164426932566SPatrick McHardy 		return -EMSGSIZE;
164547f68512SThomas Graf 
164647f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16471da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16481da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16495c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16501da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16511da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16521da177e4SLinus Torvalds 
1653978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1654978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1655d3807145SChristian Brauner 		goto nla_put_failure;
1656d3807145SChristian Brauner 
16575c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16585c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16595c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16605c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16615c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16625c766d64SJiri Pirko 
16635c766d64SJiri Pirko 			if (preferred > tval)
16645c766d64SJiri Pirko 				preferred -= tval;
16655c766d64SJiri Pirko 			else
16665c766d64SJiri Pirko 				preferred = 0;
16675c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16685c766d64SJiri Pirko 				if (valid > tval)
16695c766d64SJiri Pirko 					valid -= tval;
16705c766d64SJiri Pirko 				else
16715c766d64SJiri Pirko 					valid = 0;
16725c766d64SJiri Pirko 			}
16735c766d64SJiri Pirko 		}
16745c766d64SJiri Pirko 	} else {
16755c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16765c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16775c766d64SJiri Pirko 	}
1678f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1679930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1680f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1681930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1682f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1683930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1684f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
16855c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1686ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1687af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1688af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
16895c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
16905c766d64SJiri Pirko 			  preferred, valid))
1691f3756b79SDavid S. Miller 		goto nla_put_failure;
169247f68512SThomas Graf 
1693053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1694053c095aSJohannes Berg 	return 0;
169547f68512SThomas Graf 
169647f68512SThomas Graf nla_put_failure:
169726932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
169826932566SPatrick McHardy 	return -EMSGSIZE;
16991da177e4SLinus Torvalds }
17001da177e4SLinus Torvalds 
1701c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1702c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1703c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17045fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1705c33078e3SDavid Ahern {
17065fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1707c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1708c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1709c33078e3SDavid Ahern 	int err, i;
1710c33078e3SDavid Ahern 
1711c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1712c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1713c33078e3SDavid Ahern 		return -EINVAL;
1714c33078e3SDavid Ahern 	}
1715c33078e3SDavid Ahern 
1716c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1717c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1718c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1719c33078e3SDavid Ahern 		return -EINVAL;
1720c33078e3SDavid Ahern 	}
17215fcd266aSDavid Ahern 
17225fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17235fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17245fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17255fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1726c33078e3SDavid Ahern 	}
1727c33078e3SDavid Ahern 
17288cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1729c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1730c33078e3SDavid Ahern 	if (err < 0)
1731c33078e3SDavid Ahern 		return err;
1732c33078e3SDavid Ahern 
1733c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1734c33078e3SDavid Ahern 		if (!tb[i])
1735c33078e3SDavid Ahern 			continue;
1736c33078e3SDavid Ahern 
1737c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1738c33078e3SDavid Ahern 			struct net *net;
1739c33078e3SDavid Ahern 
1740c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1741c33078e3SDavid Ahern 
1742c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1743c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1744bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1745c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1746c33078e3SDavid Ahern 				return PTR_ERR(net);
1747c33078e3SDavid Ahern 			}
1748c33078e3SDavid Ahern 			*tgt_net = net;
1749c33078e3SDavid Ahern 		} else {
1750c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1751c33078e3SDavid Ahern 			return -EINVAL;
1752c33078e3SDavid Ahern 		}
1753c33078e3SDavid Ahern 	}
1754c33078e3SDavid Ahern 
1755c33078e3SDavid Ahern 	return 0;
1756c33078e3SDavid Ahern }
1757c33078e3SDavid Ahern 
17581c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
17591c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
17601c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
17611c98eca4SDavid Ahern {
17621c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
17631c98eca4SDavid Ahern 	int ip_idx = 0;
17641c98eca4SDavid Ahern 	int err;
17651c98eca4SDavid Ahern 
1766d3e6e285SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1767ef11db33SFlorian Westphal 		if (ip_idx < s_ip_idx) {
1768ef11db33SFlorian Westphal 			ip_idx++;
17691c98eca4SDavid Ahern 			continue;
1770ef11db33SFlorian Westphal 		}
17711c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
17721c98eca4SDavid Ahern 		if (err < 0)
17731c98eca4SDavid Ahern 			goto done;
17741c98eca4SDavid Ahern 
17751c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1776ef11db33SFlorian Westphal 		ip_idx++;
17771c98eca4SDavid Ahern 	}
17781c98eca4SDavid Ahern 	err = 0;
17791c98eca4SDavid Ahern 
17801c98eca4SDavid Ahern done:
17811c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
17821c98eca4SDavid Ahern 
17831c98eca4SDavid Ahern 	return err;
17841c98eca4SDavid Ahern }
17851c98eca4SDavid Ahern 
17861da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
17871da177e4SLinus Torvalds {
1788c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1789978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1790978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1791c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1792978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1793978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1794978a46faSChristian Brauner 		.netnsid = -1,
1795978a46faSChristian Brauner 	};
17963b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1797d3807145SChristian Brauner 	struct net *tgt_net = net;
1798eec4df98SEric Dumazet 	int h, s_h;
1799eec4df98SEric Dumazet 	int idx, s_idx;
18001c98eca4SDavid Ahern 	int s_ip_idx;
18011da177e4SLinus Torvalds 	struct net_device *dev;
18021da177e4SLinus Torvalds 	struct in_device *in_dev;
1803eec4df98SEric Dumazet 	struct hlist_head *head;
1804d7e38611SDavid Ahern 	int err = 0;
18051da177e4SLinus Torvalds 
1806eec4df98SEric Dumazet 	s_h = cb->args[0];
1807eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
18081c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1809eec4df98SEric Dumazet 
1810c33078e3SDavid Ahern 	if (cb->strict_check) {
1811c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18125fcd266aSDavid Ahern 						 skb->sk, cb);
1813c33078e3SDavid Ahern 		if (err < 0)
1814d7e38611SDavid Ahern 			goto put_tgt_net;
18155fcd266aSDavid Ahern 
1816d7e38611SDavid Ahern 		err = 0;
18175fcd266aSDavid Ahern 		if (fillargs.ifindex) {
18185fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1819d7e38611SDavid Ahern 			if (!dev) {
1820d7e38611SDavid Ahern 				err = -ENODEV;
1821d7e38611SDavid Ahern 				goto put_tgt_net;
1822d7e38611SDavid Ahern 			}
18235fcd266aSDavid Ahern 
18245fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
18255fcd266aSDavid Ahern 			if (in_dev) {
18265fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18275fcd266aSDavid Ahern 						       &fillargs);
18285fcd266aSDavid Ahern 			}
18295fcd266aSDavid Ahern 			goto put_tgt_net;
18305fcd266aSDavid Ahern 		}
1831d3807145SChristian Brauner 	}
1832d3807145SChristian Brauner 
1833eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18347562f876SPavel Emelianov 		idx = 0;
1835d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1836eec4df98SEric Dumazet 		rcu_read_lock();
1837d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1838d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1839b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18401da177e4SLinus Torvalds 			if (idx < s_idx)
18417562f876SPavel Emelianov 				goto cont;
18424b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
18431da177e4SLinus Torvalds 				s_ip_idx = 0;
1844eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18459f9354b9SEric Dumazet 			if (!in_dev)
18467562f876SPavel Emelianov 				goto cont;
18471da177e4SLinus Torvalds 
18481c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18491c98eca4SDavid Ahern 					       &fillargs);
18501c98eca4SDavid Ahern 			if (err < 0) {
1851eec4df98SEric Dumazet 				rcu_read_unlock();
18521da177e4SLinus Torvalds 				goto done;
18531da177e4SLinus Torvalds 			}
18547562f876SPavel Emelianov cont:
18557562f876SPavel Emelianov 			idx++;
18561da177e4SLinus Torvalds 		}
1857eec4df98SEric Dumazet 		rcu_read_unlock();
1858eec4df98SEric Dumazet 	}
18591da177e4SLinus Torvalds 
18601da177e4SLinus Torvalds done:
1861eec4df98SEric Dumazet 	cb->args[0] = h;
1862eec4df98SEric Dumazet 	cb->args[1] = idx;
18635fcd266aSDavid Ahern put_tgt_net:
1864978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1865d3807145SChristian Brauner 		put_net(tgt_net);
18661da177e4SLinus Torvalds 
18677c1e8a38SArthur Gautier 	return skb->len ? : err;
18681da177e4SLinus Torvalds }
18691da177e4SLinus Torvalds 
1870d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
187115e47304SEric W. Biederman 		      u32 portid)
18721da177e4SLinus Torvalds {
1873978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1874978a46faSChristian Brauner 		.portid = portid,
1875978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1876978a46faSChristian Brauner 		.event = event,
1877978a46faSChristian Brauner 		.flags = 0,
1878978a46faSChristian Brauner 		.netnsid = -1,
1879978a46faSChristian Brauner 	};
188047f68512SThomas Graf 	struct sk_buff *skb;
1881d6062cbbSThomas Graf 	int err = -ENOBUFS;
18824b8aa9abSDenis V. Lunev 	struct net *net;
18831da177e4SLinus Torvalds 
1884c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1885339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
188651456b29SIan Morris 	if (!skb)
1887d6062cbbSThomas Graf 		goto errout;
1888d6062cbbSThomas Graf 
1889978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
189026932566SPatrick McHardy 	if (err < 0) {
189126932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
189226932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
189326932566SPatrick McHardy 		kfree_skb(skb);
189426932566SPatrick McHardy 		goto errout;
189526932566SPatrick McHardy 	}
189615e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
18971ce85fe4SPablo Neira Ayuso 	return;
1898d6062cbbSThomas Graf errout:
1899d6062cbbSThomas Graf 	if (err < 0)
19004b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19011da177e4SLinus Torvalds }
19021da177e4SLinus Torvalds 
1903b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1904b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19059f0f7272SThomas Graf {
19061fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19079f0f7272SThomas Graf 
19089f0f7272SThomas Graf 	if (!in_dev)
19099f0f7272SThomas Graf 		return 0;
19109f0f7272SThomas Graf 
19119f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19129f0f7272SThomas Graf }
19139f0f7272SThomas Graf 
1914d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1915d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19169f0f7272SThomas Graf {
19171fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19189f0f7272SThomas Graf 	struct nlattr *nla;
19199f0f7272SThomas Graf 	int i;
19209f0f7272SThomas Graf 
19219f0f7272SThomas Graf 	if (!in_dev)
19229f0f7272SThomas Graf 		return -ENODATA;
19239f0f7272SThomas Graf 
19249f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
192551456b29SIan Morris 	if (!nla)
19269f0f7272SThomas Graf 		return -EMSGSIZE;
19279f0f7272SThomas Graf 
19289f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19299f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
19309f0f7272SThomas Graf 
19319f0f7272SThomas Graf 	return 0;
19329f0f7272SThomas Graf }
19339f0f7272SThomas Graf 
19349f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19359f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19369f0f7272SThomas Graf };
19379f0f7272SThomas Graf 
1938cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1939cf7afbfeSThomas Graf 				 const struct nlattr *nla)
19409f0f7272SThomas Graf {
19419f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19429f0f7272SThomas Graf 	int err, rem;
19439f0f7272SThomas Graf 
19445fa85a09SFlorian Westphal 	if (dev && !__in_dev_get_rcu(dev))
1945cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19469f0f7272SThomas Graf 
19478cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
19488cb08174SJohannes Berg 					  inet_af_policy, NULL);
19499f0f7272SThomas Graf 	if (err < 0)
19509f0f7272SThomas Graf 		return err;
19519f0f7272SThomas Graf 
19529f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19539f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
19549f0f7272SThomas Graf 			int cfgid = nla_type(a);
19559f0f7272SThomas Graf 
19569f0f7272SThomas Graf 			if (nla_len(a) < 4)
19579f0f7272SThomas Graf 				return -EINVAL;
19589f0f7272SThomas Graf 
19599f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
19609f0f7272SThomas Graf 				return -EINVAL;
19619f0f7272SThomas Graf 		}
19629f0f7272SThomas Graf 	}
19639f0f7272SThomas Graf 
1964cf7afbfeSThomas Graf 	return 0;
1965cf7afbfeSThomas Graf }
1966cf7afbfeSThomas Graf 
1967cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1968cf7afbfeSThomas Graf {
19695fa85a09SFlorian Westphal 	struct in_device *in_dev = __in_dev_get_rcu(dev);
1970cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1971cf7afbfeSThomas Graf 	int rem;
1972cf7afbfeSThomas Graf 
1973cf7afbfeSThomas Graf 	if (!in_dev)
1974cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1975cf7afbfeSThomas Graf 
19768cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
1977cf7afbfeSThomas Graf 		BUG();
1978cf7afbfeSThomas Graf 
19799f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19809f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
19819f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
19829f0f7272SThomas Graf 	}
19839f0f7272SThomas Graf 
19849f0f7272SThomas Graf 	return 0;
19859f0f7272SThomas Graf }
19869f0f7272SThomas Graf 
1987edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1988edc9e748SNicolas Dichtel {
1989edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1990edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1991136ba622SZhang Shengju 	bool all = false;
1992edc9e748SNicolas Dichtel 
1993136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1994136ba622SZhang Shengju 		all = true;
1995136ba622SZhang Shengju 
1996136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
1997edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1998136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
1999cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2000136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2001d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20025cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20035cbf777cSXin Long 		size += nla_total_size(4);
2004136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2005f085ff1cSstephen hemminger 		size += nla_total_size(4);
2006136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2007974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2008edc9e748SNicolas Dichtel 
2009edc9e748SNicolas Dichtel 	return size;
2010edc9e748SNicolas Dichtel }
2011edc9e748SNicolas Dichtel 
2012edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
2013edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
2014edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
2015edc9e748SNicolas Dichtel 				     int type)
2016edc9e748SNicolas Dichtel {
2017edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2018edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2019136ba622SZhang Shengju 	bool all = false;
2020edc9e748SNicolas Dichtel 
2021edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2022edc9e748SNicolas Dichtel 			flags);
202351456b29SIan Morris 	if (!nlh)
2024edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2025edc9e748SNicolas Dichtel 
2026136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2027136ba622SZhang Shengju 		all = true;
2028136ba622SZhang Shengju 
2029edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2030edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2031edc9e748SNicolas Dichtel 
2032edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2033edc9e748SNicolas Dichtel 		goto nla_put_failure;
2034edc9e748SNicolas Dichtel 
2035b5c9641dSDavid Ahern 	if (!devconf)
2036b5c9641dSDavid Ahern 		goto out;
2037b5c9641dSDavid Ahern 
2038136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2039edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2040edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2041edc9e748SNicolas Dichtel 		goto nla_put_failure;
2042136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2043cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2044cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2045cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2046136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2047d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2048d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2049d67b8c61SNicolas Dichtel 		goto nla_put_failure;
20505cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
20515cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
20525cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
20535cbf777cSXin Long 		goto nla_put_failure;
2054136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
205509aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2056f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2057f085ff1cSstephen hemminger 		goto nla_put_failure;
2058136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2059974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2060974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2061974d7af5SAndy Gospodarek 		goto nla_put_failure;
2062edc9e748SNicolas Dichtel 
2063b5c9641dSDavid Ahern out:
2064053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2065053c095aSJohannes Berg 	return 0;
2066edc9e748SNicolas Dichtel 
2067edc9e748SNicolas Dichtel nla_put_failure:
2068edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2069edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2070edc9e748SNicolas Dichtel }
2071edc9e748SNicolas Dichtel 
20723b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
20733b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2074edc9e748SNicolas Dichtel {
2075edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2076edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2077edc9e748SNicolas Dichtel 
2078fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
207951456b29SIan Morris 	if (!skb)
2080edc9e748SNicolas Dichtel 		goto errout;
2081edc9e748SNicolas Dichtel 
2082edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
20833b022865SDavid Ahern 					event, 0, type);
2084edc9e748SNicolas Dichtel 	if (err < 0) {
2085edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2086edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2087edc9e748SNicolas Dichtel 		kfree_skb(skb);
2088edc9e748SNicolas Dichtel 		goto errout;
2089edc9e748SNicolas Dichtel 	}
2090fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2091edc9e748SNicolas Dichtel 	return;
2092edc9e748SNicolas Dichtel errout:
2093edc9e748SNicolas Dichtel 	if (err < 0)
2094edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2095edc9e748SNicolas Dichtel }
2096edc9e748SNicolas Dichtel 
20979e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
20989e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
20999e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2100cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
210109aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2102974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21039e551110SNicolas Dichtel };
21049e551110SNicolas Dichtel 
2105eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2106eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2107eede370dSJakub Kicinski 				      struct nlattr **tb,
2108eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2109eede370dSJakub Kicinski {
2110eede370dSJakub Kicinski 	int i, err;
2111eede370dSJakub Kicinski 
2112eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2113eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2114eede370dSJakub Kicinski 		return -EINVAL;
2115eede370dSJakub Kicinski 	}
2116eede370dSJakub Kicinski 
2117eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21188cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21198cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21208cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2121eede370dSJakub Kicinski 
21228cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21238cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21248cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2125eede370dSJakub Kicinski 	if (err)
2126eede370dSJakub Kicinski 		return err;
2127eede370dSJakub Kicinski 
2128eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2129eede370dSJakub Kicinski 		if (!tb[i])
2130eede370dSJakub Kicinski 			continue;
2131eede370dSJakub Kicinski 
2132eede370dSJakub Kicinski 		switch (i) {
2133eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2134eede370dSJakub Kicinski 			break;
2135eede370dSJakub Kicinski 		default:
2136eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2137eede370dSJakub Kicinski 			return -EINVAL;
2138eede370dSJakub Kicinski 		}
2139eede370dSJakub Kicinski 	}
2140eede370dSJakub Kicinski 
2141eede370dSJakub Kicinski 	return 0;
2142eede370dSJakub Kicinski }
2143eede370dSJakub Kicinski 
21449e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2145c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2146c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
21479e551110SNicolas Dichtel {
21489e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
21499e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
21509e551110SNicolas Dichtel 	struct sk_buff *skb;
21519e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
21529e551110SNicolas Dichtel 	struct in_device *in_dev;
21539e551110SNicolas Dichtel 	struct net_device *dev;
21549e551110SNicolas Dichtel 	int ifindex;
21559e551110SNicolas Dichtel 	int err;
21569e551110SNicolas Dichtel 
2157eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2158eede370dSJakub Kicinski 	if (err)
21599e551110SNicolas Dichtel 		goto errout;
21609e551110SNicolas Dichtel 
2161a97eb33fSAnton Protopopov 	err = -EINVAL;
21629e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
21639e551110SNicolas Dichtel 		goto errout;
21649e551110SNicolas Dichtel 
21659e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
21669e551110SNicolas Dichtel 	switch (ifindex) {
21679e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
21689e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
21699e551110SNicolas Dichtel 		break;
21709e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
21719e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
21729e551110SNicolas Dichtel 		break;
21739e551110SNicolas Dichtel 	default:
21749e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
217551456b29SIan Morris 		if (!dev)
21769e551110SNicolas Dichtel 			goto errout;
21779e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
217851456b29SIan Morris 		if (!in_dev)
21799e551110SNicolas Dichtel 			goto errout;
21809e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
21819e551110SNicolas Dichtel 		break;
21829e551110SNicolas Dichtel 	}
21839e551110SNicolas Dichtel 
21849e551110SNicolas Dichtel 	err = -ENOBUFS;
2185fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
218651456b29SIan Morris 	if (!skb)
21879e551110SNicolas Dichtel 		goto errout;
21889e551110SNicolas Dichtel 
21899e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
21909e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
21919e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2192136ba622SZhang Shengju 					NETCONFA_ALL);
21939e551110SNicolas Dichtel 	if (err < 0) {
21949e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
21959e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
21969e551110SNicolas Dichtel 		kfree_skb(skb);
21979e551110SNicolas Dichtel 		goto errout;
21989e551110SNicolas Dichtel 	}
21999e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22009e551110SNicolas Dichtel errout:
22019e551110SNicolas Dichtel 	return err;
22029e551110SNicolas Dichtel }
22039e551110SNicolas Dichtel 
22047a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22057a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22067a674200SNicolas Dichtel {
2207addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22087a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
22097a674200SNicolas Dichtel 	int h, s_h;
22107a674200SNicolas Dichtel 	int idx, s_idx;
22117a674200SNicolas Dichtel 	struct net_device *dev;
22127a674200SNicolas Dichtel 	struct in_device *in_dev;
22137a674200SNicolas Dichtel 	struct hlist_head *head;
22147a674200SNicolas Dichtel 
2215addd383fSDavid Ahern 	if (cb->strict_check) {
2216addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2217addd383fSDavid Ahern 		struct netconfmsg *ncm;
2218addd383fSDavid Ahern 
2219addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2220addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2221addd383fSDavid Ahern 			return -EINVAL;
2222addd383fSDavid Ahern 		}
2223addd383fSDavid Ahern 
2224addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2225addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2226addd383fSDavid Ahern 			return -EINVAL;
2227addd383fSDavid Ahern 		}
2228addd383fSDavid Ahern 	}
2229addd383fSDavid Ahern 
22307a674200SNicolas Dichtel 	s_h = cb->args[0];
22317a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
22327a674200SNicolas Dichtel 
22337a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
22347a674200SNicolas Dichtel 		idx = 0;
22357a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
22367a674200SNicolas Dichtel 		rcu_read_lock();
22370465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
22380465277fSNicolas Dichtel 			  net->dev_base_seq;
22397a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
22407a674200SNicolas Dichtel 			if (idx < s_idx)
22417a674200SNicolas Dichtel 				goto cont;
22427a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
22437a674200SNicolas Dichtel 			if (!in_dev)
22447a674200SNicolas Dichtel 				goto cont;
22457a674200SNicolas Dichtel 
22467a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
22477a674200SNicolas Dichtel 						      &in_dev->cnf,
22487a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2249addd383fSDavid Ahern 						      nlh->nlmsg_seq,
22507a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
22517a674200SNicolas Dichtel 						      NLM_F_MULTI,
2252136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
22537a674200SNicolas Dichtel 				rcu_read_unlock();
22547a674200SNicolas Dichtel 				goto done;
22557a674200SNicolas Dichtel 			}
22560465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
22577a674200SNicolas Dichtel cont:
22587a674200SNicolas Dichtel 			idx++;
22597a674200SNicolas Dichtel 		}
22607a674200SNicolas Dichtel 		rcu_read_unlock();
22617a674200SNicolas Dichtel 	}
22627a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
22637a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
22647a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
22657a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2266addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22677a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2268136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22697a674200SNicolas Dichtel 			goto done;
22707a674200SNicolas Dichtel 		else
22717a674200SNicolas Dichtel 			h++;
22727a674200SNicolas Dichtel 	}
22737a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
22747a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
22757a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
22767a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2277addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22787a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2279136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22807a674200SNicolas Dichtel 			goto done;
22817a674200SNicolas Dichtel 		else
22827a674200SNicolas Dichtel 			h++;
22837a674200SNicolas Dichtel 	}
22847a674200SNicolas Dichtel done:
22857a674200SNicolas Dichtel 	cb->args[0] = h;
22867a674200SNicolas Dichtel 	cb->args[1] = idx;
22877a674200SNicolas Dichtel 
22887a674200SNicolas Dichtel 	return skb->len;
22897a674200SNicolas Dichtel }
22907a674200SNicolas Dichtel 
22911da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
22921da177e4SLinus Torvalds 
2293c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
229431be3085SHerbert Xu {
229531be3085SHerbert Xu 	struct net_device *dev;
229631be3085SHerbert Xu 
229731be3085SHerbert Xu 	rcu_read_lock();
2298c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2299c6d14c84SEric Dumazet 		struct in_device *in_dev;
2300c6d14c84SEric Dumazet 
230131be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
230231be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23039355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2304c6d14c84SEric Dumazet 	}
230531be3085SHerbert Xu 	rcu_read_unlock();
230631be3085SHerbert Xu }
230731be3085SHerbert Xu 
2308c6d14c84SEric Dumazet /* called with RTNL locked */
2309c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
231068dd299bSPavel Emelyanov {
231168dd299bSPavel Emelyanov 	struct net_device *dev;
2312586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
231368dd299bSPavel Emelyanov 
2314586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23159355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23163b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23173b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2318edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2319edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23203b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23213b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2322edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2323edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
232468dd299bSPavel Emelyanov 
2325c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
232668dd299bSPavel Emelyanov 		struct in_device *in_dev;
2327fa17806cSEric Dumazet 
23280187bdfbSBen Hutchings 		if (on)
23290187bdfbSBen Hutchings 			dev_disable_lro(dev);
2330fa17806cSEric Dumazet 
2331fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2332edc9e748SNicolas Dichtel 		if (in_dev) {
233368dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23343b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23353b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2336edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2337edc9e748SNicolas Dichtel 		}
233868dd299bSPavel Emelyanov 	}
233968dd299bSPavel Emelyanov }
234068dd299bSPavel Emelyanov 
2341f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2342f085ff1cSstephen hemminger {
2343f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2344f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2345f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2346f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2347f085ff1cSstephen hemminger 	else {
2348f085ff1cSstephen hemminger 		struct in_device *idev
2349f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2350f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2351f085ff1cSstephen hemminger 	}
2352f085ff1cSstephen hemminger }
2353f085ff1cSstephen hemminger 
2354fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
23558d65af78SAlexey Dobriyan 			     void __user *buffer,
235631be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
235731be3085SHerbert Xu {
2358d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23598d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2360d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
236131be3085SHerbert Xu 
236231be3085SHerbert Xu 	if (write) {
236331be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2364c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
236531be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2366f085ff1cSstephen hemminger 		int ifindex;
236731be3085SHerbert Xu 
236831be3085SHerbert Xu 		set_bit(i, cnf->state);
236931be3085SHerbert Xu 
23709355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2371c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2372d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2373d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2374d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
23754ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2376f085ff1cSstephen hemminger 
23775cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
23785cbf777cSXin Long 		    new_value != old_value)
23795cbf777cSXin Long 			rt_cache_flush(net);
23805cbf777cSXin Long 
2381cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2382cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2383f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
23843b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23853b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2386cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2387cc535dfbSNicolas Dichtel 		}
2388f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2389f085ff1cSstephen hemminger 		    new_value != old_value) {
2390f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
23913b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23923b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2393f085ff1cSstephen hemminger 						    ifindex, cnf);
2394f085ff1cSstephen hemminger 		}
2395974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2396974d7af5SAndy Gospodarek 		    new_value != old_value) {
2397974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
23983b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23993b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2400974d7af5SAndy Gospodarek 						    ifindex, cnf);
2401974d7af5SAndy Gospodarek 		}
240231be3085SHerbert Xu 	}
240331be3085SHerbert Xu 
240431be3085SHerbert Xu 	return ret;
240531be3085SHerbert Xu }
240631be3085SHerbert Xu 
2407fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
24088d65af78SAlexey Dobriyan 				  void __user *buffer,
24091da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
24101da177e4SLinus Torvalds {
24111da177e4SLinus Torvalds 	int *valp = ctl->data;
24121da177e4SLinus Torvalds 	int val = *valp;
241388af182eSEric W. Biederman 	loff_t pos = *ppos;
24148d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24151da177e4SLinus Torvalds 
24161da177e4SLinus Torvalds 	if (write && *valp != val) {
2417c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2418c0ce9fb3SPavel Emelyanov 
24190187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
242088af182eSEric W. Biederman 			if (!rtnl_trylock()) {
242188af182eSEric W. Biederman 				/* Restore the original values before restarting */
242288af182eSEric W. Biederman 				*valp = val;
242388af182eSEric W. Biederman 				*ppos = pos;
24249b8adb5eSEric W. Biederman 				return restart_syscall();
242588af182eSEric W. Biederman 			}
24260187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2427c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2428edc9e748SNicolas Dichtel 			} else {
24290187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24300187bdfbSBen Hutchings 				struct in_device *idev =
24310187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2432edc9e748SNicolas Dichtel 				if (*valp)
24330187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24343b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2435edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2436edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2437edc9e748SNicolas Dichtel 							    cnf);
24380187bdfbSBen Hutchings 			}
24390187bdfbSBen Hutchings 			rtnl_unlock();
24404ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2441edc9e748SNicolas Dichtel 		} else
24423b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24433b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2444edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2445edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24460187bdfbSBen Hutchings 	}
24471da177e4SLinus Torvalds 
24481da177e4SLinus Torvalds 	return ret;
24491da177e4SLinus Torvalds }
24501da177e4SLinus Torvalds 
2451fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
24528d65af78SAlexey Dobriyan 				void __user *buffer,
24531da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
24541da177e4SLinus Torvalds {
24551da177e4SLinus Torvalds 	int *valp = ctl->data;
24561da177e4SLinus Torvalds 	int val = *valp;
24578d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
245876e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24591da177e4SLinus Torvalds 
24601da177e4SLinus Torvalds 	if (write && *valp != val)
24614ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
24621da177e4SLinus Torvalds 
24631da177e4SLinus Torvalds 	return ret;
24641da177e4SLinus Torvalds }
24651da177e4SLinus Torvalds 
2466f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
246742f811b8SHerbert Xu 	{ \
246842f811b8SHerbert Xu 		.procname	= name, \
246942f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
247002291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
247142f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
247242f811b8SHerbert Xu 		.mode		= mval, \
247342f811b8SHerbert Xu 		.proc_handler	= proc, \
247431be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
247542f811b8SHerbert Xu 	}
247642f811b8SHerbert Xu 
247742f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2478f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
247942f811b8SHerbert Xu 
248042f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2481f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
248242f811b8SHerbert Xu 
2483f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2484f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
248542f811b8SHerbert Xu 
248642f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2487f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
248842f811b8SHerbert Xu 
24891da177e4SLinus Torvalds static struct devinet_sysctl_table {
24901da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
249102291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
24921da177e4SLinus Torvalds } devinet_sysctl = {
24931da177e4SLinus Torvalds 	.devinet_vars = {
249442f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2495f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
249642f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
24975cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
249842f811b8SHerbert Xu 
249942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
250042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
250142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
250242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
250342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
250442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
250542f811b8SHerbert Xu 					"accept_source_route"),
25068153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
250728f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
250842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
250942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
251042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
251142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
251242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
251342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
251442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
251542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
251642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2517eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
251865324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25195c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25205c6fe01cSWilliam Manley 					"force_igmp_version"),
25212690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25222690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25232690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25242690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25250eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25260eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
252797daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
252897daf331SJohannes Berg 					"drop_gratuitous_arp"),
252942f811b8SHerbert Xu 
253042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
253142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
253242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
253342f811b8SHerbert Xu 					      "promote_secondaries"),
2534d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2535d0daebc3SThomas Graf 					      "route_localnet"),
253612b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
253712b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25381da177e4SLinus Torvalds 	},
25391da177e4SLinus Torvalds };
25401da177e4SLinus Torvalds 
2541ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
254229c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25431da177e4SLinus Torvalds {
25441da177e4SLinus Torvalds 	int i;
25459fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25468607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2547bfada697SPavel Emelyanov 
25489fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
25491da177e4SLinus Torvalds 	if (!t)
25509fa89642SPavel Emelyanov 		goto out;
25519fa89642SPavel Emelyanov 
25521da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
25531da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
255431be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2555c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25561da177e4SLinus Torvalds 	}
25571da177e4SLinus Torvalds 
25588607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
25591da177e4SLinus Torvalds 
25608607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
25611da177e4SLinus Torvalds 	if (!t->sysctl_header)
25628607ddb8SEric W. Biederman 		goto free;
25631da177e4SLinus Torvalds 
25641da177e4SLinus Torvalds 	p->sysctl = t;
256529c994e3SNicolas Dichtel 
25663b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
25673b022865SDavid Ahern 				    ifindex, p);
2568ea40b324SPavel Emelyanov 	return 0;
25691da177e4SLinus Torvalds 
25701da177e4SLinus Torvalds free:
25711da177e4SLinus Torvalds 	kfree(t);
25729fa89642SPavel Emelyanov out:
2573ea40b324SPavel Emelyanov 	return -ENOBUFS;
25741da177e4SLinus Torvalds }
25751da177e4SLinus Torvalds 
2576b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2577b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
257866f27a52SPavel Emelyanov {
257951602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
258066f27a52SPavel Emelyanov 
2581b5c9641dSDavid Ahern 	if (t) {
258251602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2583ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
25841da177e4SLinus Torvalds 		kfree(t);
25851da177e4SLinus Torvalds 	}
258651602b2aSPavel Emelyanov 
2587b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2588b5c9641dSDavid Ahern }
2589b5c9641dSDavid Ahern 
259020e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
259151602b2aSPavel Emelyanov {
259220e61da7SWANG Cong 	int err;
259320e61da7SWANG Cong 
259420e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
259520e61da7SWANG Cong 		return -EINVAL;
259620e61da7SWANG Cong 
259720e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
259820e61da7SWANG Cong 	if (err)
259920e61da7SWANG Cong 		return err;
260020e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
260129c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
260220e61da7SWANG Cong 	if (err)
260320e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
260420e61da7SWANG Cong 	return err;
260551602b2aSPavel Emelyanov }
260651602b2aSPavel Emelyanov 
260751602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
260851602b2aSPavel Emelyanov {
2609b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2610b5c9641dSDavid Ahern 
2611b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
261251602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26131da177e4SLinus Torvalds }
26141da177e4SLinus Torvalds 
261568dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
261668dd299bSPavel Emelyanov 	{
261768dd299bSPavel Emelyanov 		.procname	= "ip_forward",
261868dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
261902291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
262068dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
262168dd299bSPavel Emelyanov 		.mode		= 0644,
262268dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
262368dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2624c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
262568dd299bSPavel Emelyanov 	},
262668dd299bSPavel Emelyanov 	{ },
262768dd299bSPavel Emelyanov };
26282a75de0cSEric Dumazet #endif
262968dd299bSPavel Emelyanov 
2630752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2631752d14dcSPavel Emelyanov {
2632752d14dcSPavel Emelyanov 	int err;
2633752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26342a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2635856c395cSCong Wang 	struct ctl_table *tbl;
2636752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26372a75de0cSEric Dumazet #endif
2638752d14dcSPavel Emelyanov 
2639752d14dcSPavel Emelyanov 	err = -ENOMEM;
2640856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
264151456b29SIan Morris 	if (!all)
2642752d14dcSPavel Emelyanov 		goto err_alloc_all;
2643752d14dcSPavel Emelyanov 
2644856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
264551456b29SIan Morris 	if (!dflt)
2646752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2647752d14dcSPavel Emelyanov 
26482a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2649856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
265051456b29SIan Morris 	if (!tbl)
2651752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2652752d14dcSPavel Emelyanov 
265302291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2654752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2655752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26562a75de0cSEric Dumazet #endif
2657856c395cSCong Wang 
2658a154d5d8SArnd Bergmann 	if ((!IS_ENABLED(CONFIG_SYSCTL) ||
2659a154d5d8SArnd Bergmann 	     sysctl_devconf_inherit_init_net != 2) &&
2660a154d5d8SArnd Bergmann 	    !net_eq(net, &init_net)) {
2661856c395cSCong Wang 		memcpy(all, init_net.ipv4.devconf_all, sizeof(ipv4_devconf));
2662856c395cSCong Wang 		memcpy(dflt, init_net.ipv4.devconf_dflt, sizeof(ipv4_devconf_dflt));
2663752d14dcSPavel Emelyanov 	}
2664752d14dcSPavel Emelyanov 
2665752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
266629c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2667752d14dcSPavel Emelyanov 	if (err < 0)
2668752d14dcSPavel Emelyanov 		goto err_reg_all;
2669752d14dcSPavel Emelyanov 
267029c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
267129c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2672752d14dcSPavel Emelyanov 	if (err < 0)
2673752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2674752d14dcSPavel Emelyanov 
2675752d14dcSPavel Emelyanov 	err = -ENOMEM;
26768607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
267751456b29SIan Morris 	if (!forw_hdr)
2678752d14dcSPavel Emelyanov 		goto err_reg_ctl;
26792a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2680752d14dcSPavel Emelyanov #endif
2681752d14dcSPavel Emelyanov 
2682752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2683752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2684752d14dcSPavel Emelyanov 	return 0;
2685752d14dcSPavel Emelyanov 
2686752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2687752d14dcSPavel Emelyanov err_reg_ctl:
2688b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2689752d14dcSPavel Emelyanov err_reg_dflt:
2690b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2691752d14dcSPavel Emelyanov err_reg_all:
2692752d14dcSPavel Emelyanov 	kfree(tbl);
2693752d14dcSPavel Emelyanov err_alloc_ctl:
26942a75de0cSEric Dumazet #endif
2695752d14dcSPavel Emelyanov 	kfree(dflt);
2696752d14dcSPavel Emelyanov err_alloc_dflt:
2697752d14dcSPavel Emelyanov 	kfree(all);
2698752d14dcSPavel Emelyanov err_alloc_all:
2699752d14dcSPavel Emelyanov 	return err;
2700752d14dcSPavel Emelyanov }
2701752d14dcSPavel Emelyanov 
2702752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2703752d14dcSPavel Emelyanov {
27042a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2705752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2706752d14dcSPavel Emelyanov 
2707752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2708752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2709b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2710b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2711b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2712b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2713752d14dcSPavel Emelyanov 	kfree(tbl);
27142a75de0cSEric Dumazet #endif
2715752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2716752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2717752d14dcSPavel Emelyanov }
2718752d14dcSPavel Emelyanov 
2719752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2720752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2721752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2722752d14dcSPavel Emelyanov };
2723752d14dcSPavel Emelyanov 
2724207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
27259f0f7272SThomas Graf 	.family		  = AF_INET,
27269f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
27279f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2728cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2729cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
27309f0f7272SThomas Graf };
27319f0f7272SThomas Graf 
27321da177e4SLinus Torvalds void __init devinet_init(void)
27331da177e4SLinus Torvalds {
2734fd23c3b3SDavid S. Miller 	int i;
2735fd23c3b3SDavid S. Miller 
2736fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2737fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2738fd23c3b3SDavid S. Miller 
2739752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2740752d14dcSPavel Emelyanov 
27411da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
27421da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
274363f3444fSThomas Graf 
2744906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
27455c766d64SJiri Pirko 
27469f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
27479f0f7272SThomas Graf 
2748b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2749b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2750b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
27519e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2752b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
27531da177e4SLinus Torvalds }
2754