xref: /linux/net/ipv4/devinet.c (revision b5c9641d3d45e58dbcb35090345f863e5ade718d)
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 },
1025c753978SThomas Graf };
1035c753978SThomas Graf 
10440384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
10540384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
10640384999SEric Dumazet 
107fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
108fd23c3b3SDavid S. Miller 
1096eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
110fd23c3b3SDavid S. Miller {
11140384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
112fd23c3b3SDavid S. Miller 
11340384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
114fd23c3b3SDavid S. Miller }
115fd23c3b3SDavid S. Miller 
116fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
117fd23c3b3SDavid S. Miller {
11840384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
119fd23c3b3SDavid S. Miller 
12032a4be48SWANG Cong 	ASSERT_RTNL();
121fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
122fd23c3b3SDavid S. Miller }
123fd23c3b3SDavid S. Miller 
124fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
125fd23c3b3SDavid S. Miller {
12632a4be48SWANG Cong 	ASSERT_RTNL();
127fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
128fd23c3b3SDavid S. Miller }
129fd23c3b3SDavid S. Miller 
1309435eb1cSDavid S. Miller /**
1319435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1329435eb1cSDavid S. Miller  * @net: the net namespace
1339435eb1cSDavid S. Miller  * @addr: the source address
1349435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1359435eb1cSDavid S. Miller  *
1369435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1379435eb1cSDavid S. Miller  */
1389435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1399435eb1cSDavid S. Miller {
14040384999SEric Dumazet 	u32 hash = inet_addr_hash(net, addr);
1419435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1429435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1439435eb1cSDavid S. Miller 
1449435eb1cSDavid S. Miller 	rcu_read_lock();
145b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) {
14640384999SEric Dumazet 		if (ifa->ifa_local == addr) {
1479435eb1cSDavid S. Miller 			struct net_device *dev = ifa->ifa_dev->dev;
1489435eb1cSDavid S. Miller 
1499435eb1cSDavid S. Miller 			if (!net_eq(dev_net(dev), net))
1509435eb1cSDavid S. Miller 				continue;
1519435eb1cSDavid S. Miller 			result = dev;
1529435eb1cSDavid S. Miller 			break;
1539435eb1cSDavid S. Miller 		}
1549435eb1cSDavid S. Miller 	}
155406b6f97SDavid S. Miller 	if (!result) {
156406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
157406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
158406b6f97SDavid S. Miller 		struct fib_table *local;
159406b6f97SDavid S. Miller 
160406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
161406b6f97SDavid S. Miller 		 * over loopback subnets work.
162406b6f97SDavid S. Miller 		 */
163406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
164406b6f97SDavid S. Miller 		if (local &&
165406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
166406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
167406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
168406b6f97SDavid S. Miller 	}
1699435eb1cSDavid S. Miller 	if (result && devref)
1709435eb1cSDavid S. Miller 		dev_hold(result);
1719435eb1cSDavid S. Miller 	rcu_read_unlock();
1729435eb1cSDavid S. Miller 	return result;
1739435eb1cSDavid S. Miller }
1749435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1759435eb1cSDavid S. Miller 
176d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1771da177e4SLinus Torvalds 
178e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1791da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1801da177e4SLinus Torvalds 			 int destroy);
1811da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
18220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
18351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
18451602b2aSPavel Emelyanov #else
18520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
18651602b2aSPavel Emelyanov {
18720e61da7SWANG Cong 	return 0;
18851602b2aSPavel Emelyanov }
18940384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
19051602b2aSPavel Emelyanov {
19151602b2aSPavel Emelyanov }
1921da177e4SLinus Torvalds #endif
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds /* Locks all the inet devices. */
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1971da177e4SLinus Torvalds {
19893adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2021da177e4SLinus Torvalds {
2031da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2041da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2051da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2061da177e4SLinus Torvalds 	kfree(ifa);
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
20940384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2171da177e4SLinus Torvalds 
218547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
219547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
220e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2211da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
22291df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2231da177e4SLinus Torvalds #endif
2241da177e4SLinus Torvalds 	dev_put(dev);
2251da177e4SLinus Torvalds 	if (!idev->dead)
2269f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2279f9354b9SEric Dumazet 	else
2281da177e4SLinus Torvalds 		kfree(idev);
2291da177e4SLinus Torvalds }
2309f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2311da177e4SLinus Torvalds 
23271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	struct in_device *in_dev;
23520e61da7SWANG Cong 	int err = -ENOMEM;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	ASSERT_RTNL();
2381da177e4SLinus Torvalds 
2390da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2401da177e4SLinus Torvalds 	if (!in_dev)
2411da177e4SLinus Torvalds 		goto out;
242c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2439355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2441da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2451da177e4SLinus Torvalds 	in_dev->dev = dev;
2469f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2479f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2481da177e4SLinus Torvalds 		goto out_kfree;
2490187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2500187bdfbSBen Hutchings 		dev_disable_lro(dev);
2511da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2521da177e4SLinus Torvalds 	dev_hold(dev);
25330c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2541da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2551da177e4SLinus Torvalds 
25620e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
25720e61da7SWANG Cong 	if (err) {
25820e61da7SWANG Cong 		in_dev->dead = 1;
25920e61da7SWANG Cong 		in_dev_put(in_dev);
26020e61da7SWANG Cong 		in_dev = NULL;
26120e61da7SWANG Cong 		goto out;
26220e61da7SWANG Cong 	}
2631da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2641da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2651da177e4SLinus Torvalds 		ip_mc_up(in_dev);
266483479ecSJarek Poplawski 
26730c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
268cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
269483479ecSJarek Poplawski out:
27020e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2711da177e4SLinus Torvalds out_kfree:
2721da177e4SLinus Torvalds 	kfree(in_dev);
2731da177e4SLinus Torvalds 	in_dev = NULL;
2741da177e4SLinus Torvalds 	goto out;
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2781da177e4SLinus Torvalds {
2791da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2801da177e4SLinus Torvalds 	in_dev_put(idev);
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2841da177e4SLinus Torvalds {
2851da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2861da177e4SLinus Torvalds 	struct net_device *dev;
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	ASSERT_RTNL();
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	dev = in_dev->dev;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	in_dev->dead = 1;
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2971da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2981da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2991da177e4SLinus Torvalds 	}
3001da177e4SLinus Torvalds 
301a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3021da177e4SLinus Torvalds 
30351602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3041da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3051da177e4SLinus Torvalds 	arp_ifdown(dev);
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3081da177e4SLinus Torvalds }
3091da177e4SLinus Torvalds 
310ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3111da177e4SLinus Torvalds {
3121da177e4SLinus Torvalds 	rcu_read_lock();
3131da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3141da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3151da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3161da177e4SLinus Torvalds 				rcu_read_unlock();
3171da177e4SLinus Torvalds 				return 1;
3181da177e4SLinus Torvalds 			}
3191da177e4SLinus Torvalds 		}
3201da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3211da177e4SLinus Torvalds 	rcu_read_unlock();
3221da177e4SLinus Torvalds 	return 0;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
325d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
32615e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3271da177e4SLinus Torvalds {
3288f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3290ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3300ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3310ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3320ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	ASSERT_RTNL();
3351da177e4SLinus Torvalds 
336fbd40ea0SDavid S. Miller 	if (in_dev->dead)
337fbd40ea0SDavid S. Miller 		goto no_promotions;
338fbd40ea0SDavid S. Miller 
3398f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3408f937c60SHarald Welte 	 * unless alias promotion is set
3418f937c60SHarald Welte 	 **/
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3441da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3470ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3480ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3490ff60a45SJamal Hadi Salim 				last_prim = ifa;
3500ff60a45SJamal Hadi Salim 
3511da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3521da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3531da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3541da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3550ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3561da177e4SLinus Torvalds 				continue;
3571da177e4SLinus Torvalds 			}
3581da177e4SLinus Torvalds 
3590ff60a45SJamal Hadi Salim 			if (!do_promote) {
360fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3611da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3621da177e4SLinus Torvalds 
36315e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
364e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
365e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3661da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3678f937c60SHarald Welte 			} else {
3688f937c60SHarald Welte 				promote = ifa;
3698f937c60SHarald Welte 				break;
3708f937c60SHarald Welte 			}
3711da177e4SLinus Torvalds 		}
3721da177e4SLinus Torvalds 	}
3731da177e4SLinus Torvalds 
3742d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3752d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3762d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3772d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3782d230e2bSJulian Anastasov 	 */
3792d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3802d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3812d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3822d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3832d230e2bSJulian Anastasov 	}
3842d230e2bSJulian Anastasov 
385fbd40ea0SDavid S. Miller no_promotions:
3861da177e4SLinus Torvalds 	/* 2. Unlink it */
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
389fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3941da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3951da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3961da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3971da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3981da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3991da177e4SLinus Torvalds 	   So that, this order is correct.
4001da177e4SLinus Torvalds 	 */
40115e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
402e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4030ff60a45SJamal Hadi Salim 
4040ff60a45SJamal Hadi Salim 	if (promote) {
40504024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
4060ff60a45SJamal Hadi Salim 
4070ff60a45SJamal Hadi Salim 		if (prev_prom) {
4080ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
4090ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
4100ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4110ff60a45SJamal Hadi Salim 		}
4120ff60a45SJamal Hadi Salim 
4130ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
41415e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
415e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
416e041c683SAlan Stern 				NETDEV_UP, promote);
41704024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4180ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4190ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4200ff60a45SJamal Hadi Salim 					continue;
4210ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4220ff60a45SJamal Hadi Salim 		}
4230ff60a45SJamal Hadi Salim 
4240ff60a45SJamal Hadi Salim 	}
4256363097cSHerbert Xu 	if (destroy)
4261da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds 
429d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
430d6062cbbSThomas Graf 			 int destroy)
431d6062cbbSThomas Graf {
432d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
433d6062cbbSThomas Graf }
434d6062cbbSThomas Graf 
4355c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4365c766d64SJiri Pirko 
4375c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4385c766d64SJiri Pirko 
439d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
44015e47304SEric W. Biederman 			     u32 portid)
4411da177e4SLinus Torvalds {
4421da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4431da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	ASSERT_RTNL();
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4481da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4491da177e4SLinus Torvalds 		return 0;
4501da177e4SLinus Torvalds 	}
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4531da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4561da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4571da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4581da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4591da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4601da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4611da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4621da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4631da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4641da177e4SLinus Torvalds 				return -EEXIST;
4651da177e4SLinus Torvalds 			}
4661da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4671da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4681da177e4SLinus Torvalds 				return -EINVAL;
4691da177e4SLinus Torvalds 			}
4701da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4711da177e4SLinus Torvalds 		}
4721da177e4SLinus Torvalds 	}
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
47563862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
4761da177e4SLinus Torvalds 		ifap = last_primary;
4771da177e4SLinus Torvalds 	}
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4801da177e4SLinus Torvalds 	*ifap = ifa;
4811da177e4SLinus Torvalds 
482fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
483fd23c3b3SDavid S. Miller 
4845c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
485906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
4865c766d64SJiri Pirko 
4871da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4881da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4891da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
49015e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
491e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	return 0;
4941da177e4SLinus Torvalds }
4951da177e4SLinus Torvalds 
496d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
497d6062cbbSThomas Graf {
498d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
499d6062cbbSThomas Graf }
500d6062cbbSThomas Graf 
5011da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5021da177e4SLinus Torvalds {
503e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	ASSERT_RTNL();
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	if (!in_dev) {
5081da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5091da177e4SLinus Torvalds 		return -ENOBUFS;
5101da177e4SLinus Torvalds 	}
51171e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5121d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5131da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
514547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5151da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5161da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5171da177e4SLinus Torvalds 	}
518f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5191da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5201da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5211da177e4SLinus Torvalds }
5221da177e4SLinus Torvalds 
5238723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5248723e1b4SEric Dumazet  * We dont take a reference on found in_device
5258723e1b4SEric Dumazet  */
5267fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5271da177e4SLinus Torvalds {
5281da177e4SLinus Torvalds 	struct net_device *dev;
5291da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
530c148fc2eSEric Dumazet 
531c148fc2eSEric Dumazet 	rcu_read_lock();
532c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5331da177e4SLinus Torvalds 	if (dev)
5348723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
535c148fc2eSEric Dumazet 	rcu_read_unlock();
5361da177e4SLinus Torvalds 	return in_dev;
5371da177e4SLinus Torvalds }
5389f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5411da177e4SLinus Torvalds 
54260cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
54360cad5daSAl Viro 				    __be32 mask)
5441da177e4SLinus Torvalds {
5451da177e4SLinus Torvalds 	ASSERT_RTNL();
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5481da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5491da177e4SLinus Torvalds 			return ifa;
5501da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5511da177e4SLinus Torvalds 	return NULL;
5521da177e4SLinus Torvalds }
5531da177e4SLinus Torvalds 
55493a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
55593a714d6SMadhu Challa {
55693a714d6SMadhu Challa 	struct ip_mreqn mreq = {
55793a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
55893a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
55993a714d6SMadhu Challa 	};
56093a714d6SMadhu Challa 	int ret;
56193a714d6SMadhu Challa 
56293a714d6SMadhu Challa 	ASSERT_RTNL();
56393a714d6SMadhu Challa 
56493a714d6SMadhu Challa 	lock_sock(sk);
56593a714d6SMadhu Challa 	if (join)
56654ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
56793a714d6SMadhu Challa 	else
56854ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
56993a714d6SMadhu Challa 	release_sock(sk);
57093a714d6SMadhu Challa 
57193a714d6SMadhu Challa 	return ret;
57293a714d6SMadhu Challa }
57393a714d6SMadhu Challa 
574661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
5751da177e4SLinus Torvalds {
5763b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
577dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5781da177e4SLinus Torvalds 	struct in_device *in_dev;
579dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5801da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
581dfdd5fd4SThomas Graf 	int err = -EINVAL;
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 	ASSERT_RTNL();
5841da177e4SLinus Torvalds 
585dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
586dfdd5fd4SThomas Graf 	if (err < 0)
587dfdd5fd4SThomas Graf 		goto errout;
588dfdd5fd4SThomas Graf 
589dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5907fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
59151456b29SIan Morris 	if (!in_dev) {
592dfdd5fd4SThomas Graf 		err = -ENODEV;
593dfdd5fd4SThomas Graf 		goto errout;
594dfdd5fd4SThomas Graf 	}
595dfdd5fd4SThomas Graf 
5961da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5971da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
598dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
59967b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6001da177e4SLinus Torvalds 			continue;
601dfdd5fd4SThomas Graf 
602dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
603dfdd5fd4SThomas Graf 			continue;
604dfdd5fd4SThomas Graf 
605dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
606dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
60767b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
608dfdd5fd4SThomas Graf 			continue;
609dfdd5fd4SThomas Graf 
61093a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
61193a714d6SMadhu Challa 			ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
61215e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6131da177e4SLinus Torvalds 		return 0;
6141da177e4SLinus Torvalds 	}
615dfdd5fd4SThomas Graf 
616dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
617dfdd5fd4SThomas Graf errout:
618dfdd5fd4SThomas Graf 	return err;
6191da177e4SLinus Torvalds }
6201da177e4SLinus Torvalds 
6215c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6225c766d64SJiri Pirko 
6235c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6245c766d64SJiri Pirko {
6255c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6265c766d64SJiri Pirko 	struct in_ifaddr *ifa;
627c988d1e8SJiri Pirko 	struct hlist_node *n;
6285c766d64SJiri Pirko 	int i;
6295c766d64SJiri Pirko 
6305c766d64SJiri Pirko 	now = jiffies;
6315c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6325c766d64SJiri Pirko 
6335c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
634c988d1e8SJiri Pirko 		bool change_needed = false;
635c988d1e8SJiri Pirko 
636c988d1e8SJiri Pirko 		rcu_read_lock();
637b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6385c766d64SJiri Pirko 			unsigned long age;
6395c766d64SJiri Pirko 
6405c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6415c766d64SJiri Pirko 				continue;
6425c766d64SJiri Pirko 
6435c766d64SJiri Pirko 			/* We try to batch several events at once. */
6445c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6455c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6465c766d64SJiri Pirko 
6475c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6485c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
649c988d1e8SJiri Pirko 				change_needed = true;
650c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
651c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
652c988d1e8SJiri Pirko 				continue;
653c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
654c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
655c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
656c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
657c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
658c988d1e8SJiri Pirko 
659c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
660c988d1e8SJiri Pirko 					change_needed = true;
661c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
662c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
663c988d1e8SJiri Pirko 					       next)) {
664c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
665c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
666c988d1e8SJiri Pirko 			}
667c988d1e8SJiri Pirko 		}
668c988d1e8SJiri Pirko 		rcu_read_unlock();
669c988d1e8SJiri Pirko 		if (!change_needed)
670c988d1e8SJiri Pirko 			continue;
671c988d1e8SJiri Pirko 		rtnl_lock();
672c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
673c988d1e8SJiri Pirko 			unsigned long age;
674c988d1e8SJiri Pirko 
675c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
676c988d1e8SJiri Pirko 				continue;
677c988d1e8SJiri Pirko 
678c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
679c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
680c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
681c988d1e8SJiri Pirko 
682c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
683c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
6845c766d64SJiri Pirko 				struct in_ifaddr **ifap;
6855c766d64SJiri Pirko 
6865c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
687c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
688c988d1e8SJiri Pirko 					if (*ifap == ifa) {
6895c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
6905c766d64SJiri Pirko 							     ifap, 1);
691c988d1e8SJiri Pirko 						break;
6925c766d64SJiri Pirko 					}
693c988d1e8SJiri Pirko 				}
694c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
695c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
696c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
697c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
6985c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
6995c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7005c766d64SJiri Pirko 			}
7015c766d64SJiri Pirko 		}
702c988d1e8SJiri Pirko 		rtnl_unlock();
7035c766d64SJiri Pirko 	}
7045c766d64SJiri Pirko 
7055c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7065c766d64SJiri Pirko 	next_sched = next;
7075c766d64SJiri Pirko 
7085c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7095c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7105c766d64SJiri Pirko 		next_sched = next_sec;
7115c766d64SJiri Pirko 
7125c766d64SJiri Pirko 	now = jiffies;
7135c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7145c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7155c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7165c766d64SJiri Pirko 
717906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
718906e073fSviresh kumar 			next_sched - now);
7195c766d64SJiri Pirko }
7205c766d64SJiri Pirko 
7215c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7225c766d64SJiri Pirko 			     __u32 prefered_lft)
7235c766d64SJiri Pirko {
7245c766d64SJiri Pirko 	unsigned long timeout;
7255c766d64SJiri Pirko 
7265c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
7275c766d64SJiri Pirko 
7285c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
7295c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
7305c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7315c766d64SJiri Pirko 	else
7325c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
7335c766d64SJiri Pirko 
7345c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
7355c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
7365c766d64SJiri Pirko 		if (timeout == 0)
7375c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7385c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7395c766d64SJiri Pirko 	}
7405c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7415c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7425c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7435c766d64SJiri Pirko }
7445c766d64SJiri Pirko 
7455c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
7465c766d64SJiri Pirko 				       __u32 *pvalid_lft, __u32 *pprefered_lft)
7471da177e4SLinus Torvalds {
7485c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7495c753978SThomas Graf 	struct in_ifaddr *ifa;
7505c753978SThomas Graf 	struct ifaddrmsg *ifm;
7511da177e4SLinus Torvalds 	struct net_device *dev;
7521da177e4SLinus Torvalds 	struct in_device *in_dev;
7537b218574SDenis V. Lunev 	int err;
7541da177e4SLinus Torvalds 
7555c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
7565c753978SThomas Graf 	if (err < 0)
7575c753978SThomas Graf 		goto errout;
7581da177e4SLinus Torvalds 
7595c753978SThomas Graf 	ifm = nlmsg_data(nlh);
760c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
76151456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
7625c753978SThomas Graf 		goto errout;
7631da177e4SLinus Torvalds 
7644b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
7655c753978SThomas Graf 	err = -ENODEV;
76651456b29SIan Morris 	if (!dev)
7675c753978SThomas Graf 		goto errout;
7681da177e4SLinus Torvalds 
7695c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
7705c753978SThomas Graf 	err = -ENOBUFS;
77151456b29SIan Morris 	if (!in_dev)
7725c753978SThomas Graf 		goto errout;
77371e27da9SHerbert Xu 
7745c753978SThomas Graf 	ifa = inet_alloc_ifa();
77551456b29SIan Morris 	if (!ifa)
7765c753978SThomas Graf 		/*
7775c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
7785c753978SThomas Graf 		 * assigned to its device and is destroy with it.
7795c753978SThomas Graf 		 */
7805c753978SThomas Graf 		goto errout;
7815c753978SThomas Graf 
782a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
7831d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
7845c753978SThomas Graf 	in_dev_hold(in_dev);
7855c753978SThomas Graf 
78651456b29SIan Morris 	if (!tb[IFA_ADDRESS])
7875c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
7885c753978SThomas Graf 
789fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
7901da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
7911da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
792ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
793ad6c8135SJiri Pirko 					 ifm->ifa_flags;
7941da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
7951da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
7965c753978SThomas Graf 
79767b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
79867b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
7995c753978SThomas Graf 
8005c753978SThomas Graf 	if (tb[IFA_BROADCAST])
80167b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8025c753978SThomas Graf 
8035c753978SThomas Graf 	if (tb[IFA_LABEL])
8045c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8051da177e4SLinus Torvalds 	else
8061da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8071da177e4SLinus Torvalds 
8085c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8095c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8105c766d64SJiri Pirko 
8115c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8125c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8135c766d64SJiri Pirko 			err = -EINVAL;
814446266b0SDaniel Borkmann 			goto errout_free;
8155c766d64SJiri Pirko 		}
8165c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8175c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8185c766d64SJiri Pirko 	}
8195c766d64SJiri Pirko 
8205c753978SThomas Graf 	return ifa;
8215c753978SThomas Graf 
822446266b0SDaniel Borkmann errout_free:
823446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8245c753978SThomas Graf errout:
8255c753978SThomas Graf 	return ERR_PTR(err);
8265c753978SThomas Graf }
8275c753978SThomas Graf 
8285c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
8295c766d64SJiri Pirko {
8305c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8315c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8325c766d64SJiri Pirko 
8335c766d64SJiri Pirko 	if (!ifa->ifa_local)
8345c766d64SJiri Pirko 		return NULL;
8355c766d64SJiri Pirko 
8365c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8375c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8385c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8395c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8405c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8415c766d64SJiri Pirko 			return ifa1;
8425c766d64SJiri Pirko 	}
8435c766d64SJiri Pirko 	return NULL;
8445c766d64SJiri Pirko }
8455c766d64SJiri Pirko 
846661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
8475c753978SThomas Graf {
8483b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8495c753978SThomas Graf 	struct in_ifaddr *ifa;
8505c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8515c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8525c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8535c753978SThomas Graf 
8545c753978SThomas Graf 	ASSERT_RTNL();
8555c753978SThomas Graf 
8565c766d64SJiri Pirko 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
8575c753978SThomas Graf 	if (IS_ERR(ifa))
8585c753978SThomas Graf 		return PTR_ERR(ifa);
8595c753978SThomas Graf 
8605c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
8615c766d64SJiri Pirko 	if (!ifa_existing) {
8625c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
863614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
8645c766d64SJiri Pirko 		 */
8655c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
86693a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
86793a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
86893a714d6SMadhu Challa 					       true, ifa);
86993a714d6SMadhu Challa 
87093a714d6SMadhu Challa 			if (ret < 0) {
87193a714d6SMadhu Challa 				inet_free_ifa(ifa);
87293a714d6SMadhu Challa 				return ret;
87393a714d6SMadhu Challa 			}
87493a714d6SMadhu Challa 		}
87515e47304SEric W. Biederman 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
8765c766d64SJiri Pirko 	} else {
8775c766d64SJiri Pirko 		inet_free_ifa(ifa);
8785c766d64SJiri Pirko 
8795c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
8805c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
8815c766d64SJiri Pirko 			return -EEXIST;
88234e2ed34SJiri Pirko 		ifa = ifa_existing;
88334e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
88405a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
885906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
886906e073fSviresh kumar 				&check_lifetime_work, 0);
88734e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
8885c766d64SJiri Pirko 	}
8895c766d64SJiri Pirko 	return 0;
8901da177e4SLinus Torvalds }
8911da177e4SLinus Torvalds 
8921da177e4SLinus Torvalds /*
8931da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
8941da177e4SLinus Torvalds  */
8951da177e4SLinus Torvalds 
89640384999SEric Dumazet static int inet_abc_len(__be32 addr)
8971da177e4SLinus Torvalds {
8981da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
8991da177e4SLinus Torvalds 
900f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
9011da177e4SLinus Torvalds 		rc = 0;
9021da177e4SLinus Torvalds 	else {
903714e85beSAl Viro 		__u32 haddr = ntohl(addr);
9041da177e4SLinus Torvalds 
905714e85beSAl Viro 		if (IN_CLASSA(haddr))
9061da177e4SLinus Torvalds 			rc = 8;
907714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9081da177e4SLinus Torvalds 			rc = 16;
909714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9101da177e4SLinus Torvalds 			rc = 24;
9111da177e4SLinus Torvalds 	}
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 	return rc;
9141da177e4SLinus Torvalds }
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds 
917e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
9181da177e4SLinus Torvalds {
9191da177e4SLinus Torvalds 	struct ifreq ifr;
9201da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
9211da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
9221da177e4SLinus Torvalds 	struct in_device *in_dev;
9231da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
9241da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
9251da177e4SLinus Torvalds 	struct net_device *dev;
9261da177e4SLinus Torvalds 	char *colon;
9271da177e4SLinus Torvalds 	int ret = -EFAULT;
9281da177e4SLinus Torvalds 	int tryaddrmatch = 0;
9291da177e4SLinus Torvalds 
9301da177e4SLinus Torvalds 	/*
9311da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
9321da177e4SLinus Torvalds 	 */
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
9351da177e4SLinus Torvalds 		goto out;
9361da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 	/* save original address for comparison */
9391da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9401da177e4SLinus Torvalds 
9411da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
9421da177e4SLinus Torvalds 	if (colon)
9431da177e4SLinus Torvalds 		*colon = 0;
9441da177e4SLinus Torvalds 
945e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds 	switch (cmd) {
9481da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9491da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9501da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9511da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9521da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
9531da177e4SLinus Torvalds 		   so that we do not impose a lock.
9541da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
9551da177e4SLinus Torvalds 		 */
9561da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
9571da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
9581da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
9591da177e4SLinus Torvalds 		break;
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
962bf5b30b8SZhao Hongjiang 		ret = -EPERM;
96352e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9641da177e4SLinus Torvalds 			goto out;
9651da177e4SLinus Torvalds 		break;
9661da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
9671da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
9681da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
9691da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
970bf5b30b8SZhao Hongjiang 		ret = -EPERM;
97152e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9721da177e4SLinus Torvalds 			goto out;
9731da177e4SLinus Torvalds 		ret = -EINVAL;
9741da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
9751da177e4SLinus Torvalds 			goto out;
9761da177e4SLinus Torvalds 		break;
9771da177e4SLinus Torvalds 	default:
9781da177e4SLinus Torvalds 		ret = -EINVAL;
9791da177e4SLinus Torvalds 		goto out;
9801da177e4SLinus Torvalds 	}
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 	rtnl_lock();
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	ret = -ENODEV;
9859f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
9869f9354b9SEric Dumazet 	if (!dev)
9871da177e4SLinus Torvalds 		goto done;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 	if (colon)
9901da177e4SLinus Torvalds 		*colon = ':';
9911da177e4SLinus Torvalds 
9929f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
9939f9354b9SEric Dumazet 	if (in_dev) {
9941da177e4SLinus Torvalds 		if (tryaddrmatch) {
9951da177e4SLinus Torvalds 			/* Matthias Andree */
9961da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
9971da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
9981da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
9991da177e4SLinus Torvalds 			   This is checked above. */
10001da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10011da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
10021da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
10031da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10046c91afe1SDavid S. Miller 							ifa->ifa_local) {
10051da177e4SLinus Torvalds 					break; /* found */
10061da177e4SLinus Torvalds 				}
10071da177e4SLinus Torvalds 			}
10081da177e4SLinus Torvalds 		}
10091da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10101da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10111da177e4SLinus Torvalds 		   comparing just the label */
10121da177e4SLinus Torvalds 		if (!ifa) {
10131da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10141da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
10151da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
10161da177e4SLinus Torvalds 					break;
10171da177e4SLinus Torvalds 		}
10181da177e4SLinus Torvalds 	}
10191da177e4SLinus Torvalds 
10201da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10211da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
10221da177e4SLinus Torvalds 		goto done;
10231da177e4SLinus Torvalds 
10241da177e4SLinus Torvalds 	switch (cmd) {
10251da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10261da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
10271da177e4SLinus Torvalds 		goto rarok;
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10301da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
10311da177e4SLinus Torvalds 		goto rarok;
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10341da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
10351da177e4SLinus Torvalds 		goto rarok;
10361da177e4SLinus Torvalds 
10371da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10381da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
10391da177e4SLinus Torvalds 		goto rarok;
10401da177e4SLinus Torvalds 
10411da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10421da177e4SLinus Torvalds 		if (colon) {
10431da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
10441da177e4SLinus Torvalds 			if (!ifa)
10451da177e4SLinus Torvalds 				break;
10461da177e4SLinus Torvalds 			ret = 0;
10471da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
10481da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
10491da177e4SLinus Torvalds 			break;
10501da177e4SLinus Torvalds 		}
10511da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
10521da177e4SLinus Torvalds 		break;
10531da177e4SLinus Torvalds 
10541da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10551da177e4SLinus Torvalds 		ret = -EINVAL;
10561da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10571da177e4SLinus Torvalds 			break;
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds 		if (!ifa) {
10601da177e4SLinus Torvalds 			ret = -ENOBUFS;
10619f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
10629f9354b9SEric Dumazet 			if (!ifa)
10631da177e4SLinus Torvalds 				break;
1064c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
10651da177e4SLinus Torvalds 			if (colon)
10661da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
10671da177e4SLinus Torvalds 			else
10681da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
10691da177e4SLinus Torvalds 		} else {
10701da177e4SLinus Torvalds 			ret = 0;
10711da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
10721da177e4SLinus Torvalds 				break;
10731da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10741da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1075148f9729SBjorn Mork 			ifa->ifa_scope = 0;
10761da177e4SLinus Torvalds 		}
10771da177e4SLinus Torvalds 
10781da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
10811da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
10821da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
10831da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10841da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
10851da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
10861da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
10871da177e4SLinus Torvalds 		} else {
10881da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
10891da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
10901da177e4SLinus Torvalds 		}
10915c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
10921da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
10931da177e4SLinus Torvalds 		break;
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10961da177e4SLinus Torvalds 		ret = 0;
10971da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
10981da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10991da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11001da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11011da177e4SLinus Torvalds 		}
11021da177e4SLinus Torvalds 		break;
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11051da177e4SLinus Torvalds 		ret = 0;
11061da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11071da177e4SLinus Torvalds 			break;
11081da177e4SLinus Torvalds 		ret = -EINVAL;
11091da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11101da177e4SLinus Torvalds 			break;
11111da177e4SLinus Torvalds 		ret = 0;
11121da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11131da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11141da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11151da177e4SLinus Torvalds 		break;
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 		/*
11201da177e4SLinus Torvalds 		 *	The mask we set must be legal.
11211da177e4SLinus Torvalds 		 */
11221da177e4SLinus Torvalds 		ret = -EINVAL;
11231da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
11241da177e4SLinus Torvalds 			break;
11251da177e4SLinus Torvalds 		ret = 0;
11261da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1127a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
11281da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11291da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
11301da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 			/* See if current broadcast address matches
11331da177e4SLinus Torvalds 			 * with current netmask, then recalculate
11341da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
11351da177e4SLinus Torvalds 			 * funny address, so don't touch it since
11361da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
11371da177e4SLinus Torvalds 			 */
11381da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11391da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11401da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1141dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11421da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
11431da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
11441da177e4SLinus Torvalds 			}
11451da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11461da177e4SLinus Torvalds 		}
11471da177e4SLinus Torvalds 		break;
11481da177e4SLinus Torvalds 	}
11491da177e4SLinus Torvalds done:
11501da177e4SLinus Torvalds 	rtnl_unlock();
11511da177e4SLinus Torvalds out:
11521da177e4SLinus Torvalds 	return ret;
11531da177e4SLinus Torvalds rarok:
11541da177e4SLinus Torvalds 	rtnl_unlock();
11551da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
11561da177e4SLinus Torvalds 	goto out;
11571da177e4SLinus Torvalds }
11581da177e4SLinus Torvalds 
11591da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
11601da177e4SLinus Torvalds {
1161e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11621da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
11631da177e4SLinus Torvalds 	struct ifreq ifr;
11641da177e4SLinus Torvalds 	int done = 0;
11651da177e4SLinus Torvalds 
11669f9354b9SEric Dumazet 	if (!in_dev)
11671da177e4SLinus Torvalds 		goto out;
11681da177e4SLinus Torvalds 
11699f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
11701da177e4SLinus Torvalds 		if (!buf) {
11711da177e4SLinus Torvalds 			done += sizeof(ifr);
11721da177e4SLinus Torvalds 			continue;
11731da177e4SLinus Torvalds 		}
11741da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
11751da177e4SLinus Torvalds 			break;
11761da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
11771da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
11781da177e4SLinus Torvalds 
11791da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
11801da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
11811da177e4SLinus Torvalds 								ifa->ifa_local;
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
11841da177e4SLinus Torvalds 			done = -EFAULT;
11851da177e4SLinus Torvalds 			break;
11861da177e4SLinus Torvalds 		}
11871da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
11881da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
11891da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
11901da177e4SLinus Torvalds 	}
11911da177e4SLinus Torvalds out:
11921da177e4SLinus Torvalds 	return done;
11931da177e4SLinus Torvalds }
11941da177e4SLinus Torvalds 
11958b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
11968b57fd1eSGao Feng 				 int scope)
11978b57fd1eSGao Feng {
11988b57fd1eSGao Feng 	for_primary_ifa(in_dev) {
11998b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12008b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12018b57fd1eSGao Feng 			return ifa->ifa_local;
12028b57fd1eSGao Feng 	} endfor_ifa(in_dev);
12038b57fd1eSGao Feng 
12048b57fd1eSGao Feng 	return 0;
12058b57fd1eSGao Feng }
12068b57fd1eSGao Feng 
1207a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
12081da177e4SLinus Torvalds {
1209a61ced5dSAl Viro 	__be32 addr = 0;
12101da177e4SLinus Torvalds 	struct in_device *in_dev;
1211c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12123f2fb9a8SDavid Ahern 	int master_idx;
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 	rcu_read_lock();
1215e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
12161da177e4SLinus Torvalds 	if (!in_dev)
12171da177e4SLinus Torvalds 		goto no_in_dev;
12181da177e4SLinus Torvalds 
12191da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
12201da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
12211da177e4SLinus Torvalds 			continue;
12221da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
12231da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12241da177e4SLinus Torvalds 			break;
12251da177e4SLinus Torvalds 		}
12261da177e4SLinus Torvalds 		if (!addr)
12271da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12281da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds 	if (addr)
1231c6d14c84SEric Dumazet 		goto out_unlock;
12329f9354b9SEric Dumazet no_in_dev:
12333f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
12341da177e4SLinus Torvalds 
123517b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
123617b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
123717b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
123817b693cdSDavid Lamparter 	 * equality check in the loop below.
123917b693cdSDavid Lamparter 	 */
124017b693cdSDavid Lamparter 	if (master_idx &&
124117b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
124217b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
12438b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
12448b57fd1eSGao Feng 		if (addr)
124517b693cdSDavid Lamparter 			goto out_unlock;
124617b693cdSDavid Lamparter 	}
124717b693cdSDavid Lamparter 
12481da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1249ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
12501da177e4SLinus Torvalds 	   in dev_base list.
12511da177e4SLinus Torvalds 	 */
1252c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
12533f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
12543f2fb9a8SDavid Ahern 			continue;
12553f2fb9a8SDavid Ahern 
12569f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
12579f9354b9SEric Dumazet 		if (!in_dev)
12581da177e4SLinus Torvalds 			continue;
12591da177e4SLinus Torvalds 
12608b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
12618b57fd1eSGao Feng 		if (addr)
1262c6d14c84SEric Dumazet 			goto out_unlock;
12631da177e4SLinus Torvalds 	}
1264c6d14c84SEric Dumazet out_unlock:
12651da177e4SLinus Torvalds 	rcu_read_unlock();
12661da177e4SLinus Torvalds 	return addr;
12671da177e4SLinus Torvalds }
12689f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
12691da177e4SLinus Torvalds 
127060cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
127160cad5daSAl Viro 			      __be32 local, int scope)
12721da177e4SLinus Torvalds {
12731da177e4SLinus Torvalds 	int same = 0;
1274a144ea4bSAl Viro 	__be32 addr = 0;
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds 	for_ifa(in_dev) {
12771da177e4SLinus Torvalds 		if (!addr &&
12781da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
12791da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
12801da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12811da177e4SLinus Torvalds 			if (same)
12821da177e4SLinus Torvalds 				break;
12831da177e4SLinus Torvalds 		}
12841da177e4SLinus Torvalds 		if (!same) {
12851da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
12861da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
12871da177e4SLinus Torvalds 			if (same && addr) {
12881da177e4SLinus Torvalds 				if (local || !dst)
12891da177e4SLinus Torvalds 					break;
12901da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
12911da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
12921da177e4SLinus Torvalds 					break;
12931da177e4SLinus Torvalds 				/* No, then can we use new local src? */
12941da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
12951da177e4SLinus Torvalds 					addr = ifa->ifa_local;
12961da177e4SLinus Torvalds 					break;
12971da177e4SLinus Torvalds 				}
12981da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
12991da177e4SLinus Torvalds 				same = 0;
13001da177e4SLinus Torvalds 			}
13011da177e4SLinus Torvalds 		}
13021da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	return same ? addr : 0;
13051da177e4SLinus Torvalds }
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds /*
13081da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1309b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1310b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
13111da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
13121da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
13131da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
13141da177e4SLinus Torvalds  */
1315b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
13169bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
13171da177e4SLinus Torvalds {
131860cad5daSAl Viro 	__be32 addr = 0;
13199bd85e32SDenis V. Lunev 	struct net_device *dev;
13201da177e4SLinus Torvalds 
132100db4124SIan Morris 	if (in_dev)
13229bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
13231da177e4SLinus Torvalds 
13241da177e4SLinus Torvalds 	rcu_read_lock();
1325c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13269f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13279f9354b9SEric Dumazet 		if (in_dev) {
13281da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
13291da177e4SLinus Torvalds 			if (addr)
13301da177e4SLinus Torvalds 				break;
13311da177e4SLinus Torvalds 		}
13321da177e4SLinus Torvalds 	}
13331da177e4SLinus Torvalds 	rcu_read_unlock();
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 	return addr;
13361da177e4SLinus Torvalds }
1337eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds /*
13401da177e4SLinus Torvalds  *	Device notifier
13411da177e4SLinus Torvalds  */
13421da177e4SLinus Torvalds 
13431da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
13441da177e4SLinus Torvalds {
1345e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
13461da177e4SLinus Torvalds }
13479f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
13481da177e4SLinus Torvalds 
13491da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
13501da177e4SLinus Torvalds {
1351e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
13521da177e4SLinus Torvalds }
13539f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
13541da177e4SLinus Torvalds 
13559f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
13569f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
13571da177e4SLinus Torvalds */
13581da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
13591da177e4SLinus Torvalds {
13601da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
13611da177e4SLinus Torvalds 	int named = 0;
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
13641da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
13651da177e4SLinus Torvalds 
13661da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
13671da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
13681da177e4SLinus Torvalds 		if (named++ == 0)
1369573bf470SThomas Graf 			goto skip;
137044344b2aSMark McLoughlin 		dot = strchr(old, ':');
137151456b29SIan Morris 		if (!dot) {
13721da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
13731da177e4SLinus Torvalds 			dot = old;
13741da177e4SLinus Torvalds 		}
13759f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
13761da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
13779f9354b9SEric Dumazet 		else
13781da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1379573bf470SThomas Graf skip:
1380573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
13811da177e4SLinus Torvalds 	}
13821da177e4SLinus Torvalds }
13831da177e4SLinus Torvalds 
138440384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
138506770843SBreno Leitao {
138606770843SBreno Leitao 	return mtu >= 68;
138706770843SBreno Leitao }
138806770843SBreno Leitao 
1389d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1390d11327adSIan Campbell 					struct in_device *in_dev)
1391d11327adSIan Campbell 
1392d11327adSIan Campbell {
1393b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1394d11327adSIan Campbell 
1395b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1396b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1397d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
13986c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
13996c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1400d11327adSIan Campbell 			 dev->dev_addr, NULL);
1401d11327adSIan Campbell 	}
1402b76d0789SZoltan Kiss }
1403d11327adSIan Campbell 
14041da177e4SLinus Torvalds /* Called only under RTNL semaphore */
14051da177e4SLinus Torvalds 
14061da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
14071da177e4SLinus Torvalds 			 void *ptr)
14081da177e4SLinus Torvalds {
1409351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1410748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
14111da177e4SLinus Torvalds 
14121da177e4SLinus Torvalds 	ASSERT_RTNL();
14131da177e4SLinus Torvalds 
14141da177e4SLinus Torvalds 	if (!in_dev) {
14158030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
14161da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
141720e61da7SWANG Cong 			if (IS_ERR(in_dev))
141820e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
14190cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
142042f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
142142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
14221da177e4SLinus Torvalds 			}
142306770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
142406770843SBreno Leitao 			/* Re-enabling IP */
142506770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
142606770843SBreno Leitao 				in_dev = inetdev_init(dev);
14278030f544SHerbert Xu 		}
14281da177e4SLinus Torvalds 		goto out;
14291da177e4SLinus Torvalds 	}
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds 	switch (event) {
14321da177e4SLinus Torvalds 	case NETDEV_REGISTER:
143391df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1434a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
14351da177e4SLinus Torvalds 		break;
14361da177e4SLinus Torvalds 	case NETDEV_UP:
143706770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
14381da177e4SLinus Torvalds 			break;
14390cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
14409f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
14419f9354b9SEric Dumazet 
14429f9354b9SEric Dumazet 			if (ifa) {
1443fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
14441da177e4SLinus Torvalds 				ifa->ifa_local =
14451da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
14461da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
14471da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
14481da177e4SLinus Torvalds 				in_dev_hold(in_dev);
14491da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
14501da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
14511da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14525c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
14535c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1454dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1455dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
14561da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
14571da177e4SLinus Torvalds 			}
14581da177e4SLinus Torvalds 		}
14591da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1460eefef1cfSStephen Hemminger 		/* fall through */
1461eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1462d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1463d11327adSIan Campbell 			break;
1464d11327adSIan Campbell 		/* fall through */
1465d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1466a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1467d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
14681da177e4SLinus Torvalds 		break;
14691da177e4SLinus Torvalds 	case NETDEV_DOWN:
14701da177e4SLinus Torvalds 		ip_mc_down(in_dev);
14711da177e4SLinus Torvalds 		break;
147293d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
147375c78500SMoni Shoua 		ip_mc_unmap(in_dev);
147475c78500SMoni Shoua 		break;
147593d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
147675c78500SMoni Shoua 		ip_mc_remap(in_dev);
147775c78500SMoni Shoua 		break;
14781da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
147906770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
14801da177e4SLinus Torvalds 			break;
148106770843SBreno Leitao 		/* disable IP when MTU is not enough */
14821da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
14831da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
14841da177e4SLinus Torvalds 		break;
14851da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
14861da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
14871da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
14881da177e4SLinus Torvalds 		 */
14891da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
14901da177e4SLinus Torvalds 
149151602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
149266f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
14931da177e4SLinus Torvalds 		break;
14941da177e4SLinus Torvalds 	}
14951da177e4SLinus Torvalds out:
14961da177e4SLinus Torvalds 	return NOTIFY_DONE;
14971da177e4SLinus Torvalds }
14981da177e4SLinus Torvalds 
14991da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
15001da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
15011da177e4SLinus Torvalds };
15021da177e4SLinus Torvalds 
150340384999SEric Dumazet static size_t inet_nlmsg_size(void)
1504339bf98fSThomas Graf {
1505339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1506339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1507339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1508339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1509ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
151063b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
151163b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1512339bf98fSThomas Graf }
1513339bf98fSThomas Graf 
15145c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
15155c766d64SJiri Pirko {
15165c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
15175c766d64SJiri Pirko }
15185c766d64SJiri Pirko 
15195c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
15205c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
15215c766d64SJiri Pirko {
15225c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
15235c766d64SJiri Pirko 
15245c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
15255c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
15265c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
15275c766d64SJiri Pirko 	ci.ifa_valid = valid;
15285c766d64SJiri Pirko 
15295c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
15305c766d64SJiri Pirko }
15315c766d64SJiri Pirko 
15321da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
153315e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
15341da177e4SLinus Torvalds {
15351da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
15361da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
15375c766d64SJiri Pirko 	u32 preferred, valid;
15381da177e4SLinus Torvalds 
153915e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
154051456b29SIan Morris 	if (!nlh)
154126932566SPatrick McHardy 		return -EMSGSIZE;
154247f68512SThomas Graf 
154347f68512SThomas Graf 	ifm = nlmsg_data(nlh);
15441da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
15451da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
15465c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
15471da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
15481da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
15491da177e4SLinus Torvalds 
15505c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
15515c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
15525c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
15535c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
15545c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
15555c766d64SJiri Pirko 
15565c766d64SJiri Pirko 			if (preferred > tval)
15575c766d64SJiri Pirko 				preferred -= tval;
15585c766d64SJiri Pirko 			else
15595c766d64SJiri Pirko 				preferred = 0;
15605c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
15615c766d64SJiri Pirko 				if (valid > tval)
15625c766d64SJiri Pirko 					valid -= tval;
15635c766d64SJiri Pirko 				else
15645c766d64SJiri Pirko 					valid = 0;
15655c766d64SJiri Pirko 			}
15665c766d64SJiri Pirko 		}
15675c766d64SJiri Pirko 	} else {
15685c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
15695c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
15705c766d64SJiri Pirko 	}
1571f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1572930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1573f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1574930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1575f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1576930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1577f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
15785c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1579ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
15805c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
15815c766d64SJiri Pirko 			  preferred, valid))
1582f3756b79SDavid S. Miller 		goto nla_put_failure;
158347f68512SThomas Graf 
1584053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1585053c095aSJohannes Berg 	return 0;
158647f68512SThomas Graf 
158747f68512SThomas Graf nla_put_failure:
158826932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
158926932566SPatrick McHardy 	return -EMSGSIZE;
15901da177e4SLinus Torvalds }
15911da177e4SLinus Torvalds 
15921da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
15931da177e4SLinus Torvalds {
15943b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1595eec4df98SEric Dumazet 	int h, s_h;
1596eec4df98SEric Dumazet 	int idx, s_idx;
1597eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
15981da177e4SLinus Torvalds 	struct net_device *dev;
15991da177e4SLinus Torvalds 	struct in_device *in_dev;
16001da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1601eec4df98SEric Dumazet 	struct hlist_head *head;
16021da177e4SLinus Torvalds 
1603eec4df98SEric Dumazet 	s_h = cb->args[0];
1604eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1605eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1606eec4df98SEric Dumazet 
1607eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
16087562f876SPavel Emelianov 		idx = 0;
1609eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1610eec4df98SEric Dumazet 		rcu_read_lock();
16110465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
16120465277fSNicolas Dichtel 			  net->dev_base_seq;
1613b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
16141da177e4SLinus Torvalds 			if (idx < s_idx)
16157562f876SPavel Emelianov 				goto cont;
16164b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
16171da177e4SLinus Torvalds 				s_ip_idx = 0;
1618eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
16199f9354b9SEric Dumazet 			if (!in_dev)
16207562f876SPavel Emelianov 				goto cont;
16211da177e4SLinus Torvalds 
16221da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
16231da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
16241da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1625596e4150SStephen Hemminger 					continue;
1626eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
162715e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
16281da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1629053c095aSJohannes Berg 					     RTM_NEWADDR, NLM_F_MULTI) < 0) {
1630eec4df98SEric Dumazet 					rcu_read_unlock();
16311da177e4SLinus Torvalds 					goto done;
16321da177e4SLinus Torvalds 				}
16330465277fSNicolas Dichtel 				nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1634eec4df98SEric Dumazet 			}
16357562f876SPavel Emelianov cont:
16367562f876SPavel Emelianov 			idx++;
16371da177e4SLinus Torvalds 		}
1638eec4df98SEric Dumazet 		rcu_read_unlock();
1639eec4df98SEric Dumazet 	}
16401da177e4SLinus Torvalds 
16411da177e4SLinus Torvalds done:
1642eec4df98SEric Dumazet 	cb->args[0] = h;
1643eec4df98SEric Dumazet 	cb->args[1] = idx;
1644eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
16451da177e4SLinus Torvalds 
16461da177e4SLinus Torvalds 	return skb->len;
16471da177e4SLinus Torvalds }
16481da177e4SLinus Torvalds 
1649d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
165015e47304SEric W. Biederman 		      u32 portid)
16511da177e4SLinus Torvalds {
165247f68512SThomas Graf 	struct sk_buff *skb;
1653d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1654d6062cbbSThomas Graf 	int err = -ENOBUFS;
16554b8aa9abSDenis V. Lunev 	struct net *net;
16561da177e4SLinus Torvalds 
1657c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1658339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
165951456b29SIan Morris 	if (!skb)
1660d6062cbbSThomas Graf 		goto errout;
1661d6062cbbSThomas Graf 
166215e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
166326932566SPatrick McHardy 	if (err < 0) {
166426932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
166526932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
166626932566SPatrick McHardy 		kfree_skb(skb);
166726932566SPatrick McHardy 		goto errout;
166826932566SPatrick McHardy 	}
166915e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
16701ce85fe4SPablo Neira Ayuso 	return;
1671d6062cbbSThomas Graf errout:
1672d6062cbbSThomas Graf 	if (err < 0)
16734b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
16741da177e4SLinus Torvalds }
16751da177e4SLinus Torvalds 
1676b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1677b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
16789f0f7272SThomas Graf {
16791fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16809f0f7272SThomas Graf 
16819f0f7272SThomas Graf 	if (!in_dev)
16829f0f7272SThomas Graf 		return 0;
16839f0f7272SThomas Graf 
16849f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
16859f0f7272SThomas Graf }
16869f0f7272SThomas Graf 
1687d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1688d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
16899f0f7272SThomas Graf {
16901fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16919f0f7272SThomas Graf 	struct nlattr *nla;
16929f0f7272SThomas Graf 	int i;
16939f0f7272SThomas Graf 
16949f0f7272SThomas Graf 	if (!in_dev)
16959f0f7272SThomas Graf 		return -ENODATA;
16969f0f7272SThomas Graf 
16979f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
169851456b29SIan Morris 	if (!nla)
16999f0f7272SThomas Graf 		return -EMSGSIZE;
17009f0f7272SThomas Graf 
17019f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
17029f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
17039f0f7272SThomas Graf 
17049f0f7272SThomas Graf 	return 0;
17059f0f7272SThomas Graf }
17069f0f7272SThomas Graf 
17079f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
17089f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
17099f0f7272SThomas Graf };
17109f0f7272SThomas Graf 
1711cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1712cf7afbfeSThomas Graf 				 const struct nlattr *nla)
17139f0f7272SThomas Graf {
17149f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
17159f0f7272SThomas Graf 	int err, rem;
17169f0f7272SThomas Graf 
1717f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1718cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
17199f0f7272SThomas Graf 
17209f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
17219f0f7272SThomas Graf 	if (err < 0)
17229f0f7272SThomas Graf 		return err;
17239f0f7272SThomas Graf 
17249f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
17259f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
17269f0f7272SThomas Graf 			int cfgid = nla_type(a);
17279f0f7272SThomas Graf 
17289f0f7272SThomas Graf 			if (nla_len(a) < 4)
17299f0f7272SThomas Graf 				return -EINVAL;
17309f0f7272SThomas Graf 
17319f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
17329f0f7272SThomas Graf 				return -EINVAL;
17339f0f7272SThomas Graf 		}
17349f0f7272SThomas Graf 	}
17359f0f7272SThomas Graf 
1736cf7afbfeSThomas Graf 	return 0;
1737cf7afbfeSThomas Graf }
1738cf7afbfeSThomas Graf 
1739cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1740cf7afbfeSThomas Graf {
1741f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1742cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1743cf7afbfeSThomas Graf 	int rem;
1744cf7afbfeSThomas Graf 
1745cf7afbfeSThomas Graf 	if (!in_dev)
1746cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1747cf7afbfeSThomas Graf 
1748cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1749cf7afbfeSThomas Graf 		BUG();
1750cf7afbfeSThomas Graf 
17519f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
17529f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
17539f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
17549f0f7272SThomas Graf 	}
17559f0f7272SThomas Graf 
17569f0f7272SThomas Graf 	return 0;
17579f0f7272SThomas Graf }
17589f0f7272SThomas Graf 
1759edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1760edc9e748SNicolas Dichtel {
1761edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1762edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1763136ba622SZhang Shengju 	bool all = false;
1764edc9e748SNicolas Dichtel 
1765136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1766136ba622SZhang Shengju 		all = true;
1767136ba622SZhang Shengju 
1768136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
1769edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1770136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
1771cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1772136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
1773d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
1774136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
1775f085ff1cSstephen hemminger 		size += nla_total_size(4);
1776136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
1777974d7af5SAndy Gospodarek 		size += nla_total_size(4);
1778edc9e748SNicolas Dichtel 
1779edc9e748SNicolas Dichtel 	return size;
1780edc9e748SNicolas Dichtel }
1781edc9e748SNicolas Dichtel 
1782edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1783edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1784edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1785edc9e748SNicolas Dichtel 				     int type)
1786edc9e748SNicolas Dichtel {
1787edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1788edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1789136ba622SZhang Shengju 	bool all = false;
1790edc9e748SNicolas Dichtel 
1791edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1792edc9e748SNicolas Dichtel 			flags);
179351456b29SIan Morris 	if (!nlh)
1794edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1795edc9e748SNicolas Dichtel 
1796136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1797136ba622SZhang Shengju 		all = true;
1798136ba622SZhang Shengju 
1799edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1800edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1801edc9e748SNicolas Dichtel 
1802edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1803edc9e748SNicolas Dichtel 		goto nla_put_failure;
1804edc9e748SNicolas Dichtel 
1805*b5c9641dSDavid Ahern 	if (!devconf)
1806*b5c9641dSDavid Ahern 		goto out;
1807*b5c9641dSDavid Ahern 
1808136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
1809edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1810edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1811edc9e748SNicolas Dichtel 		goto nla_put_failure;
1812136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
1813cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1814cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1815cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1816136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
1817d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1818d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1819d67b8c61SNicolas Dichtel 		goto nla_put_failure;
1820136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
182109aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
1822f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
1823f085ff1cSstephen hemminger 		goto nla_put_failure;
1824136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
1825974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
1826974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
1827974d7af5SAndy Gospodarek 		goto nla_put_failure;
1828edc9e748SNicolas Dichtel 
1829*b5c9641dSDavid Ahern out:
1830053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1831053c095aSJohannes Berg 	return 0;
1832edc9e748SNicolas Dichtel 
1833edc9e748SNicolas Dichtel nla_put_failure:
1834edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1835edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1836edc9e748SNicolas Dichtel }
1837edc9e748SNicolas Dichtel 
18383b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
18393b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
1840edc9e748SNicolas Dichtel {
1841edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1842edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1843edc9e748SNicolas Dichtel 
1844fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
184551456b29SIan Morris 	if (!skb)
1846edc9e748SNicolas Dichtel 		goto errout;
1847edc9e748SNicolas Dichtel 
1848edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
18493b022865SDavid Ahern 					event, 0, type);
1850edc9e748SNicolas Dichtel 	if (err < 0) {
1851edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1852edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1853edc9e748SNicolas Dichtel 		kfree_skb(skb);
1854edc9e748SNicolas Dichtel 		goto errout;
1855edc9e748SNicolas Dichtel 	}
1856fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
1857edc9e748SNicolas Dichtel 	return;
1858edc9e748SNicolas Dichtel errout:
1859edc9e748SNicolas Dichtel 	if (err < 0)
1860edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1861edc9e748SNicolas Dichtel }
1862edc9e748SNicolas Dichtel 
18639e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
18649e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
18659e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1866cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
186709aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
1868974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
18699e551110SNicolas Dichtel };
18709e551110SNicolas Dichtel 
18719e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
1872661d2967SThomas Graf 				    struct nlmsghdr *nlh)
18739e551110SNicolas Dichtel {
18749e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
18759e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
18769e551110SNicolas Dichtel 	struct netconfmsg *ncm;
18779e551110SNicolas Dichtel 	struct sk_buff *skb;
18789e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
18799e551110SNicolas Dichtel 	struct in_device *in_dev;
18809e551110SNicolas Dichtel 	struct net_device *dev;
18819e551110SNicolas Dichtel 	int ifindex;
18829e551110SNicolas Dichtel 	int err;
18839e551110SNicolas Dichtel 
18849e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
18859e551110SNicolas Dichtel 			  devconf_ipv4_policy);
18869e551110SNicolas Dichtel 	if (err < 0)
18879e551110SNicolas Dichtel 		goto errout;
18889e551110SNicolas Dichtel 
1889a97eb33fSAnton Protopopov 	err = -EINVAL;
18909e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
18919e551110SNicolas Dichtel 		goto errout;
18929e551110SNicolas Dichtel 
18939e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
18949e551110SNicolas Dichtel 	switch (ifindex) {
18959e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
18969e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
18979e551110SNicolas Dichtel 		break;
18989e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
18999e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
19009e551110SNicolas Dichtel 		break;
19019e551110SNicolas Dichtel 	default:
19029e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
190351456b29SIan Morris 		if (!dev)
19049e551110SNicolas Dichtel 			goto errout;
19059e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
190651456b29SIan Morris 		if (!in_dev)
19079e551110SNicolas Dichtel 			goto errout;
19089e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
19099e551110SNicolas Dichtel 		break;
19109e551110SNicolas Dichtel 	}
19119e551110SNicolas Dichtel 
19129e551110SNicolas Dichtel 	err = -ENOBUFS;
1913fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
191451456b29SIan Morris 	if (!skb)
19159e551110SNicolas Dichtel 		goto errout;
19169e551110SNicolas Dichtel 
19179e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
19189e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
19199e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
1920136ba622SZhang Shengju 					NETCONFA_ALL);
19219e551110SNicolas Dichtel 	if (err < 0) {
19229e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
19239e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
19249e551110SNicolas Dichtel 		kfree_skb(skb);
19259e551110SNicolas Dichtel 		goto errout;
19269e551110SNicolas Dichtel 	}
19279e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
19289e551110SNicolas Dichtel errout:
19299e551110SNicolas Dichtel 	return err;
19309e551110SNicolas Dichtel }
19319e551110SNicolas Dichtel 
19327a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
19337a674200SNicolas Dichtel 				     struct netlink_callback *cb)
19347a674200SNicolas Dichtel {
19357a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
19367a674200SNicolas Dichtel 	int h, s_h;
19377a674200SNicolas Dichtel 	int idx, s_idx;
19387a674200SNicolas Dichtel 	struct net_device *dev;
19397a674200SNicolas Dichtel 	struct in_device *in_dev;
19407a674200SNicolas Dichtel 	struct hlist_head *head;
19417a674200SNicolas Dichtel 
19427a674200SNicolas Dichtel 	s_h = cb->args[0];
19437a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
19447a674200SNicolas Dichtel 
19457a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
19467a674200SNicolas Dichtel 		idx = 0;
19477a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
19487a674200SNicolas Dichtel 		rcu_read_lock();
19490465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
19500465277fSNicolas Dichtel 			  net->dev_base_seq;
19517a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
19527a674200SNicolas Dichtel 			if (idx < s_idx)
19537a674200SNicolas Dichtel 				goto cont;
19547a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
19557a674200SNicolas Dichtel 			if (!in_dev)
19567a674200SNicolas Dichtel 				goto cont;
19577a674200SNicolas Dichtel 
19587a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
19597a674200SNicolas Dichtel 						      &in_dev->cnf,
19607a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
19617a674200SNicolas Dichtel 						      cb->nlh->nlmsg_seq,
19627a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
19637a674200SNicolas Dichtel 						      NLM_F_MULTI,
1964136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
19657a674200SNicolas Dichtel 				rcu_read_unlock();
19667a674200SNicolas Dichtel 				goto done;
19677a674200SNicolas Dichtel 			}
19680465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
19697a674200SNicolas Dichtel cont:
19707a674200SNicolas Dichtel 			idx++;
19717a674200SNicolas Dichtel 		}
19727a674200SNicolas Dichtel 		rcu_read_unlock();
19737a674200SNicolas Dichtel 	}
19747a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
19757a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
19767a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
19777a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
19787a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
19797a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
1980136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
19817a674200SNicolas Dichtel 			goto done;
19827a674200SNicolas Dichtel 		else
19837a674200SNicolas Dichtel 			h++;
19847a674200SNicolas Dichtel 	}
19857a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
19867a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
19877a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
19887a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
19897a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
19907a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
1991136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
19927a674200SNicolas Dichtel 			goto done;
19937a674200SNicolas Dichtel 		else
19947a674200SNicolas Dichtel 			h++;
19957a674200SNicolas Dichtel 	}
19967a674200SNicolas Dichtel done:
19977a674200SNicolas Dichtel 	cb->args[0] = h;
19987a674200SNicolas Dichtel 	cb->args[1] = idx;
19997a674200SNicolas Dichtel 
20007a674200SNicolas Dichtel 	return skb->len;
20017a674200SNicolas Dichtel }
20027a674200SNicolas Dichtel 
20031da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20041da177e4SLinus Torvalds 
2005c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
200631be3085SHerbert Xu {
200731be3085SHerbert Xu 	struct net_device *dev;
200831be3085SHerbert Xu 
200931be3085SHerbert Xu 	rcu_read_lock();
2010c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2011c6d14c84SEric Dumazet 		struct in_device *in_dev;
2012c6d14c84SEric Dumazet 
201331be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
201431be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
20159355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2016c6d14c84SEric Dumazet 	}
201731be3085SHerbert Xu 	rcu_read_unlock();
201831be3085SHerbert Xu }
201931be3085SHerbert Xu 
2020c6d14c84SEric Dumazet /* called with RTNL locked */
2021c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
202268dd299bSPavel Emelyanov {
202368dd299bSPavel Emelyanov 	struct net_device *dev;
2024586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
202568dd299bSPavel Emelyanov 
2026586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
20279355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
20283b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
20293b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2030edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2031edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
20323b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
20333b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2034edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2035edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
203668dd299bSPavel Emelyanov 
2037c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
203868dd299bSPavel Emelyanov 		struct in_device *in_dev;
2039fa17806cSEric Dumazet 
20400187bdfbSBen Hutchings 		if (on)
20410187bdfbSBen Hutchings 			dev_disable_lro(dev);
2042fa17806cSEric Dumazet 
2043fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2044edc9e748SNicolas Dichtel 		if (in_dev) {
204568dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
20463b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
20473b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2048edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2049edc9e748SNicolas Dichtel 		}
205068dd299bSPavel Emelyanov 	}
205168dd299bSPavel Emelyanov }
205268dd299bSPavel Emelyanov 
2053f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2054f085ff1cSstephen hemminger {
2055f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2056f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2057f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2058f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2059f085ff1cSstephen hemminger 	else {
2060f085ff1cSstephen hemminger 		struct in_device *idev
2061f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2062f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2063f085ff1cSstephen hemminger 	}
2064f085ff1cSstephen hemminger }
2065f085ff1cSstephen hemminger 
2066fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
20678d65af78SAlexey Dobriyan 			     void __user *buffer,
206831be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
206931be3085SHerbert Xu {
2070d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
20718d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2072d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
207331be3085SHerbert Xu 
207431be3085SHerbert Xu 	if (write) {
207531be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2076c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
207731be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2078f085ff1cSstephen hemminger 		int ifindex;
207931be3085SHerbert Xu 
208031be3085SHerbert Xu 		set_bit(i, cnf->state);
208131be3085SHerbert Xu 
20829355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2083c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2084d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2085d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2086d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
20874ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2088f085ff1cSstephen hemminger 
2089cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2090cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2091f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
20923b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
20933b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2094cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2095cc535dfbSNicolas Dichtel 		}
2096f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2097f085ff1cSstephen hemminger 		    new_value != old_value) {
2098f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
20993b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21003b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2101f085ff1cSstephen hemminger 						    ifindex, cnf);
2102f085ff1cSstephen hemminger 		}
2103974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2104974d7af5SAndy Gospodarek 		    new_value != old_value) {
2105974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
21063b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21073b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2108974d7af5SAndy Gospodarek 						    ifindex, cnf);
2109974d7af5SAndy Gospodarek 		}
211031be3085SHerbert Xu 	}
211131be3085SHerbert Xu 
211231be3085SHerbert Xu 	return ret;
211331be3085SHerbert Xu }
211431be3085SHerbert Xu 
2115fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
21168d65af78SAlexey Dobriyan 				  void __user *buffer,
21171da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
21181da177e4SLinus Torvalds {
21191da177e4SLinus Torvalds 	int *valp = ctl->data;
21201da177e4SLinus Torvalds 	int val = *valp;
212188af182eSEric W. Biederman 	loff_t pos = *ppos;
21228d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
21231da177e4SLinus Torvalds 
21241da177e4SLinus Torvalds 	if (write && *valp != val) {
2125c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2126c0ce9fb3SPavel Emelyanov 
21270187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
212888af182eSEric W. Biederman 			if (!rtnl_trylock()) {
212988af182eSEric W. Biederman 				/* Restore the original values before restarting */
213088af182eSEric W. Biederman 				*valp = val;
213188af182eSEric W. Biederman 				*ppos = pos;
21329b8adb5eSEric W. Biederman 				return restart_syscall();
213388af182eSEric W. Biederman 			}
21340187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2135c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2136edc9e748SNicolas Dichtel 			} else {
21370187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
21380187bdfbSBen Hutchings 				struct in_device *idev =
21390187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2140edc9e748SNicolas Dichtel 				if (*valp)
21410187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
21423b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2143edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2144edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2145edc9e748SNicolas Dichtel 							    cnf);
21460187bdfbSBen Hutchings 			}
21470187bdfbSBen Hutchings 			rtnl_unlock();
21484ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2149edc9e748SNicolas Dichtel 		} else
21503b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21513b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2152edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2153edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
21540187bdfbSBen Hutchings 	}
21551da177e4SLinus Torvalds 
21561da177e4SLinus Torvalds 	return ret;
21571da177e4SLinus Torvalds }
21581da177e4SLinus Torvalds 
2159fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
21608d65af78SAlexey Dobriyan 				void __user *buffer,
21611da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
21621da177e4SLinus Torvalds {
21631da177e4SLinus Torvalds 	int *valp = ctl->data;
21641da177e4SLinus Torvalds 	int val = *valp;
21658d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
216676e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
21671da177e4SLinus Torvalds 
21681da177e4SLinus Torvalds 	if (write && *valp != val)
21694ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
21701da177e4SLinus Torvalds 
21711da177e4SLinus Torvalds 	return ret;
21721da177e4SLinus Torvalds }
21731da177e4SLinus Torvalds 
2174f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
217542f811b8SHerbert Xu 	{ \
217642f811b8SHerbert Xu 		.procname	= name, \
217742f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
217802291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
217942f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
218042f811b8SHerbert Xu 		.mode		= mval, \
218142f811b8SHerbert Xu 		.proc_handler	= proc, \
218231be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
218342f811b8SHerbert Xu 	}
218442f811b8SHerbert Xu 
218542f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2186f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
218742f811b8SHerbert Xu 
218842f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2189f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
219042f811b8SHerbert Xu 
2191f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2192f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
219342f811b8SHerbert Xu 
219442f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2195f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
219642f811b8SHerbert Xu 
21971da177e4SLinus Torvalds static struct devinet_sysctl_table {
21981da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
219902291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
22001da177e4SLinus Torvalds } devinet_sysctl = {
22011da177e4SLinus Torvalds 	.devinet_vars = {
220242f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2203f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
220442f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
220542f811b8SHerbert Xu 
220642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
220742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
220842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
220942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
221042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
221142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
221242f811b8SHerbert Xu 					"accept_source_route"),
22138153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
221428f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
221542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
221642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
221742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
221842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
221942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
222042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
222142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
222242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
222342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2224eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
222565324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
22265c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
22275c6fe01cSWilliam Manley 					"force_igmp_version"),
22282690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
22292690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
22302690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
22312690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
22320eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
22330eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
223497daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
223597daf331SJohannes Berg 					"drop_gratuitous_arp"),
223642f811b8SHerbert Xu 
223742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
223842f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
223942f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
224042f811b8SHerbert Xu 					      "promote_secondaries"),
2241d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2242d0daebc3SThomas Graf 					      "route_localnet"),
224312b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
224412b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
22451da177e4SLinus Torvalds 	},
22461da177e4SLinus Torvalds };
22471da177e4SLinus Torvalds 
2248ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
224929c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
22501da177e4SLinus Torvalds {
22511da177e4SLinus Torvalds 	int i;
22529fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
22538607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2254bfada697SPavel Emelyanov 
22559fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
22561da177e4SLinus Torvalds 	if (!t)
22579fa89642SPavel Emelyanov 		goto out;
22589fa89642SPavel Emelyanov 
22591da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
22601da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
226131be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2262c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
22631da177e4SLinus Torvalds 	}
22641da177e4SLinus Torvalds 
22658607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
22661da177e4SLinus Torvalds 
22678607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
22681da177e4SLinus Torvalds 	if (!t->sysctl_header)
22698607ddb8SEric W. Biederman 		goto free;
22701da177e4SLinus Torvalds 
22711da177e4SLinus Torvalds 	p->sysctl = t;
227229c994e3SNicolas Dichtel 
22733b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
22743b022865SDavid Ahern 				    ifindex, p);
2275ea40b324SPavel Emelyanov 	return 0;
22761da177e4SLinus Torvalds 
22771da177e4SLinus Torvalds free:
22781da177e4SLinus Torvalds 	kfree(t);
22799fa89642SPavel Emelyanov out:
2280ea40b324SPavel Emelyanov 	return -ENOBUFS;
22811da177e4SLinus Torvalds }
22821da177e4SLinus Torvalds 
2283*b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2284*b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
228566f27a52SPavel Emelyanov {
228651602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
228766f27a52SPavel Emelyanov 
2288*b5c9641dSDavid Ahern 	if (t) {
228951602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2290ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
22911da177e4SLinus Torvalds 		kfree(t);
22921da177e4SLinus Torvalds 	}
229351602b2aSPavel Emelyanov 
2294*b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2295*b5c9641dSDavid Ahern }
2296*b5c9641dSDavid Ahern 
229720e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
229851602b2aSPavel Emelyanov {
229920e61da7SWANG Cong 	int err;
230020e61da7SWANG Cong 
230120e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
230220e61da7SWANG Cong 		return -EINVAL;
230320e61da7SWANG Cong 
230420e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
230520e61da7SWANG Cong 	if (err)
230620e61da7SWANG Cong 		return err;
230720e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
230829c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
230920e61da7SWANG Cong 	if (err)
231020e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
231120e61da7SWANG Cong 	return err;
231251602b2aSPavel Emelyanov }
231351602b2aSPavel Emelyanov 
231451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
231551602b2aSPavel Emelyanov {
2316*b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2317*b5c9641dSDavid Ahern 
2318*b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
231951602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
23201da177e4SLinus Torvalds }
23211da177e4SLinus Torvalds 
232268dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
232368dd299bSPavel Emelyanov 	{
232468dd299bSPavel Emelyanov 		.procname	= "ip_forward",
232568dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
232602291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
232768dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
232868dd299bSPavel Emelyanov 		.mode		= 0644,
232968dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
233068dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2331c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
233268dd299bSPavel Emelyanov 	},
233368dd299bSPavel Emelyanov 	{ },
233468dd299bSPavel Emelyanov };
23352a75de0cSEric Dumazet #endif
233668dd299bSPavel Emelyanov 
2337752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2338752d14dcSPavel Emelyanov {
2339752d14dcSPavel Emelyanov 	int err;
2340752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
23412a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
23422a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2343752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
23442a75de0cSEric Dumazet #endif
2345752d14dcSPavel Emelyanov 
2346752d14dcSPavel Emelyanov 	err = -ENOMEM;
2347752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2348752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2349752d14dcSPavel Emelyanov 
235009ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2351752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
235251456b29SIan Morris 		if (!all)
2353752d14dcSPavel Emelyanov 			goto err_alloc_all;
2354752d14dcSPavel Emelyanov 
2355752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
235651456b29SIan Morris 		if (!dflt)
2357752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2358752d14dcSPavel Emelyanov 
23592a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2360752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
236151456b29SIan Morris 		if (!tbl)
2362752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2363752d14dcSPavel Emelyanov 
236402291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2365752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2366752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
23672a75de0cSEric Dumazet #endif
2368752d14dcSPavel Emelyanov 	}
2369752d14dcSPavel Emelyanov 
2370752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
237129c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2372752d14dcSPavel Emelyanov 	if (err < 0)
2373752d14dcSPavel Emelyanov 		goto err_reg_all;
2374752d14dcSPavel Emelyanov 
237529c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
237629c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2377752d14dcSPavel Emelyanov 	if (err < 0)
2378752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2379752d14dcSPavel Emelyanov 
2380752d14dcSPavel Emelyanov 	err = -ENOMEM;
23818607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
238251456b29SIan Morris 	if (!forw_hdr)
2383752d14dcSPavel Emelyanov 		goto err_reg_ctl;
23842a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2385752d14dcSPavel Emelyanov #endif
2386752d14dcSPavel Emelyanov 
2387752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2388752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2389752d14dcSPavel Emelyanov 	return 0;
2390752d14dcSPavel Emelyanov 
2391752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2392752d14dcSPavel Emelyanov err_reg_ctl:
2393*b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2394752d14dcSPavel Emelyanov err_reg_dflt:
2395*b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2396752d14dcSPavel Emelyanov err_reg_all:
2397752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2398752d14dcSPavel Emelyanov 		kfree(tbl);
2399752d14dcSPavel Emelyanov err_alloc_ctl:
24002a75de0cSEric Dumazet #endif
2401752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2402752d14dcSPavel Emelyanov 		kfree(dflt);
2403752d14dcSPavel Emelyanov err_alloc_dflt:
2404752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2405752d14dcSPavel Emelyanov 		kfree(all);
2406752d14dcSPavel Emelyanov err_alloc_all:
2407752d14dcSPavel Emelyanov 	return err;
2408752d14dcSPavel Emelyanov }
2409752d14dcSPavel Emelyanov 
2410752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2411752d14dcSPavel Emelyanov {
24122a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2413752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2414752d14dcSPavel Emelyanov 
2415752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2416752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2417*b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2418*b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2419*b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2420*b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2421752d14dcSPavel Emelyanov 	kfree(tbl);
24222a75de0cSEric Dumazet #endif
2423752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2424752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2425752d14dcSPavel Emelyanov }
2426752d14dcSPavel Emelyanov 
2427752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2428752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2429752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2430752d14dcSPavel Emelyanov };
2431752d14dcSPavel Emelyanov 
2432207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
24339f0f7272SThomas Graf 	.family		  = AF_INET,
24349f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
24359f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2436cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2437cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
24389f0f7272SThomas Graf };
24399f0f7272SThomas Graf 
24401da177e4SLinus Torvalds void __init devinet_init(void)
24411da177e4SLinus Torvalds {
2442fd23c3b3SDavid S. Miller 	int i;
2443fd23c3b3SDavid S. Miller 
2444fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2445fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2446fd23c3b3SDavid S. Miller 
2447752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2448752d14dcSPavel Emelyanov 
24491da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
24501da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
245163f3444fSThomas Graf 
2452906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
24535c766d64SJiri Pirko 
24549f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
24559f0f7272SThomas Graf 
2456c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
2457c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
2458c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
24599e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
24607a674200SNicolas Dichtel 		      inet_netconf_dump_devconf, NULL);
24611da177e4SLinus Torvalds }
2462