xref: /openbmc/linux/net/ipv4/devinet.c (revision 856c395c)
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);
1971da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1981da177e4SLinus Torvalds 			 int destroy);
1991da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20020e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20251602b2aSPavel Emelyanov #else
20320e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20451602b2aSPavel Emelyanov {
20520e61da7SWANG Cong 	return 0;
20651602b2aSPavel Emelyanov }
20740384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
20851602b2aSPavel Emelyanov {
20951602b2aSPavel Emelyanov }
2101da177e4SLinus Torvalds #endif
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds /* Locks all the inet devices. */
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2151da177e4SLinus Torvalds {
21693adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2201da177e4SLinus Torvalds {
2211da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2221da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2231da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2241da177e4SLinus Torvalds 	kfree(ifa);
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
22740384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2351da177e4SLinus Torvalds 
236547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
237547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
238e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2391da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
24091df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2411da177e4SLinus Torvalds #endif
2421da177e4SLinus Torvalds 	dev_put(dev);
2431da177e4SLinus Torvalds 	if (!idev->dead)
2449f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2459f9354b9SEric Dumazet 	else
2461da177e4SLinus Torvalds 		kfree(idev);
2471da177e4SLinus Torvalds }
2489f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2491da177e4SLinus Torvalds 
25071e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2511da177e4SLinus Torvalds {
2521da177e4SLinus Torvalds 	struct in_device *in_dev;
25320e61da7SWANG Cong 	int err = -ENOMEM;
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 	ASSERT_RTNL();
2561da177e4SLinus Torvalds 
2570da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2581da177e4SLinus Torvalds 	if (!in_dev)
2591da177e4SLinus Torvalds 		goto out;
260c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2619355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2621da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2631da177e4SLinus Torvalds 	in_dev->dev = dev;
2649f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2659f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2661da177e4SLinus Torvalds 		goto out_kfree;
2670187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2680187bdfbSBen Hutchings 		dev_disable_lro(dev);
2691da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2701da177e4SLinus Torvalds 	dev_hold(dev);
27130c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2727658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2731da177e4SLinus Torvalds 
27420e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
27520e61da7SWANG Cong 	if (err) {
27620e61da7SWANG Cong 		in_dev->dead = 1;
27720e61da7SWANG Cong 		in_dev_put(in_dev);
27820e61da7SWANG Cong 		in_dev = NULL;
27920e61da7SWANG Cong 		goto out;
28020e61da7SWANG Cong 	}
2811da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2821da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2831da177e4SLinus Torvalds 		ip_mc_up(in_dev);
284483479ecSJarek Poplawski 
28530c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
286cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
287483479ecSJarek Poplawski out:
28820e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2891da177e4SLinus Torvalds out_kfree:
2901da177e4SLinus Torvalds 	kfree(in_dev);
2911da177e4SLinus Torvalds 	in_dev = NULL;
2921da177e4SLinus Torvalds 	goto out;
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2961da177e4SLinus Torvalds {
2971da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2981da177e4SLinus Torvalds 	in_dev_put(idev);
2991da177e4SLinus Torvalds }
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
3041da177e4SLinus Torvalds 	struct net_device *dev;
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	ASSERT_RTNL();
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	dev = in_dev->dev;
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds 	in_dev->dead = 1;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
3151da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3161da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3171da177e4SLinus Torvalds 	}
3181da177e4SLinus Torvalds 
319a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3201da177e4SLinus Torvalds 
32151602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3221da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3231da177e4SLinus Torvalds 	arp_ifdown(dev);
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
328ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3291da177e4SLinus Torvalds {
3301da177e4SLinus Torvalds 	rcu_read_lock();
3311da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3321da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3331da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3341da177e4SLinus Torvalds 				rcu_read_unlock();
3351da177e4SLinus Torvalds 				return 1;
3361da177e4SLinus Torvalds 			}
3371da177e4SLinus Torvalds 		}
3381da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3391da177e4SLinus Torvalds 	rcu_read_unlock();
3401da177e4SLinus Torvalds 	return 0;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
343d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
34415e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3451da177e4SLinus Torvalds {
3468f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3470ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3480ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3490ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3500ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	ASSERT_RTNL();
3531da177e4SLinus Torvalds 
354fbd40ea0SDavid S. Miller 	if (in_dev->dead)
355fbd40ea0SDavid S. Miller 		goto no_promotions;
356fbd40ea0SDavid S. Miller 
3578f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3588f937c60SHarald Welte 	 * unless alias promotion is set
3598f937c60SHarald Welte 	 **/
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3621da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3650ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3660ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3670ff60a45SJamal Hadi Salim 				last_prim = ifa;
3680ff60a45SJamal Hadi Salim 
3691da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3701da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3711da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3721da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3730ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3741da177e4SLinus Torvalds 				continue;
3751da177e4SLinus Torvalds 			}
3761da177e4SLinus Torvalds 
3770ff60a45SJamal Hadi Salim 			if (!do_promote) {
378fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3791da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3801da177e4SLinus Torvalds 
38115e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
382e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
383e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3841da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3858f937c60SHarald Welte 			} else {
3868f937c60SHarald Welte 				promote = ifa;
3878f937c60SHarald Welte 				break;
3888f937c60SHarald Welte 			}
3891da177e4SLinus Torvalds 		}
3901da177e4SLinus Torvalds 	}
3911da177e4SLinus Torvalds 
3922d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3932d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3942d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3952d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3962d230e2bSJulian Anastasov 	 */
3972d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3982d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3992d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4002d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4012d230e2bSJulian Anastasov 	}
4022d230e2bSJulian Anastasov 
403fbd40ea0SDavid S. Miller no_promotions:
4041da177e4SLinus Torvalds 	/* 2. Unlink it */
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
407fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4121da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4131da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4141da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4151da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4161da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4171da177e4SLinus Torvalds 	   So that, this order is correct.
4181da177e4SLinus Torvalds 	 */
41915e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
420e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4210ff60a45SJamal Hadi Salim 
4220ff60a45SJamal Hadi Salim 	if (promote) {
42304024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
4240ff60a45SJamal Hadi Salim 
4250ff60a45SJamal Hadi Salim 		if (prev_prom) {
4260ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
4270ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
4280ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4290ff60a45SJamal Hadi Salim 		}
4300ff60a45SJamal Hadi Salim 
4310ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
43215e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
433e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
434e041c683SAlan Stern 				NETDEV_UP, promote);
43504024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4360ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4370ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4380ff60a45SJamal Hadi Salim 					continue;
4390ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4400ff60a45SJamal Hadi Salim 		}
4410ff60a45SJamal Hadi Salim 
4420ff60a45SJamal Hadi Salim 	}
4436363097cSHerbert Xu 	if (destroy)
4441da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
447d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
448d6062cbbSThomas Graf 			 int destroy)
449d6062cbbSThomas Graf {
450d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
451d6062cbbSThomas Graf }
452d6062cbbSThomas Graf 
4535c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4545c766d64SJiri Pirko 
4555c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4565c766d64SJiri Pirko 
457d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
458de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4591da177e4SLinus Torvalds {
4601da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4611da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4623ad7d246SKrister Johansen 	struct in_validator_info ivi;
4633ad7d246SKrister Johansen 	int ret;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	ASSERT_RTNL();
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4681da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4691da177e4SLinus Torvalds 		return 0;
4701da177e4SLinus Torvalds 	}
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4731da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4761da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4771da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4781da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4791da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4801da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4811da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4821da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4831da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4841da177e4SLinus Torvalds 				return -EEXIST;
4851da177e4SLinus Torvalds 			}
4861da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4871da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4881da177e4SLinus Torvalds 				return -EINVAL;
4891da177e4SLinus Torvalds 			}
4901da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4911da177e4SLinus Torvalds 		}
4921da177e4SLinus Torvalds 	}
4931da177e4SLinus Torvalds 
4943ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
4953ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
4963ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
4973ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
4983ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
4993ad7d246SKrister Johansen 	 * not the flags.
5003ad7d246SKrister Johansen 	 */
5013ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5023ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
503de95e047SDavid Ahern 	ivi.extack = extack;
5043ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5053ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5063ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5073ad7d246SKrister Johansen 	if (ret) {
5083ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5093ad7d246SKrister Johansen 		return ret;
5103ad7d246SKrister Johansen 	}
5113ad7d246SKrister Johansen 
5121da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
51363862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
5141da177e4SLinus Torvalds 		ifap = last_primary;
5151da177e4SLinus Torvalds 	}
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
5181da177e4SLinus Torvalds 	*ifap = ifa;
5191da177e4SLinus Torvalds 
520fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
521fd23c3b3SDavid S. Miller 
5225c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
523906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5245c766d64SJiri Pirko 
5251da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5261da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5271da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
52815e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
529e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds 	return 0;
5321da177e4SLinus Torvalds }
5331da177e4SLinus Torvalds 
534d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
535d6062cbbSThomas Graf {
536de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
537d6062cbbSThomas Graf }
538d6062cbbSThomas Graf 
5391da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5401da177e4SLinus Torvalds {
541e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 	ASSERT_RTNL();
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 	if (!in_dev) {
5461da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5471da177e4SLinus Torvalds 		return -ENOBUFS;
5481da177e4SLinus Torvalds 	}
54971e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5501d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5511da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
552547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5531da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5541da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5551da177e4SLinus Torvalds 	}
556f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5571da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5581da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds 
5618723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5628723e1b4SEric Dumazet  * We dont take a reference on found in_device
5638723e1b4SEric Dumazet  */
5647fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5651da177e4SLinus Torvalds {
5661da177e4SLinus Torvalds 	struct net_device *dev;
5671da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
568c148fc2eSEric Dumazet 
569c148fc2eSEric Dumazet 	rcu_read_lock();
570c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5711da177e4SLinus Torvalds 	if (dev)
5728723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
573c148fc2eSEric Dumazet 	rcu_read_unlock();
5741da177e4SLinus Torvalds 	return in_dev;
5751da177e4SLinus Torvalds }
5769f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5791da177e4SLinus Torvalds 
58060cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
58160cad5daSAl Viro 				    __be32 mask)
5821da177e4SLinus Torvalds {
5831da177e4SLinus Torvalds 	ASSERT_RTNL();
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5861da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5871da177e4SLinus Torvalds 			return ifa;
5881da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5891da177e4SLinus Torvalds 	return NULL;
5901da177e4SLinus Torvalds }
5911da177e4SLinus Torvalds 
59293a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
59393a714d6SMadhu Challa {
59493a714d6SMadhu Challa 	struct ip_mreqn mreq = {
59593a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
59693a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
59793a714d6SMadhu Challa 	};
59893a714d6SMadhu Challa 	int ret;
59993a714d6SMadhu Challa 
60093a714d6SMadhu Challa 	ASSERT_RTNL();
60193a714d6SMadhu Challa 
60293a714d6SMadhu Challa 	lock_sock(sk);
60393a714d6SMadhu Challa 	if (join)
60454ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
60593a714d6SMadhu Challa 	else
60654ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
60793a714d6SMadhu Challa 	release_sock(sk);
60893a714d6SMadhu Challa 
60993a714d6SMadhu Challa 	return ret;
61093a714d6SMadhu Challa }
61193a714d6SMadhu Challa 
612c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
613c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6141da177e4SLinus Torvalds {
6153b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
616dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6171da177e4SLinus Torvalds 	struct in_device *in_dev;
618dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6191da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
620dfdd5fd4SThomas Graf 	int err = -EINVAL;
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 	ASSERT_RTNL();
6231da177e4SLinus Torvalds 
624fceb6435SJohannes Berg 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy,
625c21ef3e3SDavid Ahern 			  extack);
626dfdd5fd4SThomas Graf 	if (err < 0)
627dfdd5fd4SThomas Graf 		goto errout;
628dfdd5fd4SThomas Graf 
629dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6307fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
63151456b29SIan Morris 	if (!in_dev) {
632dfdd5fd4SThomas Graf 		err = -ENODEV;
633dfdd5fd4SThomas Graf 		goto errout;
634dfdd5fd4SThomas Graf 	}
635dfdd5fd4SThomas Graf 
6361da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
6371da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
638dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
63967b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6401da177e4SLinus Torvalds 			continue;
641dfdd5fd4SThomas Graf 
642dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
643dfdd5fd4SThomas Graf 			continue;
644dfdd5fd4SThomas Graf 
645dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
646dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
64767b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
648dfdd5fd4SThomas Graf 			continue;
649dfdd5fd4SThomas Graf 
65093a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
65193a714d6SMadhu Challa 			ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
65215e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6531da177e4SLinus Torvalds 		return 0;
6541da177e4SLinus Torvalds 	}
655dfdd5fd4SThomas Graf 
656dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
657dfdd5fd4SThomas Graf errout:
658dfdd5fd4SThomas Graf 	return err;
6591da177e4SLinus Torvalds }
6601da177e4SLinus Torvalds 
6615c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6625c766d64SJiri Pirko 
6635c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6645c766d64SJiri Pirko {
6655c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6665c766d64SJiri Pirko 	struct in_ifaddr *ifa;
667c988d1e8SJiri Pirko 	struct hlist_node *n;
6685c766d64SJiri Pirko 	int i;
6695c766d64SJiri Pirko 
6705c766d64SJiri Pirko 	now = jiffies;
6715c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6725c766d64SJiri Pirko 
6735c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
674c988d1e8SJiri Pirko 		bool change_needed = false;
675c988d1e8SJiri Pirko 
676c988d1e8SJiri Pirko 		rcu_read_lock();
677b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6785c766d64SJiri Pirko 			unsigned long age;
6795c766d64SJiri Pirko 
6805c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6815c766d64SJiri Pirko 				continue;
6825c766d64SJiri Pirko 
6835c766d64SJiri Pirko 			/* We try to batch several events at once. */
6845c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6855c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6865c766d64SJiri Pirko 
6875c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6885c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
689c988d1e8SJiri Pirko 				change_needed = true;
690c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
691c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
692c988d1e8SJiri Pirko 				continue;
693c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
694c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
695c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
696c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
697c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
698c988d1e8SJiri Pirko 
699c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
700c988d1e8SJiri Pirko 					change_needed = true;
701c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
702c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
703c988d1e8SJiri Pirko 					       next)) {
704c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
705c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
706c988d1e8SJiri Pirko 			}
707c988d1e8SJiri Pirko 		}
708c988d1e8SJiri Pirko 		rcu_read_unlock();
709c988d1e8SJiri Pirko 		if (!change_needed)
710c988d1e8SJiri Pirko 			continue;
711c988d1e8SJiri Pirko 		rtnl_lock();
712c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
713c988d1e8SJiri Pirko 			unsigned long age;
714c988d1e8SJiri Pirko 
715c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
716c988d1e8SJiri Pirko 				continue;
717c988d1e8SJiri Pirko 
718c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
719c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
720c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
721c988d1e8SJiri Pirko 
722c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
723c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7245c766d64SJiri Pirko 				struct in_ifaddr **ifap;
7255c766d64SJiri Pirko 
7265c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
727c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
728c988d1e8SJiri Pirko 					if (*ifap == ifa) {
7295c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7305c766d64SJiri Pirko 							     ifap, 1);
731c988d1e8SJiri Pirko 						break;
7325c766d64SJiri Pirko 					}
733c988d1e8SJiri Pirko 				}
734c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
735c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
736c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
737c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7385c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7395c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7405c766d64SJiri Pirko 			}
7415c766d64SJiri Pirko 		}
742c988d1e8SJiri Pirko 		rtnl_unlock();
7435c766d64SJiri Pirko 	}
7445c766d64SJiri Pirko 
7455c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7465c766d64SJiri Pirko 	next_sched = next;
7475c766d64SJiri Pirko 
7485c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7495c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7505c766d64SJiri Pirko 		next_sched = next_sec;
7515c766d64SJiri Pirko 
7525c766d64SJiri Pirko 	now = jiffies;
7535c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7545c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7555c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7565c766d64SJiri Pirko 
757906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
758906e073fSviresh kumar 			next_sched - now);
7595c766d64SJiri Pirko }
7605c766d64SJiri Pirko 
7615c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7625c766d64SJiri Pirko 			     __u32 prefered_lft)
7635c766d64SJiri Pirko {
7645c766d64SJiri Pirko 	unsigned long timeout;
7655c766d64SJiri Pirko 
7665c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
7675c766d64SJiri Pirko 
7685c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
7695c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
7705c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7715c766d64SJiri Pirko 	else
7725c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
7735c766d64SJiri Pirko 
7745c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
7755c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
7765c766d64SJiri Pirko 		if (timeout == 0)
7775c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7785c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7795c766d64SJiri Pirko 	}
7805c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7815c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7825c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7835c766d64SJiri Pirko }
7845c766d64SJiri Pirko 
7855c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
786dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
787dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
7881da177e4SLinus Torvalds {
7895c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7905c753978SThomas Graf 	struct in_ifaddr *ifa;
7915c753978SThomas Graf 	struct ifaddrmsg *ifm;
7921da177e4SLinus Torvalds 	struct net_device *dev;
7931da177e4SLinus Torvalds 	struct in_device *in_dev;
7947b218574SDenis V. Lunev 	int err;
7951da177e4SLinus Torvalds 
796fceb6435SJohannes Berg 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy,
797dac9c979SDavid Ahern 			  extack);
7985c753978SThomas Graf 	if (err < 0)
7995c753978SThomas Graf 		goto errout;
8001da177e4SLinus Torvalds 
8015c753978SThomas Graf 	ifm = nlmsg_data(nlh);
802c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
80351456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8045c753978SThomas Graf 		goto errout;
8051da177e4SLinus Torvalds 
8064b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8075c753978SThomas Graf 	err = -ENODEV;
80851456b29SIan Morris 	if (!dev)
8095c753978SThomas Graf 		goto errout;
8101da177e4SLinus Torvalds 
8115c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8125c753978SThomas Graf 	err = -ENOBUFS;
81351456b29SIan Morris 	if (!in_dev)
8145c753978SThomas Graf 		goto errout;
81571e27da9SHerbert Xu 
8165c753978SThomas Graf 	ifa = inet_alloc_ifa();
81751456b29SIan Morris 	if (!ifa)
8185c753978SThomas Graf 		/*
8195c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8205c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8215c753978SThomas Graf 		 */
8225c753978SThomas Graf 		goto errout;
8235c753978SThomas Graf 
824a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8251d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8265c753978SThomas Graf 	in_dev_hold(in_dev);
8275c753978SThomas Graf 
82851456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8295c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8305c753978SThomas Graf 
831fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8321da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8331da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
834ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
835ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8361da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8371da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8385c753978SThomas Graf 
83967b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
84067b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8415c753978SThomas Graf 
8425c753978SThomas Graf 	if (tb[IFA_BROADCAST])
84367b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8445c753978SThomas Graf 
8455c753978SThomas Graf 	if (tb[IFA_LABEL])
8465c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8471da177e4SLinus Torvalds 	else
8481da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8491da177e4SLinus Torvalds 
850af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
851af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
852af4d768aSDavid Ahern 
8535c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8545c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8555c766d64SJiri Pirko 
8565c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8575c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8585c766d64SJiri Pirko 			err = -EINVAL;
859446266b0SDaniel Borkmann 			goto errout_free;
8605c766d64SJiri Pirko 		}
8615c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8625c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8635c766d64SJiri Pirko 	}
8645c766d64SJiri Pirko 
8655c753978SThomas Graf 	return ifa;
8665c753978SThomas Graf 
867446266b0SDaniel Borkmann errout_free:
868446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8695c753978SThomas Graf errout:
8705c753978SThomas Graf 	return ERR_PTR(err);
8715c753978SThomas Graf }
8725c753978SThomas Graf 
8735c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
8745c766d64SJiri Pirko {
8755c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8765c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8775c766d64SJiri Pirko 
8785c766d64SJiri Pirko 	if (!ifa->ifa_local)
8795c766d64SJiri Pirko 		return NULL;
8805c766d64SJiri Pirko 
8815c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8825c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8835c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8845c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8855c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8865c766d64SJiri Pirko 			return ifa1;
8875c766d64SJiri Pirko 	}
8885c766d64SJiri Pirko 	return NULL;
8895c766d64SJiri Pirko }
8905c766d64SJiri Pirko 
891c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
892c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
8935c753978SThomas Graf {
8943b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8955c753978SThomas Graf 	struct in_ifaddr *ifa;
8965c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8975c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8985c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8995c753978SThomas Graf 
9005c753978SThomas Graf 	ASSERT_RTNL();
9015c753978SThomas Graf 
902dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9035c753978SThomas Graf 	if (IS_ERR(ifa))
9045c753978SThomas Graf 		return PTR_ERR(ifa);
9055c753978SThomas Graf 
9065c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9075c766d64SJiri Pirko 	if (!ifa_existing) {
9085c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
909614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9105c766d64SJiri Pirko 		 */
9115c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
91293a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
91393a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
91493a714d6SMadhu Challa 					       true, ifa);
91593a714d6SMadhu Challa 
91693a714d6SMadhu Challa 			if (ret < 0) {
91793a714d6SMadhu Challa 				inet_free_ifa(ifa);
91893a714d6SMadhu Challa 				return ret;
91993a714d6SMadhu Challa 			}
92093a714d6SMadhu Challa 		}
921de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
922de95e047SDavid Ahern 					 extack);
9235c766d64SJiri Pirko 	} else {
924af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
925af4d768aSDavid Ahern 
9265c766d64SJiri Pirko 		inet_free_ifa(ifa);
9275c766d64SJiri Pirko 
9285c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9295c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9305c766d64SJiri Pirko 			return -EEXIST;
93134e2ed34SJiri Pirko 		ifa = ifa_existing;
932af4d768aSDavid Ahern 
933af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
934af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
935af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
936af4d768aSDavid Ahern 		}
937af4d768aSDavid Ahern 
93834e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
93905a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
940906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
941906e073fSviresh kumar 				&check_lifetime_work, 0);
94234e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9435c766d64SJiri Pirko 	}
9445c766d64SJiri Pirko 	return 0;
9451da177e4SLinus Torvalds }
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds /*
9481da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9491da177e4SLinus Torvalds  */
9501da177e4SLinus Torvalds 
95140384999SEric Dumazet static int inet_abc_len(__be32 addr)
9521da177e4SLinus Torvalds {
9531da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9541da177e4SLinus Torvalds 
95565cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
9561da177e4SLinus Torvalds 		rc = 0;
9571da177e4SLinus Torvalds 	else {
958714e85beSAl Viro 		__u32 haddr = ntohl(addr);
959714e85beSAl Viro 		if (IN_CLASSA(haddr))
9601da177e4SLinus Torvalds 			rc = 8;
961714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9621da177e4SLinus Torvalds 			rc = 16;
963714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9641da177e4SLinus Torvalds 			rc = 24;
96565cab850SDave Taht 		else if (IN_CLASSE(haddr))
96665cab850SDave Taht 			rc = 32;
9671da177e4SLinus Torvalds 	}
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds 	return rc;
9701da177e4SLinus Torvalds }
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 
97303aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
9741da177e4SLinus Torvalds {
9751da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
97603aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
9771da177e4SLinus Torvalds 	struct in_device *in_dev;
9781da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
9791da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
9801da177e4SLinus Torvalds 	struct net_device *dev;
9811da177e4SLinus Torvalds 	char *colon;
9821da177e4SLinus Torvalds 	int ret = -EFAULT;
9831da177e4SLinus Torvalds 	int tryaddrmatch = 0;
9841da177e4SLinus Torvalds 
98503aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
9861da177e4SLinus Torvalds 
9871da177e4SLinus Torvalds 	/* save original address for comparison */
9881da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9891da177e4SLinus Torvalds 
99003aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
9911da177e4SLinus Torvalds 	if (colon)
9921da177e4SLinus Torvalds 		*colon = 0;
9931da177e4SLinus Torvalds 
99403aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds 	switch (cmd) {
9971da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9981da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9991da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10001da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10011da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10021da177e4SLinus Torvalds 		   so that we do not impose a lock.
10031da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10041da177e4SLinus Torvalds 		 */
10051da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10061da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10071da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10081da177e4SLinus Torvalds 		break;
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1011bf5b30b8SZhao Hongjiang 		ret = -EPERM;
101252e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10131da177e4SLinus Torvalds 			goto out;
10141da177e4SLinus Torvalds 		break;
10151da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10161da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10171da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10181da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1019bf5b30b8SZhao Hongjiang 		ret = -EPERM;
102052e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10211da177e4SLinus Torvalds 			goto out;
10221da177e4SLinus Torvalds 		ret = -EINVAL;
10231da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10241da177e4SLinus Torvalds 			goto out;
10251da177e4SLinus Torvalds 		break;
10261da177e4SLinus Torvalds 	default:
10271da177e4SLinus Torvalds 		ret = -EINVAL;
10281da177e4SLinus Torvalds 		goto out;
10291da177e4SLinus Torvalds 	}
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds 	rtnl_lock();
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 	ret = -ENODEV;
103403aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10359f9354b9SEric Dumazet 	if (!dev)
10361da177e4SLinus Torvalds 		goto done;
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds 	if (colon)
10391da177e4SLinus Torvalds 		*colon = ':';
10401da177e4SLinus Torvalds 
10419f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10429f9354b9SEric Dumazet 	if (in_dev) {
10431da177e4SLinus Torvalds 		if (tryaddrmatch) {
10441da177e4SLinus Torvalds 			/* Matthias Andree */
10451da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10461da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10471da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10481da177e4SLinus Torvalds 			   This is checked above. */
10491da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10501da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
105103aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10521da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10536c91afe1SDavid S. Miller 							ifa->ifa_local) {
10541da177e4SLinus Torvalds 					break; /* found */
10551da177e4SLinus Torvalds 				}
10561da177e4SLinus Torvalds 			}
10571da177e4SLinus Torvalds 		}
10581da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10591da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10601da177e4SLinus Torvalds 		   comparing just the label */
10611da177e4SLinus Torvalds 		if (!ifa) {
10621da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10631da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
106403aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
10651da177e4SLinus Torvalds 					break;
10661da177e4SLinus Torvalds 		}
10671da177e4SLinus Torvalds 	}
10681da177e4SLinus Torvalds 
10691da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10701da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
10711da177e4SLinus Torvalds 		goto done;
10721da177e4SLinus Torvalds 
10731da177e4SLinus Torvalds 	switch (cmd) {
10741da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
107530e948a3STonghao Zhang 		ret = 0;
10761da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
107703aef17bSAl Viro 		break;
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
108030e948a3STonghao Zhang 		ret = 0;
10811da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
108203aef17bSAl Viro 		break;
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
108530e948a3STonghao Zhang 		ret = 0;
10861da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
108703aef17bSAl Viro 		break;
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
109030e948a3STonghao Zhang 		ret = 0;
10911da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
109203aef17bSAl Viro 		break;
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10951da177e4SLinus Torvalds 		if (colon) {
10961da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
10971da177e4SLinus Torvalds 			if (!ifa)
10981da177e4SLinus Torvalds 				break;
10991da177e4SLinus Torvalds 			ret = 0;
110003aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11011da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11021da177e4SLinus Torvalds 			break;
11031da177e4SLinus Torvalds 		}
1104567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11051da177e4SLinus Torvalds 		break;
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11081da177e4SLinus Torvalds 		ret = -EINVAL;
11091da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11101da177e4SLinus Torvalds 			break;
11111da177e4SLinus Torvalds 
11121da177e4SLinus Torvalds 		if (!ifa) {
11131da177e4SLinus Torvalds 			ret = -ENOBUFS;
11149f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11159f9354b9SEric Dumazet 			if (!ifa)
11161da177e4SLinus Torvalds 				break;
1117c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11181da177e4SLinus Torvalds 			if (colon)
111903aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11201da177e4SLinus Torvalds 			else
11211da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11221da177e4SLinus Torvalds 		} else {
11231da177e4SLinus Torvalds 			ret = 0;
11241da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11251da177e4SLinus Torvalds 				break;
11261da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11271da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1128148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11291da177e4SLinus Torvalds 		}
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11321da177e4SLinus Torvalds 
11331da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11341da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11351da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11361da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11371da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11381da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11391da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11401da177e4SLinus Torvalds 		} else {
11411da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11421da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11431da177e4SLinus Torvalds 		}
11445c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11451da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11461da177e4SLinus Torvalds 		break;
11471da177e4SLinus Torvalds 
11481da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11491da177e4SLinus Torvalds 		ret = 0;
11501da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11511da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11521da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11531da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11541da177e4SLinus Torvalds 		}
11551da177e4SLinus Torvalds 		break;
11561da177e4SLinus Torvalds 
11571da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11581da177e4SLinus Torvalds 		ret = 0;
11591da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11601da177e4SLinus Torvalds 			break;
11611da177e4SLinus Torvalds 		ret = -EINVAL;
11621da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11631da177e4SLinus Torvalds 			break;
11641da177e4SLinus Torvalds 		ret = 0;
11651da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11661da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11671da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11681da177e4SLinus Torvalds 		break;
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds 		/*
11731da177e4SLinus Torvalds 		 *	The mask we set must be legal.
11741da177e4SLinus Torvalds 		 */
11751da177e4SLinus Torvalds 		ret = -EINVAL;
11761da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
11771da177e4SLinus Torvalds 			break;
11781da177e4SLinus Torvalds 		ret = 0;
11791da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1180a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
11811da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11821da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
11831da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
11841da177e4SLinus Torvalds 
11851da177e4SLinus Torvalds 			/* See if current broadcast address matches
11861da177e4SLinus Torvalds 			 * with current netmask, then recalculate
11871da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
11881da177e4SLinus Torvalds 			 * funny address, so don't touch it since
11891da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
11901da177e4SLinus Torvalds 			 */
11911da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11921da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11931da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1194dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11951da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
11961da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
11971da177e4SLinus Torvalds 			}
11981da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11991da177e4SLinus Torvalds 		}
12001da177e4SLinus Torvalds 		break;
12011da177e4SLinus Torvalds 	}
12021da177e4SLinus Torvalds done:
12031da177e4SLinus Torvalds 	rtnl_unlock();
12041da177e4SLinus Torvalds out:
12051da177e4SLinus Torvalds 	return ret;
12061da177e4SLinus Torvalds }
12071da177e4SLinus Torvalds 
120836fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12091da177e4SLinus Torvalds {
1210e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
12111da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12121da177e4SLinus Torvalds 	struct ifreq ifr;
12131da177e4SLinus Torvalds 	int done = 0;
12141da177e4SLinus Torvalds 
121536fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
121636fd633eSAl Viro 		goto out;
121736fd633eSAl Viro 
12189f9354b9SEric Dumazet 	if (!in_dev)
12191da177e4SLinus Torvalds 		goto out;
12201da177e4SLinus Torvalds 
12219f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12221da177e4SLinus Torvalds 		if (!buf) {
122336fd633eSAl Viro 			done += size;
12241da177e4SLinus Torvalds 			continue;
12251da177e4SLinus Torvalds 		}
122636fd633eSAl Viro 		if (len < size)
12271da177e4SLinus Torvalds 			break;
12281da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12291da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12301da177e4SLinus Torvalds 
12311da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12321da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12331da177e4SLinus Torvalds 								ifa->ifa_local;
12341da177e4SLinus Torvalds 
123536fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12361da177e4SLinus Torvalds 			done = -EFAULT;
12371da177e4SLinus Torvalds 			break;
12381da177e4SLinus Torvalds 		}
123936fd633eSAl Viro 		len  -= size;
124036fd633eSAl Viro 		done += size;
12411da177e4SLinus Torvalds 	}
12421da177e4SLinus Torvalds out:
12431da177e4SLinus Torvalds 	return done;
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds 
12468b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12478b57fd1eSGao Feng 				 int scope)
12488b57fd1eSGao Feng {
12498b57fd1eSGao Feng 	for_primary_ifa(in_dev) {
12508b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12518b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12528b57fd1eSGao Feng 			return ifa->ifa_local;
12538b57fd1eSGao Feng 	} endfor_ifa(in_dev);
12548b57fd1eSGao Feng 
12558b57fd1eSGao Feng 	return 0;
12568b57fd1eSGao Feng }
12578b57fd1eSGao Feng 
1258a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
12591da177e4SLinus Torvalds {
1260a61ced5dSAl Viro 	__be32 addr = 0;
12611da177e4SLinus Torvalds 	struct in_device *in_dev;
1262c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12633f2fb9a8SDavid Ahern 	int master_idx;
12641da177e4SLinus Torvalds 
12651da177e4SLinus Torvalds 	rcu_read_lock();
1266e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
12671da177e4SLinus Torvalds 	if (!in_dev)
12681da177e4SLinus Torvalds 		goto no_in_dev;
12691da177e4SLinus Torvalds 
12701da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
12711da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
12721da177e4SLinus Torvalds 			continue;
12731da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
12741da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12751da177e4SLinus Torvalds 			break;
12761da177e4SLinus Torvalds 		}
12771da177e4SLinus Torvalds 		if (!addr)
12781da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12791da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12801da177e4SLinus Torvalds 
12811da177e4SLinus Torvalds 	if (addr)
1282c6d14c84SEric Dumazet 		goto out_unlock;
12839f9354b9SEric Dumazet no_in_dev:
12843f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
12851da177e4SLinus Torvalds 
128617b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
128717b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
128817b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
128917b693cdSDavid Lamparter 	 * equality check in the loop below.
129017b693cdSDavid Lamparter 	 */
129117b693cdSDavid Lamparter 	if (master_idx &&
129217b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
129317b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
12948b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
12958b57fd1eSGao Feng 		if (addr)
129617b693cdSDavid Lamparter 			goto out_unlock;
129717b693cdSDavid Lamparter 	}
129817b693cdSDavid Lamparter 
12991da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1300ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13011da177e4SLinus Torvalds 	   in dev_base list.
13021da177e4SLinus Torvalds 	 */
1303c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13043f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13053f2fb9a8SDavid Ahern 			continue;
13063f2fb9a8SDavid Ahern 
13079f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13089f9354b9SEric Dumazet 		if (!in_dev)
13091da177e4SLinus Torvalds 			continue;
13101da177e4SLinus Torvalds 
13118b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13128b57fd1eSGao Feng 		if (addr)
1313c6d14c84SEric Dumazet 			goto out_unlock;
13141da177e4SLinus Torvalds 	}
1315c6d14c84SEric Dumazet out_unlock:
13161da177e4SLinus Torvalds 	rcu_read_unlock();
13171da177e4SLinus Torvalds 	return addr;
13181da177e4SLinus Torvalds }
13199f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13201da177e4SLinus Torvalds 
132160cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
132260cad5daSAl Viro 			      __be32 local, int scope)
13231da177e4SLinus Torvalds {
13241da177e4SLinus Torvalds 	int same = 0;
1325a144ea4bSAl Viro 	__be32 addr = 0;
13261da177e4SLinus Torvalds 
13271da177e4SLinus Torvalds 	for_ifa(in_dev) {
13281da177e4SLinus Torvalds 		if (!addr &&
13291da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
13301da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
13311da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13321da177e4SLinus Torvalds 			if (same)
13331da177e4SLinus Torvalds 				break;
13341da177e4SLinus Torvalds 		}
13351da177e4SLinus Torvalds 		if (!same) {
13361da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13371da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13381da177e4SLinus Torvalds 			if (same && addr) {
13391da177e4SLinus Torvalds 				if (local || !dst)
13401da177e4SLinus Torvalds 					break;
13411da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
13421da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
13431da177e4SLinus Torvalds 					break;
13441da177e4SLinus Torvalds 				/* No, then can we use new local src? */
13451da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
13461da177e4SLinus Torvalds 					addr = ifa->ifa_local;
13471da177e4SLinus Torvalds 					break;
13481da177e4SLinus Torvalds 				}
13491da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
13501da177e4SLinus Torvalds 				same = 0;
13511da177e4SLinus Torvalds 			}
13521da177e4SLinus Torvalds 		}
13531da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 	return same ? addr : 0;
13561da177e4SLinus Torvalds }
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds /*
13591da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1360b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1361b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
13621da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
13631da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
13641da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
13651da177e4SLinus Torvalds  */
1366b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
13679bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
13681da177e4SLinus Torvalds {
136960cad5daSAl Viro 	__be32 addr = 0;
13709bd85e32SDenis V. Lunev 	struct net_device *dev;
13711da177e4SLinus Torvalds 
137200db4124SIan Morris 	if (in_dev)
13739bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
13741da177e4SLinus Torvalds 
13751da177e4SLinus Torvalds 	rcu_read_lock();
1376c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13779f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13789f9354b9SEric Dumazet 		if (in_dev) {
13791da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
13801da177e4SLinus Torvalds 			if (addr)
13811da177e4SLinus Torvalds 				break;
13821da177e4SLinus Torvalds 		}
13831da177e4SLinus Torvalds 	}
13841da177e4SLinus Torvalds 	rcu_read_unlock();
13851da177e4SLinus Torvalds 
13861da177e4SLinus Torvalds 	return addr;
13871da177e4SLinus Torvalds }
1388eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
13891da177e4SLinus Torvalds 
13901da177e4SLinus Torvalds /*
13911da177e4SLinus Torvalds  *	Device notifier
13921da177e4SLinus Torvalds  */
13931da177e4SLinus Torvalds 
13941da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
13951da177e4SLinus Torvalds {
1396e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
13971da177e4SLinus Torvalds }
13989f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
13991da177e4SLinus Torvalds 
14001da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14011da177e4SLinus Torvalds {
1402e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14031da177e4SLinus Torvalds }
14049f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14051da177e4SLinus Torvalds 
14063ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14073ad7d246SKrister Johansen {
14083ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14093ad7d246SKrister Johansen }
14103ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14113ad7d246SKrister Johansen 
14123ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14133ad7d246SKrister Johansen {
14143ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14153ad7d246SKrister Johansen 	    nb);
14163ad7d246SKrister Johansen }
14173ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14183ad7d246SKrister Johansen 
14199f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14209f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14211da177e4SLinus Torvalds */
14221da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14231da177e4SLinus Torvalds {
14241da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14251da177e4SLinus Torvalds 	int named = 0;
14261da177e4SLinus Torvalds 
14271da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
14281da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14291da177e4SLinus Torvalds 
14301da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14311da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14321da177e4SLinus Torvalds 		if (named++ == 0)
1433573bf470SThomas Graf 			goto skip;
143444344b2aSMark McLoughlin 		dot = strchr(old, ':');
143551456b29SIan Morris 		if (!dot) {
14361da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14371da177e4SLinus Torvalds 			dot = old;
14381da177e4SLinus Torvalds 		}
14399f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14401da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14419f9354b9SEric Dumazet 		else
14421da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1443573bf470SThomas Graf skip:
1444573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
14451da177e4SLinus Torvalds 	}
14461da177e4SLinus Torvalds }
14471da177e4SLinus Torvalds 
144840384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
144906770843SBreno Leitao {
1450b5476022SEric Dumazet 	return mtu >= IPV4_MIN_MTU;
145106770843SBreno Leitao }
145206770843SBreno Leitao 
1453d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1454d11327adSIan Campbell 					struct in_device *in_dev)
1455d11327adSIan Campbell 
1456d11327adSIan Campbell {
1457b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1458d11327adSIan Campbell 
1459b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1460b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1461d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
14626c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
14636c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1464d11327adSIan Campbell 			 dev->dev_addr, NULL);
1465d11327adSIan Campbell 	}
1466b76d0789SZoltan Kiss }
1467d11327adSIan Campbell 
14681da177e4SLinus Torvalds /* Called only under RTNL semaphore */
14691da177e4SLinus Torvalds 
14701da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
14711da177e4SLinus Torvalds 			 void *ptr)
14721da177e4SLinus Torvalds {
1473351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1474748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
14751da177e4SLinus Torvalds 
14761da177e4SLinus Torvalds 	ASSERT_RTNL();
14771da177e4SLinus Torvalds 
14781da177e4SLinus Torvalds 	if (!in_dev) {
14798030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
14801da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
148120e61da7SWANG Cong 			if (IS_ERR(in_dev))
148220e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
14830cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
148442f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
148542f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
14861da177e4SLinus Torvalds 			}
148706770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
148806770843SBreno Leitao 			/* Re-enabling IP */
148906770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
149006770843SBreno Leitao 				in_dev = inetdev_init(dev);
14918030f544SHerbert Xu 		}
14921da177e4SLinus Torvalds 		goto out;
14931da177e4SLinus Torvalds 	}
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds 	switch (event) {
14961da177e4SLinus Torvalds 	case NETDEV_REGISTER:
149791df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1498a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
14991da177e4SLinus Torvalds 		break;
15001da177e4SLinus Torvalds 	case NETDEV_UP:
150106770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15021da177e4SLinus Torvalds 			break;
15030cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15049f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15059f9354b9SEric Dumazet 
15069f9354b9SEric Dumazet 			if (ifa) {
1507fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15081da177e4SLinus Torvalds 				ifa->ifa_local =
15091da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15101da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15111da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15121da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15131da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15141da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15151da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15165c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15175c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1518dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1519dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15201da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15211da177e4SLinus Torvalds 			}
15221da177e4SLinus Torvalds 		}
15231da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1524eefef1cfSStephen Hemminger 		/* fall through */
1525eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1526d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1527d11327adSIan Campbell 			break;
1528d11327adSIan Campbell 		/* fall through */
1529d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1530a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1531d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15321da177e4SLinus Torvalds 		break;
15331da177e4SLinus Torvalds 	case NETDEV_DOWN:
15341da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15351da177e4SLinus Torvalds 		break;
153693d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
153775c78500SMoni Shoua 		ip_mc_unmap(in_dev);
153875c78500SMoni Shoua 		break;
153993d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
154075c78500SMoni Shoua 		ip_mc_remap(in_dev);
154175c78500SMoni Shoua 		break;
15421da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
154306770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15441da177e4SLinus Torvalds 			break;
154506770843SBreno Leitao 		/* disable IP when MTU is not enough */
1546fcfd6dfaSGustavo A. R. Silva 		/* fall through */
15471da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
15481da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
15491da177e4SLinus Torvalds 		break;
15501da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
15511da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
15521da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
15531da177e4SLinus Torvalds 		 */
15541da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
15551da177e4SLinus Torvalds 
155651602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
155766f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
15581da177e4SLinus Torvalds 		break;
15591da177e4SLinus Torvalds 	}
15601da177e4SLinus Torvalds out:
15611da177e4SLinus Torvalds 	return NOTIFY_DONE;
15621da177e4SLinus Torvalds }
15631da177e4SLinus Torvalds 
15641da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
15651da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
15661da177e4SLinus Torvalds };
15671da177e4SLinus Torvalds 
156840384999SEric Dumazet static size_t inet_nlmsg_size(void)
1569339bf98fSThomas Graf {
1570339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1571339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1572339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1573339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1574ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
157563b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1576af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
157763b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1578339bf98fSThomas Graf }
1579339bf98fSThomas Graf 
15805c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
15815c766d64SJiri Pirko {
15825c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
15835c766d64SJiri Pirko }
15845c766d64SJiri Pirko 
15855c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
15865c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
15875c766d64SJiri Pirko {
15885c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
15895c766d64SJiri Pirko 
15905c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
15915c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
15925c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
15935c766d64SJiri Pirko 	ci.ifa_valid = valid;
15945c766d64SJiri Pirko 
15955c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
15965c766d64SJiri Pirko }
15975c766d64SJiri Pirko 
15981da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1599978a46faSChristian Brauner 			    struct inet_fill_args *args)
16001da177e4SLinus Torvalds {
16011da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16021da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16035c766d64SJiri Pirko 	u32 preferred, valid;
16041da177e4SLinus Torvalds 
1605978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1606978a46faSChristian Brauner 			args->flags);
160751456b29SIan Morris 	if (!nlh)
160826932566SPatrick McHardy 		return -EMSGSIZE;
160947f68512SThomas Graf 
161047f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16111da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16121da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16135c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16141da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16151da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16161da177e4SLinus Torvalds 
1617978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1618978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1619d3807145SChristian Brauner 		goto nla_put_failure;
1620d3807145SChristian Brauner 
16215c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16225c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16235c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16245c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16255c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16265c766d64SJiri Pirko 
16275c766d64SJiri Pirko 			if (preferred > tval)
16285c766d64SJiri Pirko 				preferred -= tval;
16295c766d64SJiri Pirko 			else
16305c766d64SJiri Pirko 				preferred = 0;
16315c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16325c766d64SJiri Pirko 				if (valid > tval)
16335c766d64SJiri Pirko 					valid -= tval;
16345c766d64SJiri Pirko 				else
16355c766d64SJiri Pirko 					valid = 0;
16365c766d64SJiri Pirko 			}
16375c766d64SJiri Pirko 		}
16385c766d64SJiri Pirko 	} else {
16395c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16405c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16415c766d64SJiri Pirko 	}
1642f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1643930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1644f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1645930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1646f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1647930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1648f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
16495c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1650ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1651af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1652af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
16535c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
16545c766d64SJiri Pirko 			  preferred, valid))
1655f3756b79SDavid S. Miller 		goto nla_put_failure;
165647f68512SThomas Graf 
1657053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1658053c095aSJohannes Berg 	return 0;
165947f68512SThomas Graf 
166047f68512SThomas Graf nla_put_failure:
166126932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
166226932566SPatrick McHardy 	return -EMSGSIZE;
16631da177e4SLinus Torvalds }
16641da177e4SLinus Torvalds 
1665c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1666c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1667c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
16685fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1669c33078e3SDavid Ahern {
16705fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1671c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1672c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1673c33078e3SDavid Ahern 	int err, i;
1674c33078e3SDavid Ahern 
1675c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1676c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1677c33078e3SDavid Ahern 		return -EINVAL;
1678c33078e3SDavid Ahern 	}
1679c33078e3SDavid Ahern 
1680c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1681c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1682c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1683c33078e3SDavid Ahern 		return -EINVAL;
1684c33078e3SDavid Ahern 	}
16855fcd266aSDavid Ahern 
16865fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
16875fcd266aSDavid Ahern 	if (fillargs->ifindex) {
16885fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
16895fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1690c33078e3SDavid Ahern 	}
1691c33078e3SDavid Ahern 
1692c33078e3SDavid Ahern 	err = nlmsg_parse_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1693c33078e3SDavid Ahern 				 ifa_ipv4_policy, extack);
1694c33078e3SDavid Ahern 	if (err < 0)
1695c33078e3SDavid Ahern 		return err;
1696c33078e3SDavid Ahern 
1697c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1698c33078e3SDavid Ahern 		if (!tb[i])
1699c33078e3SDavid Ahern 			continue;
1700c33078e3SDavid Ahern 
1701c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1702c33078e3SDavid Ahern 			struct net *net;
1703c33078e3SDavid Ahern 
1704c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1705c33078e3SDavid Ahern 
1706c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1707c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1708bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1709c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1710c33078e3SDavid Ahern 				return PTR_ERR(net);
1711c33078e3SDavid Ahern 			}
1712c33078e3SDavid Ahern 			*tgt_net = net;
1713c33078e3SDavid Ahern 		} else {
1714c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1715c33078e3SDavid Ahern 			return -EINVAL;
1716c33078e3SDavid Ahern 		}
1717c33078e3SDavid Ahern 	}
1718c33078e3SDavid Ahern 
1719c33078e3SDavid Ahern 	return 0;
1720c33078e3SDavid Ahern }
1721c33078e3SDavid Ahern 
17221c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
17231c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
17241c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
17251c98eca4SDavid Ahern {
17261c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
17271c98eca4SDavid Ahern 	int ip_idx = 0;
17281c98eca4SDavid Ahern 	int err;
17291c98eca4SDavid Ahern 
17301c98eca4SDavid Ahern 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next, ip_idx++) {
17311c98eca4SDavid Ahern 		if (ip_idx < s_ip_idx)
17321c98eca4SDavid Ahern 			continue;
17331c98eca4SDavid Ahern 
17341c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
17351c98eca4SDavid Ahern 		if (err < 0)
17361c98eca4SDavid Ahern 			goto done;
17371c98eca4SDavid Ahern 
17381c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
17391c98eca4SDavid Ahern 	}
17401c98eca4SDavid Ahern 	err = 0;
17411c98eca4SDavid Ahern 
17421c98eca4SDavid Ahern done:
17431c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
17441c98eca4SDavid Ahern 
17451c98eca4SDavid Ahern 	return err;
17461c98eca4SDavid Ahern }
17471c98eca4SDavid Ahern 
17481da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
17491da177e4SLinus Torvalds {
1750c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1751978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1752978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1753c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1754978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1755978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1756978a46faSChristian Brauner 		.netnsid = -1,
1757978a46faSChristian Brauner 	};
17583b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1759d3807145SChristian Brauner 	struct net *tgt_net = net;
1760eec4df98SEric Dumazet 	int h, s_h;
1761eec4df98SEric Dumazet 	int idx, s_idx;
17621c98eca4SDavid Ahern 	int s_ip_idx;
17631da177e4SLinus Torvalds 	struct net_device *dev;
17641da177e4SLinus Torvalds 	struct in_device *in_dev;
1765eec4df98SEric Dumazet 	struct hlist_head *head;
1766d7e38611SDavid Ahern 	int err = 0;
17671da177e4SLinus Torvalds 
1768eec4df98SEric Dumazet 	s_h = cb->args[0];
1769eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
17701c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1771eec4df98SEric Dumazet 
1772c33078e3SDavid Ahern 	if (cb->strict_check) {
1773c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
17745fcd266aSDavid Ahern 						 skb->sk, cb);
1775c33078e3SDavid Ahern 		if (err < 0)
1776d7e38611SDavid Ahern 			goto put_tgt_net;
17775fcd266aSDavid Ahern 
1778d7e38611SDavid Ahern 		err = 0;
17795fcd266aSDavid Ahern 		if (fillargs.ifindex) {
17805fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1781d7e38611SDavid Ahern 			if (!dev) {
1782d7e38611SDavid Ahern 				err = -ENODEV;
1783d7e38611SDavid Ahern 				goto put_tgt_net;
1784d7e38611SDavid Ahern 			}
17855fcd266aSDavid Ahern 
17865fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
17875fcd266aSDavid Ahern 			if (in_dev) {
17885fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
17895fcd266aSDavid Ahern 						       &fillargs);
17905fcd266aSDavid Ahern 			}
17915fcd266aSDavid Ahern 			goto put_tgt_net;
17925fcd266aSDavid Ahern 		}
1793d3807145SChristian Brauner 	}
1794d3807145SChristian Brauner 
1795eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
17967562f876SPavel Emelianov 		idx = 0;
1797d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1798eec4df98SEric Dumazet 		rcu_read_lock();
1799d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1800d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1801b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18021da177e4SLinus Torvalds 			if (idx < s_idx)
18037562f876SPavel Emelianov 				goto cont;
18044b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
18051da177e4SLinus Torvalds 				s_ip_idx = 0;
1806eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18079f9354b9SEric Dumazet 			if (!in_dev)
18087562f876SPavel Emelianov 				goto cont;
18091da177e4SLinus Torvalds 
18101c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18111c98eca4SDavid Ahern 					       &fillargs);
18121c98eca4SDavid Ahern 			if (err < 0) {
1813eec4df98SEric Dumazet 				rcu_read_unlock();
18141da177e4SLinus Torvalds 				goto done;
18151da177e4SLinus Torvalds 			}
18167562f876SPavel Emelianov cont:
18177562f876SPavel Emelianov 			idx++;
18181da177e4SLinus Torvalds 		}
1819eec4df98SEric Dumazet 		rcu_read_unlock();
1820eec4df98SEric Dumazet 	}
18211da177e4SLinus Torvalds 
18221da177e4SLinus Torvalds done:
1823eec4df98SEric Dumazet 	cb->args[0] = h;
1824eec4df98SEric Dumazet 	cb->args[1] = idx;
18255fcd266aSDavid Ahern put_tgt_net:
1826978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1827d3807145SChristian Brauner 		put_net(tgt_net);
18281da177e4SLinus Torvalds 
18297c1e8a38SArthur Gautier 	return skb->len ? : err;
18301da177e4SLinus Torvalds }
18311da177e4SLinus Torvalds 
1832d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
183315e47304SEric W. Biederman 		      u32 portid)
18341da177e4SLinus Torvalds {
1835978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1836978a46faSChristian Brauner 		.portid = portid,
1837978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1838978a46faSChristian Brauner 		.event = event,
1839978a46faSChristian Brauner 		.flags = 0,
1840978a46faSChristian Brauner 		.netnsid = -1,
1841978a46faSChristian Brauner 	};
184247f68512SThomas Graf 	struct sk_buff *skb;
1843d6062cbbSThomas Graf 	int err = -ENOBUFS;
18444b8aa9abSDenis V. Lunev 	struct net *net;
18451da177e4SLinus Torvalds 
1846c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1847339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
184851456b29SIan Morris 	if (!skb)
1849d6062cbbSThomas Graf 		goto errout;
1850d6062cbbSThomas Graf 
1851978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
185226932566SPatrick McHardy 	if (err < 0) {
185326932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
185426932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
185526932566SPatrick McHardy 		kfree_skb(skb);
185626932566SPatrick McHardy 		goto errout;
185726932566SPatrick McHardy 	}
185815e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
18591ce85fe4SPablo Neira Ayuso 	return;
1860d6062cbbSThomas Graf errout:
1861d6062cbbSThomas Graf 	if (err < 0)
18624b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
18631da177e4SLinus Torvalds }
18641da177e4SLinus Torvalds 
1865b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1866b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
18679f0f7272SThomas Graf {
18681fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
18699f0f7272SThomas Graf 
18709f0f7272SThomas Graf 	if (!in_dev)
18719f0f7272SThomas Graf 		return 0;
18729f0f7272SThomas Graf 
18739f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
18749f0f7272SThomas Graf }
18759f0f7272SThomas Graf 
1876d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1877d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
18789f0f7272SThomas Graf {
18791fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
18809f0f7272SThomas Graf 	struct nlattr *nla;
18819f0f7272SThomas Graf 	int i;
18829f0f7272SThomas Graf 
18839f0f7272SThomas Graf 	if (!in_dev)
18849f0f7272SThomas Graf 		return -ENODATA;
18859f0f7272SThomas Graf 
18869f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
188751456b29SIan Morris 	if (!nla)
18889f0f7272SThomas Graf 		return -EMSGSIZE;
18899f0f7272SThomas Graf 
18909f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
18919f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
18929f0f7272SThomas Graf 
18939f0f7272SThomas Graf 	return 0;
18949f0f7272SThomas Graf }
18959f0f7272SThomas Graf 
18969f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
18979f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
18989f0f7272SThomas Graf };
18999f0f7272SThomas Graf 
1900cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1901cf7afbfeSThomas Graf 				 const struct nlattr *nla)
19029f0f7272SThomas Graf {
19039f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19049f0f7272SThomas Graf 	int err, rem;
19059f0f7272SThomas Graf 
19065fa85a09SFlorian Westphal 	if (dev && !__in_dev_get_rcu(dev))
1907cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19089f0f7272SThomas Graf 
1909fceb6435SJohannes Berg 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy, NULL);
19109f0f7272SThomas Graf 	if (err < 0)
19119f0f7272SThomas Graf 		return err;
19129f0f7272SThomas Graf 
19139f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19149f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
19159f0f7272SThomas Graf 			int cfgid = nla_type(a);
19169f0f7272SThomas Graf 
19179f0f7272SThomas Graf 			if (nla_len(a) < 4)
19189f0f7272SThomas Graf 				return -EINVAL;
19199f0f7272SThomas Graf 
19209f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
19219f0f7272SThomas Graf 				return -EINVAL;
19229f0f7272SThomas Graf 		}
19239f0f7272SThomas Graf 	}
19249f0f7272SThomas Graf 
1925cf7afbfeSThomas Graf 	return 0;
1926cf7afbfeSThomas Graf }
1927cf7afbfeSThomas Graf 
1928cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1929cf7afbfeSThomas Graf {
19305fa85a09SFlorian Westphal 	struct in_device *in_dev = __in_dev_get_rcu(dev);
1931cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1932cf7afbfeSThomas Graf 	int rem;
1933cf7afbfeSThomas Graf 
1934cf7afbfeSThomas Graf 	if (!in_dev)
1935cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1936cf7afbfeSThomas Graf 
1937fceb6435SJohannes Berg 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
1938cf7afbfeSThomas Graf 		BUG();
1939cf7afbfeSThomas Graf 
19409f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19419f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
19429f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
19439f0f7272SThomas Graf 	}
19449f0f7272SThomas Graf 
19459f0f7272SThomas Graf 	return 0;
19469f0f7272SThomas Graf }
19479f0f7272SThomas Graf 
1948edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1949edc9e748SNicolas Dichtel {
1950edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1951edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1952136ba622SZhang Shengju 	bool all = false;
1953edc9e748SNicolas Dichtel 
1954136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1955136ba622SZhang Shengju 		all = true;
1956136ba622SZhang Shengju 
1957136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
1958edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1959136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
1960cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1961136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
1962d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
19635cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
19645cbf777cSXin Long 		size += nla_total_size(4);
1965136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
1966f085ff1cSstephen hemminger 		size += nla_total_size(4);
1967136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
1968974d7af5SAndy Gospodarek 		size += nla_total_size(4);
1969edc9e748SNicolas Dichtel 
1970edc9e748SNicolas Dichtel 	return size;
1971edc9e748SNicolas Dichtel }
1972edc9e748SNicolas Dichtel 
1973edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1974edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1975edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1976edc9e748SNicolas Dichtel 				     int type)
1977edc9e748SNicolas Dichtel {
1978edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1979edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1980136ba622SZhang Shengju 	bool all = false;
1981edc9e748SNicolas Dichtel 
1982edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1983edc9e748SNicolas Dichtel 			flags);
198451456b29SIan Morris 	if (!nlh)
1985edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1986edc9e748SNicolas Dichtel 
1987136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1988136ba622SZhang Shengju 		all = true;
1989136ba622SZhang Shengju 
1990edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1991edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1992edc9e748SNicolas Dichtel 
1993edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1994edc9e748SNicolas Dichtel 		goto nla_put_failure;
1995edc9e748SNicolas Dichtel 
1996b5c9641dSDavid Ahern 	if (!devconf)
1997b5c9641dSDavid Ahern 		goto out;
1998b5c9641dSDavid Ahern 
1999136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2000edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2001edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2002edc9e748SNicolas Dichtel 		goto nla_put_failure;
2003136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2004cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2005cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2006cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2007136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2008d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2009d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2010d67b8c61SNicolas Dichtel 		goto nla_put_failure;
20115cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
20125cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
20135cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
20145cbf777cSXin Long 		goto nla_put_failure;
2015136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
201609aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2017f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2018f085ff1cSstephen hemminger 		goto nla_put_failure;
2019136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2020974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2021974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2022974d7af5SAndy Gospodarek 		goto nla_put_failure;
2023edc9e748SNicolas Dichtel 
2024b5c9641dSDavid Ahern out:
2025053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2026053c095aSJohannes Berg 	return 0;
2027edc9e748SNicolas Dichtel 
2028edc9e748SNicolas Dichtel nla_put_failure:
2029edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2030edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2031edc9e748SNicolas Dichtel }
2032edc9e748SNicolas Dichtel 
20333b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
20343b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2035edc9e748SNicolas Dichtel {
2036edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2037edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2038edc9e748SNicolas Dichtel 
2039fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
204051456b29SIan Morris 	if (!skb)
2041edc9e748SNicolas Dichtel 		goto errout;
2042edc9e748SNicolas Dichtel 
2043edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
20443b022865SDavid Ahern 					event, 0, type);
2045edc9e748SNicolas Dichtel 	if (err < 0) {
2046edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2047edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2048edc9e748SNicolas Dichtel 		kfree_skb(skb);
2049edc9e748SNicolas Dichtel 		goto errout;
2050edc9e748SNicolas Dichtel 	}
2051fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2052edc9e748SNicolas Dichtel 	return;
2053edc9e748SNicolas Dichtel errout:
2054edc9e748SNicolas Dichtel 	if (err < 0)
2055edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2056edc9e748SNicolas Dichtel }
2057edc9e748SNicolas Dichtel 
20589e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
20599e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
20609e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2061cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
206209aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2063974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
20649e551110SNicolas Dichtel };
20659e551110SNicolas Dichtel 
2066eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2067eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2068eede370dSJakub Kicinski 				      struct nlattr **tb,
2069eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2070eede370dSJakub Kicinski {
2071eede370dSJakub Kicinski 	int i, err;
2072eede370dSJakub Kicinski 
2073eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2074eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2075eede370dSJakub Kicinski 		return -EINVAL;
2076eede370dSJakub Kicinski 	}
2077eede370dSJakub Kicinski 
2078eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
2079eede370dSJakub Kicinski 		return nlmsg_parse(nlh, sizeof(struct netconfmsg), tb,
2080eede370dSJakub Kicinski 				   NETCONFA_MAX, devconf_ipv4_policy, extack);
2081eede370dSJakub Kicinski 
2082eede370dSJakub Kicinski 	err = nlmsg_parse_strict(nlh, sizeof(struct netconfmsg), tb,
2083eede370dSJakub Kicinski 				 NETCONFA_MAX, devconf_ipv4_policy, extack);
2084eede370dSJakub Kicinski 	if (err)
2085eede370dSJakub Kicinski 		return err;
2086eede370dSJakub Kicinski 
2087eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2088eede370dSJakub Kicinski 		if (!tb[i])
2089eede370dSJakub Kicinski 			continue;
2090eede370dSJakub Kicinski 
2091eede370dSJakub Kicinski 		switch (i) {
2092eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2093eede370dSJakub Kicinski 			break;
2094eede370dSJakub Kicinski 		default:
2095eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2096eede370dSJakub Kicinski 			return -EINVAL;
2097eede370dSJakub Kicinski 		}
2098eede370dSJakub Kicinski 	}
2099eede370dSJakub Kicinski 
2100eede370dSJakub Kicinski 	return 0;
2101eede370dSJakub Kicinski }
2102eede370dSJakub Kicinski 
21039e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2104c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2105c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
21069e551110SNicolas Dichtel {
21079e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
21089e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
21099e551110SNicolas Dichtel 	struct sk_buff *skb;
21109e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
21119e551110SNicolas Dichtel 	struct in_device *in_dev;
21129e551110SNicolas Dichtel 	struct net_device *dev;
21139e551110SNicolas Dichtel 	int ifindex;
21149e551110SNicolas Dichtel 	int err;
21159e551110SNicolas Dichtel 
2116eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2117eede370dSJakub Kicinski 	if (err)
21189e551110SNicolas Dichtel 		goto errout;
21199e551110SNicolas Dichtel 
2120a97eb33fSAnton Protopopov 	err = -EINVAL;
21219e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
21229e551110SNicolas Dichtel 		goto errout;
21239e551110SNicolas Dichtel 
21249e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
21259e551110SNicolas Dichtel 	switch (ifindex) {
21269e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
21279e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
21289e551110SNicolas Dichtel 		break;
21299e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
21309e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
21319e551110SNicolas Dichtel 		break;
21329e551110SNicolas Dichtel 	default:
21339e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
213451456b29SIan Morris 		if (!dev)
21359e551110SNicolas Dichtel 			goto errout;
21369e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
213751456b29SIan Morris 		if (!in_dev)
21389e551110SNicolas Dichtel 			goto errout;
21399e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
21409e551110SNicolas Dichtel 		break;
21419e551110SNicolas Dichtel 	}
21429e551110SNicolas Dichtel 
21439e551110SNicolas Dichtel 	err = -ENOBUFS;
2144fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
214551456b29SIan Morris 	if (!skb)
21469e551110SNicolas Dichtel 		goto errout;
21479e551110SNicolas Dichtel 
21489e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
21499e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
21509e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2151136ba622SZhang Shengju 					NETCONFA_ALL);
21529e551110SNicolas Dichtel 	if (err < 0) {
21539e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
21549e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
21559e551110SNicolas Dichtel 		kfree_skb(skb);
21569e551110SNicolas Dichtel 		goto errout;
21579e551110SNicolas Dichtel 	}
21589e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
21599e551110SNicolas Dichtel errout:
21609e551110SNicolas Dichtel 	return err;
21619e551110SNicolas Dichtel }
21629e551110SNicolas Dichtel 
21637a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
21647a674200SNicolas Dichtel 				     struct netlink_callback *cb)
21657a674200SNicolas Dichtel {
2166addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
21677a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
21687a674200SNicolas Dichtel 	int h, s_h;
21697a674200SNicolas Dichtel 	int idx, s_idx;
21707a674200SNicolas Dichtel 	struct net_device *dev;
21717a674200SNicolas Dichtel 	struct in_device *in_dev;
21727a674200SNicolas Dichtel 	struct hlist_head *head;
21737a674200SNicolas Dichtel 
2174addd383fSDavid Ahern 	if (cb->strict_check) {
2175addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2176addd383fSDavid Ahern 		struct netconfmsg *ncm;
2177addd383fSDavid Ahern 
2178addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2179addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2180addd383fSDavid Ahern 			return -EINVAL;
2181addd383fSDavid Ahern 		}
2182addd383fSDavid Ahern 
2183addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2184addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2185addd383fSDavid Ahern 			return -EINVAL;
2186addd383fSDavid Ahern 		}
2187addd383fSDavid Ahern 	}
2188addd383fSDavid Ahern 
21897a674200SNicolas Dichtel 	s_h = cb->args[0];
21907a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
21917a674200SNicolas Dichtel 
21927a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
21937a674200SNicolas Dichtel 		idx = 0;
21947a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
21957a674200SNicolas Dichtel 		rcu_read_lock();
21960465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
21970465277fSNicolas Dichtel 			  net->dev_base_seq;
21987a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
21997a674200SNicolas Dichtel 			if (idx < s_idx)
22007a674200SNicolas Dichtel 				goto cont;
22017a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
22027a674200SNicolas Dichtel 			if (!in_dev)
22037a674200SNicolas Dichtel 				goto cont;
22047a674200SNicolas Dichtel 
22057a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
22067a674200SNicolas Dichtel 						      &in_dev->cnf,
22077a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2208addd383fSDavid Ahern 						      nlh->nlmsg_seq,
22097a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
22107a674200SNicolas Dichtel 						      NLM_F_MULTI,
2211136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
22127a674200SNicolas Dichtel 				rcu_read_unlock();
22137a674200SNicolas Dichtel 				goto done;
22147a674200SNicolas Dichtel 			}
22150465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
22167a674200SNicolas Dichtel cont:
22177a674200SNicolas Dichtel 			idx++;
22187a674200SNicolas Dichtel 		}
22197a674200SNicolas Dichtel 		rcu_read_unlock();
22207a674200SNicolas Dichtel 	}
22217a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
22227a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
22237a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
22247a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2225addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22267a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2227136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22287a674200SNicolas Dichtel 			goto done;
22297a674200SNicolas Dichtel 		else
22307a674200SNicolas Dichtel 			h++;
22317a674200SNicolas Dichtel 	}
22327a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
22337a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
22347a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
22357a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2236addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22377a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2238136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22397a674200SNicolas Dichtel 			goto done;
22407a674200SNicolas Dichtel 		else
22417a674200SNicolas Dichtel 			h++;
22427a674200SNicolas Dichtel 	}
22437a674200SNicolas Dichtel done:
22447a674200SNicolas Dichtel 	cb->args[0] = h;
22457a674200SNicolas Dichtel 	cb->args[1] = idx;
22467a674200SNicolas Dichtel 
22477a674200SNicolas Dichtel 	return skb->len;
22487a674200SNicolas Dichtel }
22497a674200SNicolas Dichtel 
22501da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
22511da177e4SLinus Torvalds 
2252c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
225331be3085SHerbert Xu {
225431be3085SHerbert Xu 	struct net_device *dev;
225531be3085SHerbert Xu 
225631be3085SHerbert Xu 	rcu_read_lock();
2257c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2258c6d14c84SEric Dumazet 		struct in_device *in_dev;
2259c6d14c84SEric Dumazet 
226031be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
226131be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
22629355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2263c6d14c84SEric Dumazet 	}
226431be3085SHerbert Xu 	rcu_read_unlock();
226531be3085SHerbert Xu }
226631be3085SHerbert Xu 
2267c6d14c84SEric Dumazet /* called with RTNL locked */
2268c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
226968dd299bSPavel Emelyanov {
227068dd299bSPavel Emelyanov 	struct net_device *dev;
2271586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
227268dd299bSPavel Emelyanov 
2273586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
22749355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
22753b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22763b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2277edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2278edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
22793b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22803b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2281edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2282edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
228368dd299bSPavel Emelyanov 
2284c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
228568dd299bSPavel Emelyanov 		struct in_device *in_dev;
2286fa17806cSEric Dumazet 
22870187bdfbSBen Hutchings 		if (on)
22880187bdfbSBen Hutchings 			dev_disable_lro(dev);
2289fa17806cSEric Dumazet 
2290fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2291edc9e748SNicolas Dichtel 		if (in_dev) {
229268dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
22933b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22943b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2295edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2296edc9e748SNicolas Dichtel 		}
229768dd299bSPavel Emelyanov 	}
229868dd299bSPavel Emelyanov }
229968dd299bSPavel Emelyanov 
2300f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2301f085ff1cSstephen hemminger {
2302f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2303f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2304f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2305f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2306f085ff1cSstephen hemminger 	else {
2307f085ff1cSstephen hemminger 		struct in_device *idev
2308f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2309f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2310f085ff1cSstephen hemminger 	}
2311f085ff1cSstephen hemminger }
2312f085ff1cSstephen hemminger 
2313fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
23148d65af78SAlexey Dobriyan 			     void __user *buffer,
231531be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
231631be3085SHerbert Xu {
2317d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23188d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2319d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
232031be3085SHerbert Xu 
232131be3085SHerbert Xu 	if (write) {
232231be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2323c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
232431be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2325f085ff1cSstephen hemminger 		int ifindex;
232631be3085SHerbert Xu 
232731be3085SHerbert Xu 		set_bit(i, cnf->state);
232831be3085SHerbert Xu 
23299355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2330c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2331d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2332d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2333d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
23344ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2335f085ff1cSstephen hemminger 
23365cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
23375cbf777cSXin Long 		    new_value != old_value)
23385cbf777cSXin Long 			rt_cache_flush(net);
23395cbf777cSXin Long 
2340cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2341cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2342f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
23433b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23443b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2345cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2346cc535dfbSNicolas Dichtel 		}
2347f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2348f085ff1cSstephen hemminger 		    new_value != old_value) {
2349f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
23503b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23513b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2352f085ff1cSstephen hemminger 						    ifindex, cnf);
2353f085ff1cSstephen hemminger 		}
2354974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2355974d7af5SAndy Gospodarek 		    new_value != old_value) {
2356974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
23573b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23583b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2359974d7af5SAndy Gospodarek 						    ifindex, cnf);
2360974d7af5SAndy Gospodarek 		}
236131be3085SHerbert Xu 	}
236231be3085SHerbert Xu 
236331be3085SHerbert Xu 	return ret;
236431be3085SHerbert Xu }
236531be3085SHerbert Xu 
2366fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
23678d65af78SAlexey Dobriyan 				  void __user *buffer,
23681da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
23691da177e4SLinus Torvalds {
23701da177e4SLinus Torvalds 	int *valp = ctl->data;
23711da177e4SLinus Torvalds 	int val = *valp;
237288af182eSEric W. Biederman 	loff_t pos = *ppos;
23738d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
23741da177e4SLinus Torvalds 
23751da177e4SLinus Torvalds 	if (write && *valp != val) {
2376c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2377c0ce9fb3SPavel Emelyanov 
23780187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
237988af182eSEric W. Biederman 			if (!rtnl_trylock()) {
238088af182eSEric W. Biederman 				/* Restore the original values before restarting */
238188af182eSEric W. Biederman 				*valp = val;
238288af182eSEric W. Biederman 				*ppos = pos;
23839b8adb5eSEric W. Biederman 				return restart_syscall();
238488af182eSEric W. Biederman 			}
23850187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2386c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2387edc9e748SNicolas Dichtel 			} else {
23880187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
23890187bdfbSBen Hutchings 				struct in_device *idev =
23900187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2391edc9e748SNicolas Dichtel 				if (*valp)
23920187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
23933b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2394edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2395edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2396edc9e748SNicolas Dichtel 							    cnf);
23970187bdfbSBen Hutchings 			}
23980187bdfbSBen Hutchings 			rtnl_unlock();
23994ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2400edc9e748SNicolas Dichtel 		} else
24013b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24023b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2403edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2404edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24050187bdfbSBen Hutchings 	}
24061da177e4SLinus Torvalds 
24071da177e4SLinus Torvalds 	return ret;
24081da177e4SLinus Torvalds }
24091da177e4SLinus Torvalds 
2410fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
24118d65af78SAlexey Dobriyan 				void __user *buffer,
24121da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
24131da177e4SLinus Torvalds {
24141da177e4SLinus Torvalds 	int *valp = ctl->data;
24151da177e4SLinus Torvalds 	int val = *valp;
24168d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
241776e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24181da177e4SLinus Torvalds 
24191da177e4SLinus Torvalds 	if (write && *valp != val)
24204ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
24211da177e4SLinus Torvalds 
24221da177e4SLinus Torvalds 	return ret;
24231da177e4SLinus Torvalds }
24241da177e4SLinus Torvalds 
2425f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
242642f811b8SHerbert Xu 	{ \
242742f811b8SHerbert Xu 		.procname	= name, \
242842f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
242902291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
243042f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
243142f811b8SHerbert Xu 		.mode		= mval, \
243242f811b8SHerbert Xu 		.proc_handler	= proc, \
243331be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
243442f811b8SHerbert Xu 	}
243542f811b8SHerbert Xu 
243642f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2437f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
243842f811b8SHerbert Xu 
243942f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2440f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
244142f811b8SHerbert Xu 
2442f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2443f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
244442f811b8SHerbert Xu 
244542f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2446f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
244742f811b8SHerbert Xu 
24481da177e4SLinus Torvalds static struct devinet_sysctl_table {
24491da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
245002291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
24511da177e4SLinus Torvalds } devinet_sysctl = {
24521da177e4SLinus Torvalds 	.devinet_vars = {
245342f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2454f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
245542f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
24565cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
245742f811b8SHerbert Xu 
245842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
245942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
246042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
246142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
246242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
246342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
246442f811b8SHerbert Xu 					"accept_source_route"),
24658153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
246628f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
246742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
246842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
246942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
247042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
247142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
247242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
247342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
247442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
247542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2476eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
247765324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
24785c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
24795c6fe01cSWilliam Manley 					"force_igmp_version"),
24802690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
24812690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
24822690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
24832690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
24840eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
24850eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
248697daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
248797daf331SJohannes Berg 					"drop_gratuitous_arp"),
248842f811b8SHerbert Xu 
248942f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
249042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
249142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
249242f811b8SHerbert Xu 					      "promote_secondaries"),
2493d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2494d0daebc3SThomas Graf 					      "route_localnet"),
249512b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
249612b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
24971da177e4SLinus Torvalds 	},
24981da177e4SLinus Torvalds };
24991da177e4SLinus Torvalds 
2500ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
250129c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25021da177e4SLinus Torvalds {
25031da177e4SLinus Torvalds 	int i;
25049fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25058607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2506bfada697SPavel Emelyanov 
25079fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
25081da177e4SLinus Torvalds 	if (!t)
25099fa89642SPavel Emelyanov 		goto out;
25109fa89642SPavel Emelyanov 
25111da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
25121da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
251331be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2514c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25151da177e4SLinus Torvalds 	}
25161da177e4SLinus Torvalds 
25178607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
25181da177e4SLinus Torvalds 
25198607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
25201da177e4SLinus Torvalds 	if (!t->sysctl_header)
25218607ddb8SEric W. Biederman 		goto free;
25221da177e4SLinus Torvalds 
25231da177e4SLinus Torvalds 	p->sysctl = t;
252429c994e3SNicolas Dichtel 
25253b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
25263b022865SDavid Ahern 				    ifindex, p);
2527ea40b324SPavel Emelyanov 	return 0;
25281da177e4SLinus Torvalds 
25291da177e4SLinus Torvalds free:
25301da177e4SLinus Torvalds 	kfree(t);
25319fa89642SPavel Emelyanov out:
2532ea40b324SPavel Emelyanov 	return -ENOBUFS;
25331da177e4SLinus Torvalds }
25341da177e4SLinus Torvalds 
2535b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2536b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
253766f27a52SPavel Emelyanov {
253851602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
253966f27a52SPavel Emelyanov 
2540b5c9641dSDavid Ahern 	if (t) {
254151602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2542ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
25431da177e4SLinus Torvalds 		kfree(t);
25441da177e4SLinus Torvalds 	}
254551602b2aSPavel Emelyanov 
2546b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2547b5c9641dSDavid Ahern }
2548b5c9641dSDavid Ahern 
254920e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
255051602b2aSPavel Emelyanov {
255120e61da7SWANG Cong 	int err;
255220e61da7SWANG Cong 
255320e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
255420e61da7SWANG Cong 		return -EINVAL;
255520e61da7SWANG Cong 
255620e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
255720e61da7SWANG Cong 	if (err)
255820e61da7SWANG Cong 		return err;
255920e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
256029c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
256120e61da7SWANG Cong 	if (err)
256220e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
256320e61da7SWANG Cong 	return err;
256451602b2aSPavel Emelyanov }
256551602b2aSPavel Emelyanov 
256651602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
256751602b2aSPavel Emelyanov {
2568b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2569b5c9641dSDavid Ahern 
2570b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
257151602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
25721da177e4SLinus Torvalds }
25731da177e4SLinus Torvalds 
257468dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
257568dd299bSPavel Emelyanov 	{
257668dd299bSPavel Emelyanov 		.procname	= "ip_forward",
257768dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
257802291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
257968dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
258068dd299bSPavel Emelyanov 		.mode		= 0644,
258168dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
258268dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2583c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
258468dd299bSPavel Emelyanov 	},
258568dd299bSPavel Emelyanov 	{ },
258668dd299bSPavel Emelyanov };
25872a75de0cSEric Dumazet #endif
258868dd299bSPavel Emelyanov 
2589752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2590752d14dcSPavel Emelyanov {
2591752d14dcSPavel Emelyanov 	int err;
2592752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
25932a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2594856c395cSCong Wang 	struct ctl_table *tbl;
2595752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
25962a75de0cSEric Dumazet #endif
2597752d14dcSPavel Emelyanov 
2598752d14dcSPavel Emelyanov 	err = -ENOMEM;
2599856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
260051456b29SIan Morris 	if (!all)
2601752d14dcSPavel Emelyanov 		goto err_alloc_all;
2602752d14dcSPavel Emelyanov 
2603856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
260451456b29SIan Morris 	if (!dflt)
2605752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2606752d14dcSPavel Emelyanov 
26072a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2608856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
260951456b29SIan Morris 	if (!tbl)
2610752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2611752d14dcSPavel Emelyanov 
261202291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2613752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2614752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26152a75de0cSEric Dumazet #endif
2616856c395cSCong Wang 
2617856c395cSCong Wang 	if (sysctl_devconf_inherit_init_net != 2 && !net_eq(net, &init_net)) {
2618856c395cSCong Wang 		memcpy(all, init_net.ipv4.devconf_all, sizeof(ipv4_devconf));
2619856c395cSCong Wang 		memcpy(dflt, init_net.ipv4.devconf_dflt, sizeof(ipv4_devconf_dflt));
2620752d14dcSPavel Emelyanov 	}
2621752d14dcSPavel Emelyanov 
2622752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
262329c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2624752d14dcSPavel Emelyanov 	if (err < 0)
2625752d14dcSPavel Emelyanov 		goto err_reg_all;
2626752d14dcSPavel Emelyanov 
262729c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
262829c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2629752d14dcSPavel Emelyanov 	if (err < 0)
2630752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2631752d14dcSPavel Emelyanov 
2632752d14dcSPavel Emelyanov 	err = -ENOMEM;
26338607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
263451456b29SIan Morris 	if (!forw_hdr)
2635752d14dcSPavel Emelyanov 		goto err_reg_ctl;
26362a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2637752d14dcSPavel Emelyanov #endif
2638752d14dcSPavel Emelyanov 
2639752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2640752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2641752d14dcSPavel Emelyanov 	return 0;
2642752d14dcSPavel Emelyanov 
2643752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2644752d14dcSPavel Emelyanov err_reg_ctl:
2645b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2646752d14dcSPavel Emelyanov err_reg_dflt:
2647b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2648752d14dcSPavel Emelyanov err_reg_all:
2649752d14dcSPavel Emelyanov 	kfree(tbl);
2650752d14dcSPavel Emelyanov err_alloc_ctl:
26512a75de0cSEric Dumazet #endif
2652752d14dcSPavel Emelyanov 	kfree(dflt);
2653752d14dcSPavel Emelyanov err_alloc_dflt:
2654752d14dcSPavel Emelyanov 	kfree(all);
2655752d14dcSPavel Emelyanov err_alloc_all:
2656752d14dcSPavel Emelyanov 	return err;
2657752d14dcSPavel Emelyanov }
2658752d14dcSPavel Emelyanov 
2659752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2660752d14dcSPavel Emelyanov {
26612a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2662752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2663752d14dcSPavel Emelyanov 
2664752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2665752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2666b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2667b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2668b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2669b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2670752d14dcSPavel Emelyanov 	kfree(tbl);
26712a75de0cSEric Dumazet #endif
2672752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2673752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2674752d14dcSPavel Emelyanov }
2675752d14dcSPavel Emelyanov 
2676752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2677752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2678752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2679752d14dcSPavel Emelyanov };
2680752d14dcSPavel Emelyanov 
2681207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
26829f0f7272SThomas Graf 	.family		  = AF_INET,
26839f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
26849f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2685cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2686cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
26879f0f7272SThomas Graf };
26889f0f7272SThomas Graf 
26891da177e4SLinus Torvalds void __init devinet_init(void)
26901da177e4SLinus Torvalds {
2691fd23c3b3SDavid S. Miller 	int i;
2692fd23c3b3SDavid S. Miller 
2693fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2694fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2695fd23c3b3SDavid S. Miller 
2696752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2697752d14dcSPavel Emelyanov 
26981da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
26991da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
270063f3444fSThomas Graf 
2701906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
27025c766d64SJiri Pirko 
27039f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
27049f0f7272SThomas Graf 
2705b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2706b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2707b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
27089e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2709b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
27101da177e4SLinus Torvalds }
2711