xref: /openbmc/linux/net/ipv4/devinet.c (revision 5cbf777c)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	NET3	IP device support routines.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
1002c30a84SJesper Juhl  * 		Authors:	Ross Biro
111da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
121da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Additional Authors:
151da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
161da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *	Changes:
191da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
201da177e4SLinus Torvalds  *					lists.
211da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
221da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
231da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
241da177e4SLinus Torvalds  *					fall back to comparing just the label
251da177e4SLinus Torvalds  *					if no match found.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds 
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
301da177e4SLinus Torvalds #include <linux/bitops.h>
314fc268d2SRandy Dunlap #include <linux/capability.h>
321da177e4SLinus Torvalds #include <linux/module.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/kernel.h>
35174cd4b1SIngo Molnar #include <linux/sched/signal.h>
361da177e4SLinus Torvalds #include <linux/string.h>
371da177e4SLinus Torvalds #include <linux/mm.h>
381da177e4SLinus Torvalds #include <linux/socket.h>
391da177e4SLinus Torvalds #include <linux/sockios.h>
401da177e4SLinus Torvalds #include <linux/in.h>
411da177e4SLinus Torvalds #include <linux/errno.h>
421da177e4SLinus Torvalds #include <linux/interrupt.h>
431823730fSThomas Graf #include <linux/if_addr.h>
441da177e4SLinus Torvalds #include <linux/if_ether.h>
451da177e4SLinus Torvalds #include <linux/inet.h>
461da177e4SLinus Torvalds #include <linux/netdevice.h>
471da177e4SLinus Torvalds #include <linux/etherdevice.h>
481da177e4SLinus Torvalds #include <linux/skbuff.h>
491da177e4SLinus Torvalds #include <linux/init.h>
501da177e4SLinus Torvalds #include <linux/notifier.h>
511da177e4SLinus Torvalds #include <linux/inetdevice.h>
521da177e4SLinus Torvalds #include <linux/igmp.h>
535a0e3ad6STejun Heo #include <linux/slab.h>
54fd23c3b3SDavid S. Miller #include <linux/hash.h>
551da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
561da177e4SLinus Torvalds #include <linux/sysctl.h>
571da177e4SLinus Torvalds #endif
581da177e4SLinus Torvalds #include <linux/kmod.h>
59edc9e748SNicolas Dichtel #include <linux/netconf.h>
601da177e4SLinus Torvalds 
6114c85021SArnaldo Carvalho de Melo #include <net/arp.h>
621da177e4SLinus Torvalds #include <net/ip.h>
631da177e4SLinus Torvalds #include <net/route.h>
641da177e4SLinus Torvalds #include <net/ip_fib.h>
6563f3444fSThomas Graf #include <net/rtnetlink.h>
66752d14dcSPavel Emelyanov #include <net/net_namespace.h>
675c766d64SJiri Pirko #include <net/addrconf.h>
681da177e4SLinus Torvalds 
690027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7042f811b8SHerbert Xu 	.data = {
7102291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7202291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
752690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
7742f811b8SHerbert Xu 	},
781da177e4SLinus Torvalds };
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8142f811b8SHerbert Xu 	.data = {
8202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
872690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
882690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
8942f811b8SHerbert Xu 	},
901da177e4SLinus Torvalds };
911da177e4SLinus Torvalds 
929355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
939355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9442f811b8SHerbert Xu 
95ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
965c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
975c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
985c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
995176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1005c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
101ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
102af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
1035c753978SThomas Graf };
1045c753978SThomas Graf 
10540384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
10640384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
10740384999SEric Dumazet 
108fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
109fd23c3b3SDavid S. Miller 
1106eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
111fd23c3b3SDavid S. Miller {
11240384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
113fd23c3b3SDavid S. Miller 
11440384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
115fd23c3b3SDavid S. Miller }
116fd23c3b3SDavid S. Miller 
117fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
118fd23c3b3SDavid S. Miller {
11940384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
120fd23c3b3SDavid S. Miller 
12132a4be48SWANG Cong 	ASSERT_RTNL();
122fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
123fd23c3b3SDavid S. Miller }
124fd23c3b3SDavid S. Miller 
125fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
126fd23c3b3SDavid S. Miller {
12732a4be48SWANG Cong 	ASSERT_RTNL();
128fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
129fd23c3b3SDavid S. Miller }
130fd23c3b3SDavid S. Miller 
1319435eb1cSDavid S. Miller /**
1329435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1339435eb1cSDavid S. Miller  * @net: the net namespace
1349435eb1cSDavid S. Miller  * @addr: the source address
1359435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1369435eb1cSDavid S. Miller  *
1379435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1389435eb1cSDavid S. Miller  */
1399435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1409435eb1cSDavid S. Miller {
1419435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1429435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1439435eb1cSDavid S. Miller 
1449435eb1cSDavid S. Miller 	rcu_read_lock();
1456e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1466e617de8SPaolo Abeni 	if (!ifa) {
147406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
148406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
149406b6f97SDavid S. Miller 		struct fib_table *local;
150406b6f97SDavid S. Miller 
151406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
152406b6f97SDavid S. Miller 		 * over loopback subnets work.
153406b6f97SDavid S. Miller 		 */
154406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
155406b6f97SDavid S. Miller 		if (local &&
156406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
157406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
158406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1596e617de8SPaolo Abeni 	} else {
1606e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
161406b6f97SDavid S. Miller 	}
1629435eb1cSDavid S. Miller 	if (result && devref)
1639435eb1cSDavid S. Miller 		dev_hold(result);
1649435eb1cSDavid S. Miller 	rcu_read_unlock();
1659435eb1cSDavid S. Miller 	return result;
1669435eb1cSDavid S. Miller }
1679435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1689435eb1cSDavid S. Miller 
1696e617de8SPaolo Abeni /* called under RCU lock */
1706e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1716e617de8SPaolo Abeni {
1726e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1736e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1746e617de8SPaolo Abeni 
1756e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1766e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1776e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1786e617de8SPaolo Abeni 			return ifa;
1796e617de8SPaolo Abeni 
1806e617de8SPaolo Abeni 	return NULL;
1816e617de8SPaolo Abeni }
1826e617de8SPaolo Abeni 
183d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1841da177e4SLinus Torvalds 
185e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1863ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
1871da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1881da177e4SLinus Torvalds 			 int destroy);
1891da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
19020e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
19151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
19251602b2aSPavel Emelyanov #else
19320e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
19451602b2aSPavel Emelyanov {
19520e61da7SWANG Cong 	return 0;
19651602b2aSPavel Emelyanov }
19740384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
19851602b2aSPavel Emelyanov {
19951602b2aSPavel Emelyanov }
2001da177e4SLinus Torvalds #endif
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds /* Locks all the inet devices. */
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2051da177e4SLinus Torvalds {
20693adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2121da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2131da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2141da177e4SLinus Torvalds 	kfree(ifa);
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
21740384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2231da177e4SLinus Torvalds {
2241da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2251da177e4SLinus Torvalds 
226547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
227547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
228e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2291da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
23091df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2311da177e4SLinus Torvalds #endif
2321da177e4SLinus Torvalds 	dev_put(dev);
2331da177e4SLinus Torvalds 	if (!idev->dead)
2349f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2359f9354b9SEric Dumazet 	else
2361da177e4SLinus Torvalds 		kfree(idev);
2371da177e4SLinus Torvalds }
2389f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2391da177e4SLinus Torvalds 
24071e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2411da177e4SLinus Torvalds {
2421da177e4SLinus Torvalds 	struct in_device *in_dev;
24320e61da7SWANG Cong 	int err = -ENOMEM;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	ASSERT_RTNL();
2461da177e4SLinus Torvalds 
2470da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2481da177e4SLinus Torvalds 	if (!in_dev)
2491da177e4SLinus Torvalds 		goto out;
250c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2519355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2521da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2531da177e4SLinus Torvalds 	in_dev->dev = dev;
2549f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2559f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2561da177e4SLinus Torvalds 		goto out_kfree;
2570187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2580187bdfbSBen Hutchings 		dev_disable_lro(dev);
2591da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2601da177e4SLinus Torvalds 	dev_hold(dev);
26130c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2627658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2631da177e4SLinus Torvalds 
26420e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
26520e61da7SWANG Cong 	if (err) {
26620e61da7SWANG Cong 		in_dev->dead = 1;
26720e61da7SWANG Cong 		in_dev_put(in_dev);
26820e61da7SWANG Cong 		in_dev = NULL;
26920e61da7SWANG Cong 		goto out;
27020e61da7SWANG Cong 	}
2711da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2721da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2731da177e4SLinus Torvalds 		ip_mc_up(in_dev);
274483479ecSJarek Poplawski 
27530c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
276cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
277483479ecSJarek Poplawski out:
27820e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2791da177e4SLinus Torvalds out_kfree:
2801da177e4SLinus Torvalds 	kfree(in_dev);
2811da177e4SLinus Torvalds 	in_dev = NULL;
2821da177e4SLinus Torvalds 	goto out;
2831da177e4SLinus Torvalds }
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2861da177e4SLinus Torvalds {
2871da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2881da177e4SLinus Torvalds 	in_dev_put(idev);
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2941da177e4SLinus Torvalds 	struct net_device *dev;
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	ASSERT_RTNL();
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	dev = in_dev->dev;
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	in_dev->dead = 1;
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
3051da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3061da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3071da177e4SLinus Torvalds 	}
3081da177e4SLinus Torvalds 
309a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3101da177e4SLinus Torvalds 
31151602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3121da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3131da177e4SLinus Torvalds 	arp_ifdown(dev);
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds 
318ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3191da177e4SLinus Torvalds {
3201da177e4SLinus Torvalds 	rcu_read_lock();
3211da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3221da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3231da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3241da177e4SLinus Torvalds 				rcu_read_unlock();
3251da177e4SLinus Torvalds 				return 1;
3261da177e4SLinus Torvalds 			}
3271da177e4SLinus Torvalds 		}
3281da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3291da177e4SLinus Torvalds 	rcu_read_unlock();
3301da177e4SLinus Torvalds 	return 0;
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
333d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
33415e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3351da177e4SLinus Torvalds {
3368f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3370ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3380ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3390ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3400ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	ASSERT_RTNL();
3431da177e4SLinus Torvalds 
344fbd40ea0SDavid S. Miller 	if (in_dev->dead)
345fbd40ea0SDavid S. Miller 		goto no_promotions;
346fbd40ea0SDavid S. Miller 
3478f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3488f937c60SHarald Welte 	 * unless alias promotion is set
3498f937c60SHarald Welte 	 **/
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3521da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3550ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3560ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3570ff60a45SJamal Hadi Salim 				last_prim = ifa;
3580ff60a45SJamal Hadi Salim 
3591da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3601da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3611da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3621da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3630ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3641da177e4SLinus Torvalds 				continue;
3651da177e4SLinus Torvalds 			}
3661da177e4SLinus Torvalds 
3670ff60a45SJamal Hadi Salim 			if (!do_promote) {
368fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3691da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3701da177e4SLinus Torvalds 
37115e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
372e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
373e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3741da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3758f937c60SHarald Welte 			} else {
3768f937c60SHarald Welte 				promote = ifa;
3778f937c60SHarald Welte 				break;
3788f937c60SHarald Welte 			}
3791da177e4SLinus Torvalds 		}
3801da177e4SLinus Torvalds 	}
3811da177e4SLinus Torvalds 
3822d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3832d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3842d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3852d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3862d230e2bSJulian Anastasov 	 */
3872d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3882d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3892d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3902d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3912d230e2bSJulian Anastasov 	}
3922d230e2bSJulian Anastasov 
393fbd40ea0SDavid S. Miller no_promotions:
3941da177e4SLinus Torvalds 	/* 2. Unlink it */
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
397fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4021da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4031da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4041da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4051da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4061da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4071da177e4SLinus Torvalds 	   So that, this order is correct.
4081da177e4SLinus Torvalds 	 */
40915e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
410e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4110ff60a45SJamal Hadi Salim 
4120ff60a45SJamal Hadi Salim 	if (promote) {
41304024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
4140ff60a45SJamal Hadi Salim 
4150ff60a45SJamal Hadi Salim 		if (prev_prom) {
4160ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
4170ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
4180ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4190ff60a45SJamal Hadi Salim 		}
4200ff60a45SJamal Hadi Salim 
4210ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
42215e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
423e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
424e041c683SAlan Stern 				NETDEV_UP, promote);
42504024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4260ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4270ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4280ff60a45SJamal Hadi Salim 					continue;
4290ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4300ff60a45SJamal Hadi Salim 		}
4310ff60a45SJamal Hadi Salim 
4320ff60a45SJamal Hadi Salim 	}
4336363097cSHerbert Xu 	if (destroy)
4341da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
437d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
438d6062cbbSThomas Graf 			 int destroy)
439d6062cbbSThomas Graf {
440d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
441d6062cbbSThomas Graf }
442d6062cbbSThomas Graf 
4435c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4445c766d64SJiri Pirko 
4455c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4465c766d64SJiri Pirko 
447d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
448de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4491da177e4SLinus Torvalds {
4501da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4511da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4523ad7d246SKrister Johansen 	struct in_validator_info ivi;
4533ad7d246SKrister Johansen 	int ret;
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 	ASSERT_RTNL();
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4581da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4591da177e4SLinus Torvalds 		return 0;
4601da177e4SLinus Torvalds 	}
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4631da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4661da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4671da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4681da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4691da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4701da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4711da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4721da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4731da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4741da177e4SLinus Torvalds 				return -EEXIST;
4751da177e4SLinus Torvalds 			}
4761da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4771da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4781da177e4SLinus Torvalds 				return -EINVAL;
4791da177e4SLinus Torvalds 			}
4801da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4811da177e4SLinus Torvalds 		}
4821da177e4SLinus Torvalds 	}
4831da177e4SLinus Torvalds 
4843ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
4853ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
4863ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
4873ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
4883ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
4893ad7d246SKrister Johansen 	 * not the flags.
4903ad7d246SKrister Johansen 	 */
4913ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
4923ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
493de95e047SDavid Ahern 	ivi.extack = extack;
4943ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
4953ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
4963ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
4973ad7d246SKrister Johansen 	if (ret) {
4983ad7d246SKrister Johansen 		inet_free_ifa(ifa);
4993ad7d246SKrister Johansen 		return ret;
5003ad7d246SKrister Johansen 	}
5013ad7d246SKrister Johansen 
5021da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
50363862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
5041da177e4SLinus Torvalds 		ifap = last_primary;
5051da177e4SLinus Torvalds 	}
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
5081da177e4SLinus Torvalds 	*ifap = ifa;
5091da177e4SLinus Torvalds 
510fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
511fd23c3b3SDavid S. Miller 
5125c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
513906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5145c766d64SJiri Pirko 
5151da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5161da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5171da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
51815e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
519e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	return 0;
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds 
524d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
525d6062cbbSThomas Graf {
526de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
527d6062cbbSThomas Graf }
528d6062cbbSThomas Graf 
5291da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5301da177e4SLinus Torvalds {
531e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	ASSERT_RTNL();
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds 	if (!in_dev) {
5361da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5371da177e4SLinus Torvalds 		return -ENOBUFS;
5381da177e4SLinus Torvalds 	}
53971e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5401d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5411da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
542547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5431da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5441da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5451da177e4SLinus Torvalds 	}
546f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5471da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5481da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds 
5518723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5528723e1b4SEric Dumazet  * We dont take a reference on found in_device
5538723e1b4SEric Dumazet  */
5547fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5551da177e4SLinus Torvalds {
5561da177e4SLinus Torvalds 	struct net_device *dev;
5571da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
558c148fc2eSEric Dumazet 
559c148fc2eSEric Dumazet 	rcu_read_lock();
560c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5611da177e4SLinus Torvalds 	if (dev)
5628723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
563c148fc2eSEric Dumazet 	rcu_read_unlock();
5641da177e4SLinus Torvalds 	return in_dev;
5651da177e4SLinus Torvalds }
5669f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5691da177e4SLinus Torvalds 
57060cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
57160cad5daSAl Viro 				    __be32 mask)
5721da177e4SLinus Torvalds {
5731da177e4SLinus Torvalds 	ASSERT_RTNL();
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5761da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5771da177e4SLinus Torvalds 			return ifa;
5781da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5791da177e4SLinus Torvalds 	return NULL;
5801da177e4SLinus Torvalds }
5811da177e4SLinus Torvalds 
58293a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
58393a714d6SMadhu Challa {
58493a714d6SMadhu Challa 	struct ip_mreqn mreq = {
58593a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
58693a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
58793a714d6SMadhu Challa 	};
58893a714d6SMadhu Challa 	int ret;
58993a714d6SMadhu Challa 
59093a714d6SMadhu Challa 	ASSERT_RTNL();
59193a714d6SMadhu Challa 
59293a714d6SMadhu Challa 	lock_sock(sk);
59393a714d6SMadhu Challa 	if (join)
59454ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
59593a714d6SMadhu Challa 	else
59654ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
59793a714d6SMadhu Challa 	release_sock(sk);
59893a714d6SMadhu Challa 
59993a714d6SMadhu Challa 	return ret;
60093a714d6SMadhu Challa }
60193a714d6SMadhu Challa 
602c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
603c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6041da177e4SLinus Torvalds {
6053b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
606dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6071da177e4SLinus Torvalds 	struct in_device *in_dev;
608dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6091da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
610dfdd5fd4SThomas Graf 	int err = -EINVAL;
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	ASSERT_RTNL();
6131da177e4SLinus Torvalds 
614fceb6435SJohannes Berg 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy,
615c21ef3e3SDavid Ahern 			  extack);
616dfdd5fd4SThomas Graf 	if (err < 0)
617dfdd5fd4SThomas Graf 		goto errout;
618dfdd5fd4SThomas Graf 
619dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6207fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
62151456b29SIan Morris 	if (!in_dev) {
622dfdd5fd4SThomas Graf 		err = -ENODEV;
623dfdd5fd4SThomas Graf 		goto errout;
624dfdd5fd4SThomas Graf 	}
625dfdd5fd4SThomas Graf 
6261da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
6271da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
628dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
62967b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6301da177e4SLinus Torvalds 			continue;
631dfdd5fd4SThomas Graf 
632dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
633dfdd5fd4SThomas Graf 			continue;
634dfdd5fd4SThomas Graf 
635dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
636dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
63767b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
638dfdd5fd4SThomas Graf 			continue;
639dfdd5fd4SThomas Graf 
64093a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
64193a714d6SMadhu Challa 			ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
64215e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6431da177e4SLinus Torvalds 		return 0;
6441da177e4SLinus Torvalds 	}
645dfdd5fd4SThomas Graf 
646dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
647dfdd5fd4SThomas Graf errout:
648dfdd5fd4SThomas Graf 	return err;
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds 
6515c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6525c766d64SJiri Pirko 
6535c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6545c766d64SJiri Pirko {
6555c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6565c766d64SJiri Pirko 	struct in_ifaddr *ifa;
657c988d1e8SJiri Pirko 	struct hlist_node *n;
6585c766d64SJiri Pirko 	int i;
6595c766d64SJiri Pirko 
6605c766d64SJiri Pirko 	now = jiffies;
6615c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6625c766d64SJiri Pirko 
6635c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
664c988d1e8SJiri Pirko 		bool change_needed = false;
665c988d1e8SJiri Pirko 
666c988d1e8SJiri Pirko 		rcu_read_lock();
667b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6685c766d64SJiri Pirko 			unsigned long age;
6695c766d64SJiri Pirko 
6705c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6715c766d64SJiri Pirko 				continue;
6725c766d64SJiri Pirko 
6735c766d64SJiri Pirko 			/* We try to batch several events at once. */
6745c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6755c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6765c766d64SJiri Pirko 
6775c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6785c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
679c988d1e8SJiri Pirko 				change_needed = true;
680c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
681c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
682c988d1e8SJiri Pirko 				continue;
683c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
684c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
685c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
686c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
687c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
688c988d1e8SJiri Pirko 
689c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
690c988d1e8SJiri Pirko 					change_needed = true;
691c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
692c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
693c988d1e8SJiri Pirko 					       next)) {
694c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
695c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
696c988d1e8SJiri Pirko 			}
697c988d1e8SJiri Pirko 		}
698c988d1e8SJiri Pirko 		rcu_read_unlock();
699c988d1e8SJiri Pirko 		if (!change_needed)
700c988d1e8SJiri Pirko 			continue;
701c988d1e8SJiri Pirko 		rtnl_lock();
702c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
703c988d1e8SJiri Pirko 			unsigned long age;
704c988d1e8SJiri Pirko 
705c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
706c988d1e8SJiri Pirko 				continue;
707c988d1e8SJiri Pirko 
708c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
709c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
710c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
711c988d1e8SJiri Pirko 
712c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
713c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7145c766d64SJiri Pirko 				struct in_ifaddr **ifap;
7155c766d64SJiri Pirko 
7165c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
717c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
718c988d1e8SJiri Pirko 					if (*ifap == ifa) {
7195c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7205c766d64SJiri Pirko 							     ifap, 1);
721c988d1e8SJiri Pirko 						break;
7225c766d64SJiri Pirko 					}
723c988d1e8SJiri Pirko 				}
724c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
725c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
726c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
727c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7285c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7295c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7305c766d64SJiri Pirko 			}
7315c766d64SJiri Pirko 		}
732c988d1e8SJiri Pirko 		rtnl_unlock();
7335c766d64SJiri Pirko 	}
7345c766d64SJiri Pirko 
7355c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7365c766d64SJiri Pirko 	next_sched = next;
7375c766d64SJiri Pirko 
7385c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7395c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7405c766d64SJiri Pirko 		next_sched = next_sec;
7415c766d64SJiri Pirko 
7425c766d64SJiri Pirko 	now = jiffies;
7435c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7445c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7455c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7465c766d64SJiri Pirko 
747906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
748906e073fSviresh kumar 			next_sched - now);
7495c766d64SJiri Pirko }
7505c766d64SJiri Pirko 
7515c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7525c766d64SJiri Pirko 			     __u32 prefered_lft)
7535c766d64SJiri Pirko {
7545c766d64SJiri Pirko 	unsigned long timeout;
7555c766d64SJiri Pirko 
7565c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
7575c766d64SJiri Pirko 
7585c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
7595c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
7605c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7615c766d64SJiri Pirko 	else
7625c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
7635c766d64SJiri Pirko 
7645c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
7655c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
7665c766d64SJiri Pirko 		if (timeout == 0)
7675c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7685c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7695c766d64SJiri Pirko 	}
7705c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7715c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7725c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7735c766d64SJiri Pirko }
7745c766d64SJiri Pirko 
7755c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
7765c766d64SJiri Pirko 				       __u32 *pvalid_lft, __u32 *pprefered_lft)
7771da177e4SLinus Torvalds {
7785c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7795c753978SThomas Graf 	struct in_ifaddr *ifa;
7805c753978SThomas Graf 	struct ifaddrmsg *ifm;
7811da177e4SLinus Torvalds 	struct net_device *dev;
7821da177e4SLinus Torvalds 	struct in_device *in_dev;
7837b218574SDenis V. Lunev 	int err;
7841da177e4SLinus Torvalds 
785fceb6435SJohannes Berg 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy,
786fceb6435SJohannes Berg 			  NULL);
7875c753978SThomas Graf 	if (err < 0)
7885c753978SThomas Graf 		goto errout;
7891da177e4SLinus Torvalds 
7905c753978SThomas Graf 	ifm = nlmsg_data(nlh);
791c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
79251456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
7935c753978SThomas Graf 		goto errout;
7941da177e4SLinus Torvalds 
7954b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
7965c753978SThomas Graf 	err = -ENODEV;
79751456b29SIan Morris 	if (!dev)
7985c753978SThomas Graf 		goto errout;
7991da177e4SLinus Torvalds 
8005c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8015c753978SThomas Graf 	err = -ENOBUFS;
80251456b29SIan Morris 	if (!in_dev)
8035c753978SThomas Graf 		goto errout;
80471e27da9SHerbert Xu 
8055c753978SThomas Graf 	ifa = inet_alloc_ifa();
80651456b29SIan Morris 	if (!ifa)
8075c753978SThomas Graf 		/*
8085c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8095c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8105c753978SThomas Graf 		 */
8115c753978SThomas Graf 		goto errout;
8125c753978SThomas Graf 
813a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8141d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8155c753978SThomas Graf 	in_dev_hold(in_dev);
8165c753978SThomas Graf 
81751456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8185c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8195c753978SThomas Graf 
820fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8211da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8221da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
823ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
824ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8251da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8261da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8275c753978SThomas Graf 
82867b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
82967b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8305c753978SThomas Graf 
8315c753978SThomas Graf 	if (tb[IFA_BROADCAST])
83267b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8335c753978SThomas Graf 
8345c753978SThomas Graf 	if (tb[IFA_LABEL])
8355c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8361da177e4SLinus Torvalds 	else
8371da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8381da177e4SLinus Torvalds 
839af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
840af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
841af4d768aSDavid Ahern 
8425c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8435c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8445c766d64SJiri Pirko 
8455c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8465c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8475c766d64SJiri Pirko 			err = -EINVAL;
848446266b0SDaniel Borkmann 			goto errout_free;
8495c766d64SJiri Pirko 		}
8505c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8515c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8525c766d64SJiri Pirko 	}
8535c766d64SJiri Pirko 
8545c753978SThomas Graf 	return ifa;
8555c753978SThomas Graf 
856446266b0SDaniel Borkmann errout_free:
857446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8585c753978SThomas Graf errout:
8595c753978SThomas Graf 	return ERR_PTR(err);
8605c753978SThomas Graf }
8615c753978SThomas Graf 
8625c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
8635c766d64SJiri Pirko {
8645c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8655c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8665c766d64SJiri Pirko 
8675c766d64SJiri Pirko 	if (!ifa->ifa_local)
8685c766d64SJiri Pirko 		return NULL;
8695c766d64SJiri Pirko 
8705c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8715c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8725c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8735c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8745c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8755c766d64SJiri Pirko 			return ifa1;
8765c766d64SJiri Pirko 	}
8775c766d64SJiri Pirko 	return NULL;
8785c766d64SJiri Pirko }
8795c766d64SJiri Pirko 
880c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
881c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
8825c753978SThomas Graf {
8833b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8845c753978SThomas Graf 	struct in_ifaddr *ifa;
8855c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8865c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8875c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8885c753978SThomas Graf 
8895c753978SThomas Graf 	ASSERT_RTNL();
8905c753978SThomas Graf 
8915c766d64SJiri Pirko 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
8925c753978SThomas Graf 	if (IS_ERR(ifa))
8935c753978SThomas Graf 		return PTR_ERR(ifa);
8945c753978SThomas Graf 
8955c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
8965c766d64SJiri Pirko 	if (!ifa_existing) {
8975c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
898614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
8995c766d64SJiri Pirko 		 */
9005c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
90193a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
90293a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
90393a714d6SMadhu Challa 					       true, ifa);
90493a714d6SMadhu Challa 
90593a714d6SMadhu Challa 			if (ret < 0) {
90693a714d6SMadhu Challa 				inet_free_ifa(ifa);
90793a714d6SMadhu Challa 				return ret;
90893a714d6SMadhu Challa 			}
90993a714d6SMadhu Challa 		}
910de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
911de95e047SDavid Ahern 					 extack);
9125c766d64SJiri Pirko 	} else {
913af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
914af4d768aSDavid Ahern 
9155c766d64SJiri Pirko 		inet_free_ifa(ifa);
9165c766d64SJiri Pirko 
9175c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9185c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9195c766d64SJiri Pirko 			return -EEXIST;
92034e2ed34SJiri Pirko 		ifa = ifa_existing;
921af4d768aSDavid Ahern 
922af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
923af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
924af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
925af4d768aSDavid Ahern 		}
926af4d768aSDavid Ahern 
92734e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
92805a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
929906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
930906e073fSviresh kumar 				&check_lifetime_work, 0);
93134e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9325c766d64SJiri Pirko 	}
9335c766d64SJiri Pirko 	return 0;
9341da177e4SLinus Torvalds }
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds /*
9371da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9381da177e4SLinus Torvalds  */
9391da177e4SLinus Torvalds 
94040384999SEric Dumazet static int inet_abc_len(__be32 addr)
9411da177e4SLinus Torvalds {
9421da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9431da177e4SLinus Torvalds 
944f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
9451da177e4SLinus Torvalds 		rc = 0;
9461da177e4SLinus Torvalds 	else {
947714e85beSAl Viro 		__u32 haddr = ntohl(addr);
9481da177e4SLinus Torvalds 
949714e85beSAl Viro 		if (IN_CLASSA(haddr))
9501da177e4SLinus Torvalds 			rc = 8;
951714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9521da177e4SLinus Torvalds 			rc = 16;
953714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9541da177e4SLinus Torvalds 			rc = 24;
9551da177e4SLinus Torvalds 	}
9561da177e4SLinus Torvalds 
9571da177e4SLinus Torvalds 	return rc;
9581da177e4SLinus Torvalds }
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds 
96103aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
9621da177e4SLinus Torvalds {
9631da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
96403aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
9651da177e4SLinus Torvalds 	struct in_device *in_dev;
9661da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
9671da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
9681da177e4SLinus Torvalds 	struct net_device *dev;
9691da177e4SLinus Torvalds 	char *colon;
9701da177e4SLinus Torvalds 	int ret = -EFAULT;
9711da177e4SLinus Torvalds 	int tryaddrmatch = 0;
9721da177e4SLinus Torvalds 
97303aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
9741da177e4SLinus Torvalds 
9751da177e4SLinus Torvalds 	/* save original address for comparison */
9761da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9771da177e4SLinus Torvalds 
97803aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
9791da177e4SLinus Torvalds 	if (colon)
9801da177e4SLinus Torvalds 		*colon = 0;
9811da177e4SLinus Torvalds 
98203aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	switch (cmd) {
9851da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9861da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9871da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9881da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9891da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
9901da177e4SLinus Torvalds 		   so that we do not impose a lock.
9911da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
9921da177e4SLinus Torvalds 		 */
9931da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
9941da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
9951da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
9961da177e4SLinus Torvalds 		break;
9971da177e4SLinus Torvalds 
9981da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
999bf5b30b8SZhao Hongjiang 		ret = -EPERM;
100052e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10011da177e4SLinus Torvalds 			goto out;
10021da177e4SLinus Torvalds 		break;
10031da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10041da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10051da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10061da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1007bf5b30b8SZhao Hongjiang 		ret = -EPERM;
100852e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10091da177e4SLinus Torvalds 			goto out;
10101da177e4SLinus Torvalds 		ret = -EINVAL;
10111da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10121da177e4SLinus Torvalds 			goto out;
10131da177e4SLinus Torvalds 		break;
10141da177e4SLinus Torvalds 	default:
10151da177e4SLinus Torvalds 		ret = -EINVAL;
10161da177e4SLinus Torvalds 		goto out;
10171da177e4SLinus Torvalds 	}
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds 	rtnl_lock();
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds 	ret = -ENODEV;
102203aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10239f9354b9SEric Dumazet 	if (!dev)
10241da177e4SLinus Torvalds 		goto done;
10251da177e4SLinus Torvalds 
10261da177e4SLinus Torvalds 	if (colon)
10271da177e4SLinus Torvalds 		*colon = ':';
10281da177e4SLinus Torvalds 
10299f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10309f9354b9SEric Dumazet 	if (in_dev) {
10311da177e4SLinus Torvalds 		if (tryaddrmatch) {
10321da177e4SLinus Torvalds 			/* Matthias Andree */
10331da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10341da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10351da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10361da177e4SLinus Torvalds 			   This is checked above. */
10371da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10381da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
103903aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10401da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10416c91afe1SDavid S. Miller 							ifa->ifa_local) {
10421da177e4SLinus Torvalds 					break; /* found */
10431da177e4SLinus Torvalds 				}
10441da177e4SLinus Torvalds 			}
10451da177e4SLinus Torvalds 		}
10461da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10471da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10481da177e4SLinus Torvalds 		   comparing just the label */
10491da177e4SLinus Torvalds 		if (!ifa) {
10501da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10511da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
105203aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
10531da177e4SLinus Torvalds 					break;
10541da177e4SLinus Torvalds 		}
10551da177e4SLinus Torvalds 	}
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10581da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
10591da177e4SLinus Torvalds 		goto done;
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds 	switch (cmd) {
10621da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
106330e948a3STonghao Zhang 		ret = 0;
10641da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
106503aef17bSAl Viro 		break;
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
106830e948a3STonghao Zhang 		ret = 0;
10691da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
107003aef17bSAl Viro 		break;
10711da177e4SLinus Torvalds 
10721da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
107330e948a3STonghao Zhang 		ret = 0;
10741da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
107503aef17bSAl Viro 		break;
10761da177e4SLinus Torvalds 
10771da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
107830e948a3STonghao Zhang 		ret = 0;
10791da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
108003aef17bSAl Viro 		break;
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10831da177e4SLinus Torvalds 		if (colon) {
10841da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
10851da177e4SLinus Torvalds 			if (!ifa)
10861da177e4SLinus Torvalds 				break;
10871da177e4SLinus Torvalds 			ret = 0;
108803aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
10891da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
10901da177e4SLinus Torvalds 			break;
10911da177e4SLinus Torvalds 		}
109203aef17bSAl Viro 		ret = dev_change_flags(dev, ifr->ifr_flags);
10931da177e4SLinus Torvalds 		break;
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10961da177e4SLinus Torvalds 		ret = -EINVAL;
10971da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10981da177e4SLinus Torvalds 			break;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds 		if (!ifa) {
11011da177e4SLinus Torvalds 			ret = -ENOBUFS;
11029f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11039f9354b9SEric Dumazet 			if (!ifa)
11041da177e4SLinus Torvalds 				break;
1105c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11061da177e4SLinus Torvalds 			if (colon)
110703aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11081da177e4SLinus Torvalds 			else
11091da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11101da177e4SLinus Torvalds 		} else {
11111da177e4SLinus Torvalds 			ret = 0;
11121da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11131da177e4SLinus Torvalds 				break;
11141da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11151da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1116148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11171da177e4SLinus Torvalds 		}
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11221da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11231da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11241da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11251da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11261da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11271da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11281da177e4SLinus Torvalds 		} else {
11291da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11301da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11311da177e4SLinus Torvalds 		}
11325c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11331da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11341da177e4SLinus Torvalds 		break;
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11371da177e4SLinus Torvalds 		ret = 0;
11381da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11391da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11401da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11411da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11421da177e4SLinus Torvalds 		}
11431da177e4SLinus Torvalds 		break;
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11461da177e4SLinus Torvalds 		ret = 0;
11471da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11481da177e4SLinus Torvalds 			break;
11491da177e4SLinus Torvalds 		ret = -EINVAL;
11501da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11511da177e4SLinus Torvalds 			break;
11521da177e4SLinus Torvalds 		ret = 0;
11531da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11541da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11551da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11561da177e4SLinus Torvalds 		break;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
11591da177e4SLinus Torvalds 
11601da177e4SLinus Torvalds 		/*
11611da177e4SLinus Torvalds 		 *	The mask we set must be legal.
11621da177e4SLinus Torvalds 		 */
11631da177e4SLinus Torvalds 		ret = -EINVAL;
11641da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
11651da177e4SLinus Torvalds 			break;
11661da177e4SLinus Torvalds 		ret = 0;
11671da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1168a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
11691da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11701da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
11711da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 			/* See if current broadcast address matches
11741da177e4SLinus Torvalds 			 * with current netmask, then recalculate
11751da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
11761da177e4SLinus Torvalds 			 * funny address, so don't touch it since
11771da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
11781da177e4SLinus Torvalds 			 */
11791da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11801da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11811da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1182dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11831da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
11841da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
11851da177e4SLinus Torvalds 			}
11861da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11871da177e4SLinus Torvalds 		}
11881da177e4SLinus Torvalds 		break;
11891da177e4SLinus Torvalds 	}
11901da177e4SLinus Torvalds done:
11911da177e4SLinus Torvalds 	rtnl_unlock();
11921da177e4SLinus Torvalds out:
11931da177e4SLinus Torvalds 	return ret;
11941da177e4SLinus Torvalds }
11951da177e4SLinus Torvalds 
119636fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
11971da177e4SLinus Torvalds {
1198e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11991da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12001da177e4SLinus Torvalds 	struct ifreq ifr;
12011da177e4SLinus Torvalds 	int done = 0;
12021da177e4SLinus Torvalds 
120336fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
120436fd633eSAl Viro 		goto out;
120536fd633eSAl Viro 
12069f9354b9SEric Dumazet 	if (!in_dev)
12071da177e4SLinus Torvalds 		goto out;
12081da177e4SLinus Torvalds 
12099f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12101da177e4SLinus Torvalds 		if (!buf) {
121136fd633eSAl Viro 			done += size;
12121da177e4SLinus Torvalds 			continue;
12131da177e4SLinus Torvalds 		}
121436fd633eSAl Viro 		if (len < size)
12151da177e4SLinus Torvalds 			break;
12161da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12171da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12181da177e4SLinus Torvalds 
12191da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12201da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12211da177e4SLinus Torvalds 								ifa->ifa_local;
12221da177e4SLinus Torvalds 
122336fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12241da177e4SLinus Torvalds 			done = -EFAULT;
12251da177e4SLinus Torvalds 			break;
12261da177e4SLinus Torvalds 		}
122736fd633eSAl Viro 		len  -= size;
122836fd633eSAl Viro 		done += size;
12291da177e4SLinus Torvalds 	}
12301da177e4SLinus Torvalds out:
12311da177e4SLinus Torvalds 	return done;
12321da177e4SLinus Torvalds }
12331da177e4SLinus Torvalds 
12348b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12358b57fd1eSGao Feng 				 int scope)
12368b57fd1eSGao Feng {
12378b57fd1eSGao Feng 	for_primary_ifa(in_dev) {
12388b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12398b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12408b57fd1eSGao Feng 			return ifa->ifa_local;
12418b57fd1eSGao Feng 	} endfor_ifa(in_dev);
12428b57fd1eSGao Feng 
12438b57fd1eSGao Feng 	return 0;
12448b57fd1eSGao Feng }
12458b57fd1eSGao Feng 
1246a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
12471da177e4SLinus Torvalds {
1248a61ced5dSAl Viro 	__be32 addr = 0;
12491da177e4SLinus Torvalds 	struct in_device *in_dev;
1250c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12513f2fb9a8SDavid Ahern 	int master_idx;
12521da177e4SLinus Torvalds 
12531da177e4SLinus Torvalds 	rcu_read_lock();
1254e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
12551da177e4SLinus Torvalds 	if (!in_dev)
12561da177e4SLinus Torvalds 		goto no_in_dev;
12571da177e4SLinus Torvalds 
12581da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
12591da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
12601da177e4SLinus Torvalds 			continue;
12611da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
12621da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12631da177e4SLinus Torvalds 			break;
12641da177e4SLinus Torvalds 		}
12651da177e4SLinus Torvalds 		if (!addr)
12661da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12671da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 	if (addr)
1270c6d14c84SEric Dumazet 		goto out_unlock;
12719f9354b9SEric Dumazet no_in_dev:
12723f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
12731da177e4SLinus Torvalds 
127417b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
127517b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
127617b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
127717b693cdSDavid Lamparter 	 * equality check in the loop below.
127817b693cdSDavid Lamparter 	 */
127917b693cdSDavid Lamparter 	if (master_idx &&
128017b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
128117b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
12828b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
12838b57fd1eSGao Feng 		if (addr)
128417b693cdSDavid Lamparter 			goto out_unlock;
128517b693cdSDavid Lamparter 	}
128617b693cdSDavid Lamparter 
12871da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1288ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
12891da177e4SLinus Torvalds 	   in dev_base list.
12901da177e4SLinus Torvalds 	 */
1291c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
12923f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
12933f2fb9a8SDavid Ahern 			continue;
12943f2fb9a8SDavid Ahern 
12959f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
12969f9354b9SEric Dumazet 		if (!in_dev)
12971da177e4SLinus Torvalds 			continue;
12981da177e4SLinus Torvalds 
12998b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13008b57fd1eSGao Feng 		if (addr)
1301c6d14c84SEric Dumazet 			goto out_unlock;
13021da177e4SLinus Torvalds 	}
1303c6d14c84SEric Dumazet out_unlock:
13041da177e4SLinus Torvalds 	rcu_read_unlock();
13051da177e4SLinus Torvalds 	return addr;
13061da177e4SLinus Torvalds }
13079f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13081da177e4SLinus Torvalds 
130960cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
131060cad5daSAl Viro 			      __be32 local, int scope)
13111da177e4SLinus Torvalds {
13121da177e4SLinus Torvalds 	int same = 0;
1313a144ea4bSAl Viro 	__be32 addr = 0;
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds 	for_ifa(in_dev) {
13161da177e4SLinus Torvalds 		if (!addr &&
13171da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
13181da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
13191da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13201da177e4SLinus Torvalds 			if (same)
13211da177e4SLinus Torvalds 				break;
13221da177e4SLinus Torvalds 		}
13231da177e4SLinus Torvalds 		if (!same) {
13241da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13251da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13261da177e4SLinus Torvalds 			if (same && addr) {
13271da177e4SLinus Torvalds 				if (local || !dst)
13281da177e4SLinus Torvalds 					break;
13291da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
13301da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
13311da177e4SLinus Torvalds 					break;
13321da177e4SLinus Torvalds 				/* No, then can we use new local src? */
13331da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
13341da177e4SLinus Torvalds 					addr = ifa->ifa_local;
13351da177e4SLinus Torvalds 					break;
13361da177e4SLinus Torvalds 				}
13371da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
13381da177e4SLinus Torvalds 				same = 0;
13391da177e4SLinus Torvalds 			}
13401da177e4SLinus Torvalds 		}
13411da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
13421da177e4SLinus Torvalds 
13431da177e4SLinus Torvalds 	return same ? addr : 0;
13441da177e4SLinus Torvalds }
13451da177e4SLinus Torvalds 
13461da177e4SLinus Torvalds /*
13471da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1348b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1349b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
13501da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
13511da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
13521da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
13531da177e4SLinus Torvalds  */
1354b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
13559bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
13561da177e4SLinus Torvalds {
135760cad5daSAl Viro 	__be32 addr = 0;
13589bd85e32SDenis V. Lunev 	struct net_device *dev;
13591da177e4SLinus Torvalds 
136000db4124SIan Morris 	if (in_dev)
13619bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 	rcu_read_lock();
1364c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13659f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13669f9354b9SEric Dumazet 		if (in_dev) {
13671da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
13681da177e4SLinus Torvalds 			if (addr)
13691da177e4SLinus Torvalds 				break;
13701da177e4SLinus Torvalds 		}
13711da177e4SLinus Torvalds 	}
13721da177e4SLinus Torvalds 	rcu_read_unlock();
13731da177e4SLinus Torvalds 
13741da177e4SLinus Torvalds 	return addr;
13751da177e4SLinus Torvalds }
1376eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
13771da177e4SLinus Torvalds 
13781da177e4SLinus Torvalds /*
13791da177e4SLinus Torvalds  *	Device notifier
13801da177e4SLinus Torvalds  */
13811da177e4SLinus Torvalds 
13821da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
13831da177e4SLinus Torvalds {
1384e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
13851da177e4SLinus Torvalds }
13869f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
13871da177e4SLinus Torvalds 
13881da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
13891da177e4SLinus Torvalds {
1390e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
13911da177e4SLinus Torvalds }
13929f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
13931da177e4SLinus Torvalds 
13943ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
13953ad7d246SKrister Johansen {
13963ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
13973ad7d246SKrister Johansen }
13983ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
13993ad7d246SKrister Johansen 
14003ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14013ad7d246SKrister Johansen {
14023ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14033ad7d246SKrister Johansen 	    nb);
14043ad7d246SKrister Johansen }
14053ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14063ad7d246SKrister Johansen 
14079f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14089f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14091da177e4SLinus Torvalds */
14101da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14111da177e4SLinus Torvalds {
14121da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14131da177e4SLinus Torvalds 	int named = 0;
14141da177e4SLinus Torvalds 
14151da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
14161da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14171da177e4SLinus Torvalds 
14181da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14191da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14201da177e4SLinus Torvalds 		if (named++ == 0)
1421573bf470SThomas Graf 			goto skip;
142244344b2aSMark McLoughlin 		dot = strchr(old, ':');
142351456b29SIan Morris 		if (!dot) {
14241da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14251da177e4SLinus Torvalds 			dot = old;
14261da177e4SLinus Torvalds 		}
14279f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14281da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14299f9354b9SEric Dumazet 		else
14301da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1431573bf470SThomas Graf skip:
1432573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
14331da177e4SLinus Torvalds 	}
14341da177e4SLinus Torvalds }
14351da177e4SLinus Torvalds 
143640384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
143706770843SBreno Leitao {
1438b5476022SEric Dumazet 	return mtu >= IPV4_MIN_MTU;
143906770843SBreno Leitao }
144006770843SBreno Leitao 
1441d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1442d11327adSIan Campbell 					struct in_device *in_dev)
1443d11327adSIan Campbell 
1444d11327adSIan Campbell {
1445b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1446d11327adSIan Campbell 
1447b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1448b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1449d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
14506c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
14516c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1452d11327adSIan Campbell 			 dev->dev_addr, NULL);
1453d11327adSIan Campbell 	}
1454b76d0789SZoltan Kiss }
1455d11327adSIan Campbell 
14561da177e4SLinus Torvalds /* Called only under RTNL semaphore */
14571da177e4SLinus Torvalds 
14581da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
14591da177e4SLinus Torvalds 			 void *ptr)
14601da177e4SLinus Torvalds {
1461351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1462748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
14631da177e4SLinus Torvalds 
14641da177e4SLinus Torvalds 	ASSERT_RTNL();
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 	if (!in_dev) {
14678030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
14681da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
146920e61da7SWANG Cong 			if (IS_ERR(in_dev))
147020e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
14710cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
147242f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
147342f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
14741da177e4SLinus Torvalds 			}
147506770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
147606770843SBreno Leitao 			/* Re-enabling IP */
147706770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
147806770843SBreno Leitao 				in_dev = inetdev_init(dev);
14798030f544SHerbert Xu 		}
14801da177e4SLinus Torvalds 		goto out;
14811da177e4SLinus Torvalds 	}
14821da177e4SLinus Torvalds 
14831da177e4SLinus Torvalds 	switch (event) {
14841da177e4SLinus Torvalds 	case NETDEV_REGISTER:
148591df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1486a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
14871da177e4SLinus Torvalds 		break;
14881da177e4SLinus Torvalds 	case NETDEV_UP:
148906770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
14901da177e4SLinus Torvalds 			break;
14910cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
14929f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
14939f9354b9SEric Dumazet 
14949f9354b9SEric Dumazet 			if (ifa) {
1495fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
14961da177e4SLinus Torvalds 				ifa->ifa_local =
14971da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
14981da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
14991da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15001da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15011da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15021da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15031da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15045c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15055c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1506dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1507dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15081da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15091da177e4SLinus Torvalds 			}
15101da177e4SLinus Torvalds 		}
15111da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1512eefef1cfSStephen Hemminger 		/* fall through */
1513eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1514d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1515d11327adSIan Campbell 			break;
1516d11327adSIan Campbell 		/* fall through */
1517d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1518a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1519d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15201da177e4SLinus Torvalds 		break;
15211da177e4SLinus Torvalds 	case NETDEV_DOWN:
15221da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15231da177e4SLinus Torvalds 		break;
152493d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
152575c78500SMoni Shoua 		ip_mc_unmap(in_dev);
152675c78500SMoni Shoua 		break;
152793d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
152875c78500SMoni Shoua 		ip_mc_remap(in_dev);
152975c78500SMoni Shoua 		break;
15301da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
153106770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15321da177e4SLinus Torvalds 			break;
153306770843SBreno Leitao 		/* disable IP when MTU is not enough */
1534fcfd6dfaSGustavo A. R. Silva 		/* fall through */
15351da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
15361da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
15371da177e4SLinus Torvalds 		break;
15381da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
15391da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
15401da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
15411da177e4SLinus Torvalds 		 */
15421da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
15431da177e4SLinus Torvalds 
154451602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
154566f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
15461da177e4SLinus Torvalds 		break;
15471da177e4SLinus Torvalds 	}
15481da177e4SLinus Torvalds out:
15491da177e4SLinus Torvalds 	return NOTIFY_DONE;
15501da177e4SLinus Torvalds }
15511da177e4SLinus Torvalds 
15521da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
15531da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
15541da177e4SLinus Torvalds };
15551da177e4SLinus Torvalds 
155640384999SEric Dumazet static size_t inet_nlmsg_size(void)
1557339bf98fSThomas Graf {
1558339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1559339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1560339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1561339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1562ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
156363b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1564af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
156563b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1566339bf98fSThomas Graf }
1567339bf98fSThomas Graf 
15685c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
15695c766d64SJiri Pirko {
15705c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
15715c766d64SJiri Pirko }
15725c766d64SJiri Pirko 
15735c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
15745c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
15755c766d64SJiri Pirko {
15765c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
15775c766d64SJiri Pirko 
15785c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
15795c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
15805c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
15815c766d64SJiri Pirko 	ci.ifa_valid = valid;
15825c766d64SJiri Pirko 
15835c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
15845c766d64SJiri Pirko }
15855c766d64SJiri Pirko 
15861da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
158715e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
15881da177e4SLinus Torvalds {
15891da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
15901da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
15915c766d64SJiri Pirko 	u32 preferred, valid;
15921da177e4SLinus Torvalds 
159315e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
159451456b29SIan Morris 	if (!nlh)
159526932566SPatrick McHardy 		return -EMSGSIZE;
159647f68512SThomas Graf 
159747f68512SThomas Graf 	ifm = nlmsg_data(nlh);
15981da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
15991da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16005c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16011da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16021da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16031da177e4SLinus Torvalds 
16045c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16055c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16065c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16075c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16085c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16095c766d64SJiri Pirko 
16105c766d64SJiri Pirko 			if (preferred > tval)
16115c766d64SJiri Pirko 				preferred -= tval;
16125c766d64SJiri Pirko 			else
16135c766d64SJiri Pirko 				preferred = 0;
16145c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16155c766d64SJiri Pirko 				if (valid > tval)
16165c766d64SJiri Pirko 					valid -= tval;
16175c766d64SJiri Pirko 				else
16185c766d64SJiri Pirko 					valid = 0;
16195c766d64SJiri Pirko 			}
16205c766d64SJiri Pirko 		}
16215c766d64SJiri Pirko 	} else {
16225c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16235c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16245c766d64SJiri Pirko 	}
1625f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1626930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1627f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1628930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1629f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1630930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1631f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
16325c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1633ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1634af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1635af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
16365c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
16375c766d64SJiri Pirko 			  preferred, valid))
1638f3756b79SDavid S. Miller 		goto nla_put_failure;
163947f68512SThomas Graf 
1640053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1641053c095aSJohannes Berg 	return 0;
164247f68512SThomas Graf 
164347f68512SThomas Graf nla_put_failure:
164426932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
164526932566SPatrick McHardy 	return -EMSGSIZE;
16461da177e4SLinus Torvalds }
16471da177e4SLinus Torvalds 
16481da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
16491da177e4SLinus Torvalds {
16503b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1651eec4df98SEric Dumazet 	int h, s_h;
1652eec4df98SEric Dumazet 	int idx, s_idx;
1653eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
16541da177e4SLinus Torvalds 	struct net_device *dev;
16551da177e4SLinus Torvalds 	struct in_device *in_dev;
16561da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1657eec4df98SEric Dumazet 	struct hlist_head *head;
16581da177e4SLinus Torvalds 
1659eec4df98SEric Dumazet 	s_h = cb->args[0];
1660eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1661eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1662eec4df98SEric Dumazet 
1663eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
16647562f876SPavel Emelianov 		idx = 0;
1665eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1666eec4df98SEric Dumazet 		rcu_read_lock();
16670465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
16680465277fSNicolas Dichtel 			  net->dev_base_seq;
1669b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
16701da177e4SLinus Torvalds 			if (idx < s_idx)
16717562f876SPavel Emelianov 				goto cont;
16724b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
16731da177e4SLinus Torvalds 				s_ip_idx = 0;
1674eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
16759f9354b9SEric Dumazet 			if (!in_dev)
16767562f876SPavel Emelianov 				goto cont;
16771da177e4SLinus Torvalds 
16781da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
16791da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
16801da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1681596e4150SStephen Hemminger 					continue;
1682eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
168315e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
16841da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1685053c095aSJohannes Berg 					     RTM_NEWADDR, NLM_F_MULTI) < 0) {
1686eec4df98SEric Dumazet 					rcu_read_unlock();
16871da177e4SLinus Torvalds 					goto done;
16881da177e4SLinus Torvalds 				}
16890465277fSNicolas Dichtel 				nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1690eec4df98SEric Dumazet 			}
16917562f876SPavel Emelianov cont:
16927562f876SPavel Emelianov 			idx++;
16931da177e4SLinus Torvalds 		}
1694eec4df98SEric Dumazet 		rcu_read_unlock();
1695eec4df98SEric Dumazet 	}
16961da177e4SLinus Torvalds 
16971da177e4SLinus Torvalds done:
1698eec4df98SEric Dumazet 	cb->args[0] = h;
1699eec4df98SEric Dumazet 	cb->args[1] = idx;
1700eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
17011da177e4SLinus Torvalds 
17021da177e4SLinus Torvalds 	return skb->len;
17031da177e4SLinus Torvalds }
17041da177e4SLinus Torvalds 
1705d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
170615e47304SEric W. Biederman 		      u32 portid)
17071da177e4SLinus Torvalds {
170847f68512SThomas Graf 	struct sk_buff *skb;
1709d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1710d6062cbbSThomas Graf 	int err = -ENOBUFS;
17114b8aa9abSDenis V. Lunev 	struct net *net;
17121da177e4SLinus Torvalds 
1713c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1714339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
171551456b29SIan Morris 	if (!skb)
1716d6062cbbSThomas Graf 		goto errout;
1717d6062cbbSThomas Graf 
171815e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
171926932566SPatrick McHardy 	if (err < 0) {
172026932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
172126932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
172226932566SPatrick McHardy 		kfree_skb(skb);
172326932566SPatrick McHardy 		goto errout;
172426932566SPatrick McHardy 	}
172515e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
17261ce85fe4SPablo Neira Ayuso 	return;
1727d6062cbbSThomas Graf errout:
1728d6062cbbSThomas Graf 	if (err < 0)
17294b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
17301da177e4SLinus Torvalds }
17311da177e4SLinus Torvalds 
1732b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1733b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
17349f0f7272SThomas Graf {
17351fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
17369f0f7272SThomas Graf 
17379f0f7272SThomas Graf 	if (!in_dev)
17389f0f7272SThomas Graf 		return 0;
17399f0f7272SThomas Graf 
17409f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
17419f0f7272SThomas Graf }
17429f0f7272SThomas Graf 
1743d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1744d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
17459f0f7272SThomas Graf {
17461fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
17479f0f7272SThomas Graf 	struct nlattr *nla;
17489f0f7272SThomas Graf 	int i;
17499f0f7272SThomas Graf 
17509f0f7272SThomas Graf 	if (!in_dev)
17519f0f7272SThomas Graf 		return -ENODATA;
17529f0f7272SThomas Graf 
17539f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
175451456b29SIan Morris 	if (!nla)
17559f0f7272SThomas Graf 		return -EMSGSIZE;
17569f0f7272SThomas Graf 
17579f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
17589f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
17599f0f7272SThomas Graf 
17609f0f7272SThomas Graf 	return 0;
17619f0f7272SThomas Graf }
17629f0f7272SThomas Graf 
17639f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
17649f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
17659f0f7272SThomas Graf };
17669f0f7272SThomas Graf 
1767cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1768cf7afbfeSThomas Graf 				 const struct nlattr *nla)
17699f0f7272SThomas Graf {
17709f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
17719f0f7272SThomas Graf 	int err, rem;
17729f0f7272SThomas Graf 
17735fa85a09SFlorian Westphal 	if (dev && !__in_dev_get_rcu(dev))
1774cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
17759f0f7272SThomas Graf 
1776fceb6435SJohannes Berg 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy, NULL);
17779f0f7272SThomas Graf 	if (err < 0)
17789f0f7272SThomas Graf 		return err;
17799f0f7272SThomas Graf 
17809f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
17819f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
17829f0f7272SThomas Graf 			int cfgid = nla_type(a);
17839f0f7272SThomas Graf 
17849f0f7272SThomas Graf 			if (nla_len(a) < 4)
17859f0f7272SThomas Graf 				return -EINVAL;
17869f0f7272SThomas Graf 
17879f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
17889f0f7272SThomas Graf 				return -EINVAL;
17899f0f7272SThomas Graf 		}
17909f0f7272SThomas Graf 	}
17919f0f7272SThomas Graf 
1792cf7afbfeSThomas Graf 	return 0;
1793cf7afbfeSThomas Graf }
1794cf7afbfeSThomas Graf 
1795cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1796cf7afbfeSThomas Graf {
17975fa85a09SFlorian Westphal 	struct in_device *in_dev = __in_dev_get_rcu(dev);
1798cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1799cf7afbfeSThomas Graf 	int rem;
1800cf7afbfeSThomas Graf 
1801cf7afbfeSThomas Graf 	if (!in_dev)
1802cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1803cf7afbfeSThomas Graf 
1804fceb6435SJohannes Berg 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
1805cf7afbfeSThomas Graf 		BUG();
1806cf7afbfeSThomas Graf 
18079f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
18089f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
18099f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
18109f0f7272SThomas Graf 	}
18119f0f7272SThomas Graf 
18129f0f7272SThomas Graf 	return 0;
18139f0f7272SThomas Graf }
18149f0f7272SThomas Graf 
1815edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1816edc9e748SNicolas Dichtel {
1817edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1818edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1819136ba622SZhang Shengju 	bool all = false;
1820edc9e748SNicolas Dichtel 
1821136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1822136ba622SZhang Shengju 		all = true;
1823136ba622SZhang Shengju 
1824136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
1825edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1826136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
1827cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1828136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
1829d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
18305cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
18315cbf777cSXin Long 		size += nla_total_size(4);
1832136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
1833f085ff1cSstephen hemminger 		size += nla_total_size(4);
1834136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
1835974d7af5SAndy Gospodarek 		size += nla_total_size(4);
1836edc9e748SNicolas Dichtel 
1837edc9e748SNicolas Dichtel 	return size;
1838edc9e748SNicolas Dichtel }
1839edc9e748SNicolas Dichtel 
1840edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1841edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1842edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1843edc9e748SNicolas Dichtel 				     int type)
1844edc9e748SNicolas Dichtel {
1845edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1846edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1847136ba622SZhang Shengju 	bool all = false;
1848edc9e748SNicolas Dichtel 
1849edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1850edc9e748SNicolas Dichtel 			flags);
185151456b29SIan Morris 	if (!nlh)
1852edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1853edc9e748SNicolas Dichtel 
1854136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1855136ba622SZhang Shengju 		all = true;
1856136ba622SZhang Shengju 
1857edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1858edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1859edc9e748SNicolas Dichtel 
1860edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1861edc9e748SNicolas Dichtel 		goto nla_put_failure;
1862edc9e748SNicolas Dichtel 
1863b5c9641dSDavid Ahern 	if (!devconf)
1864b5c9641dSDavid Ahern 		goto out;
1865b5c9641dSDavid Ahern 
1866136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
1867edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1868edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1869edc9e748SNicolas Dichtel 		goto nla_put_failure;
1870136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
1871cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1872cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1873cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1874136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
1875d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1876d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1877d67b8c61SNicolas Dichtel 		goto nla_put_failure;
18785cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
18795cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
18805cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
18815cbf777cSXin Long 		goto nla_put_failure;
1882136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
188309aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
1884f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
1885f085ff1cSstephen hemminger 		goto nla_put_failure;
1886136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
1887974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
1888974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
1889974d7af5SAndy Gospodarek 		goto nla_put_failure;
1890edc9e748SNicolas Dichtel 
1891b5c9641dSDavid Ahern out:
1892053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1893053c095aSJohannes Berg 	return 0;
1894edc9e748SNicolas Dichtel 
1895edc9e748SNicolas Dichtel nla_put_failure:
1896edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1897edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1898edc9e748SNicolas Dichtel }
1899edc9e748SNicolas Dichtel 
19003b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
19013b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
1902edc9e748SNicolas Dichtel {
1903edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1904edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1905edc9e748SNicolas Dichtel 
1906fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
190751456b29SIan Morris 	if (!skb)
1908edc9e748SNicolas Dichtel 		goto errout;
1909edc9e748SNicolas Dichtel 
1910edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
19113b022865SDavid Ahern 					event, 0, type);
1912edc9e748SNicolas Dichtel 	if (err < 0) {
1913edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1914edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1915edc9e748SNicolas Dichtel 		kfree_skb(skb);
1916edc9e748SNicolas Dichtel 		goto errout;
1917edc9e748SNicolas Dichtel 	}
1918fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
1919edc9e748SNicolas Dichtel 	return;
1920edc9e748SNicolas Dichtel errout:
1921edc9e748SNicolas Dichtel 	if (err < 0)
1922edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1923edc9e748SNicolas Dichtel }
1924edc9e748SNicolas Dichtel 
19259e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
19269e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
19279e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1928cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
192909aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
1930974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
19319e551110SNicolas Dichtel };
19329e551110SNicolas Dichtel 
19339e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
1934c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
1935c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
19369e551110SNicolas Dichtel {
19379e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
19389e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
19399e551110SNicolas Dichtel 	struct netconfmsg *ncm;
19409e551110SNicolas Dichtel 	struct sk_buff *skb;
19419e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
19429e551110SNicolas Dichtel 	struct in_device *in_dev;
19439e551110SNicolas Dichtel 	struct net_device *dev;
19449e551110SNicolas Dichtel 	int ifindex;
19459e551110SNicolas Dichtel 	int err;
19469e551110SNicolas Dichtel 
19479e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
1948c21ef3e3SDavid Ahern 			  devconf_ipv4_policy, extack);
19499e551110SNicolas Dichtel 	if (err < 0)
19509e551110SNicolas Dichtel 		goto errout;
19519e551110SNicolas Dichtel 
1952a97eb33fSAnton Protopopov 	err = -EINVAL;
19539e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
19549e551110SNicolas Dichtel 		goto errout;
19559e551110SNicolas Dichtel 
19569e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
19579e551110SNicolas Dichtel 	switch (ifindex) {
19589e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
19599e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
19609e551110SNicolas Dichtel 		break;
19619e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
19629e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
19639e551110SNicolas Dichtel 		break;
19649e551110SNicolas Dichtel 	default:
19659e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
196651456b29SIan Morris 		if (!dev)
19679e551110SNicolas Dichtel 			goto errout;
19689e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
196951456b29SIan Morris 		if (!in_dev)
19709e551110SNicolas Dichtel 			goto errout;
19719e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
19729e551110SNicolas Dichtel 		break;
19739e551110SNicolas Dichtel 	}
19749e551110SNicolas Dichtel 
19759e551110SNicolas Dichtel 	err = -ENOBUFS;
1976fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
197751456b29SIan Morris 	if (!skb)
19789e551110SNicolas Dichtel 		goto errout;
19799e551110SNicolas Dichtel 
19809e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
19819e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
19829e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
1983136ba622SZhang Shengju 					NETCONFA_ALL);
19849e551110SNicolas Dichtel 	if (err < 0) {
19859e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
19869e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
19879e551110SNicolas Dichtel 		kfree_skb(skb);
19889e551110SNicolas Dichtel 		goto errout;
19899e551110SNicolas Dichtel 	}
19909e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
19919e551110SNicolas Dichtel errout:
19929e551110SNicolas Dichtel 	return err;
19939e551110SNicolas Dichtel }
19949e551110SNicolas Dichtel 
19957a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
19967a674200SNicolas Dichtel 				     struct netlink_callback *cb)
19977a674200SNicolas Dichtel {
19987a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
19997a674200SNicolas Dichtel 	int h, s_h;
20007a674200SNicolas Dichtel 	int idx, s_idx;
20017a674200SNicolas Dichtel 	struct net_device *dev;
20027a674200SNicolas Dichtel 	struct in_device *in_dev;
20037a674200SNicolas Dichtel 	struct hlist_head *head;
20047a674200SNicolas Dichtel 
20057a674200SNicolas Dichtel 	s_h = cb->args[0];
20067a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
20077a674200SNicolas Dichtel 
20087a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
20097a674200SNicolas Dichtel 		idx = 0;
20107a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
20117a674200SNicolas Dichtel 		rcu_read_lock();
20120465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
20130465277fSNicolas Dichtel 			  net->dev_base_seq;
20147a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
20157a674200SNicolas Dichtel 			if (idx < s_idx)
20167a674200SNicolas Dichtel 				goto cont;
20177a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
20187a674200SNicolas Dichtel 			if (!in_dev)
20197a674200SNicolas Dichtel 				goto cont;
20207a674200SNicolas Dichtel 
20217a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
20227a674200SNicolas Dichtel 						      &in_dev->cnf,
20237a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
20247a674200SNicolas Dichtel 						      cb->nlh->nlmsg_seq,
20257a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
20267a674200SNicolas Dichtel 						      NLM_F_MULTI,
2027136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
20287a674200SNicolas Dichtel 				rcu_read_unlock();
20297a674200SNicolas Dichtel 				goto done;
20307a674200SNicolas Dichtel 			}
20310465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
20327a674200SNicolas Dichtel cont:
20337a674200SNicolas Dichtel 			idx++;
20347a674200SNicolas Dichtel 		}
20357a674200SNicolas Dichtel 		rcu_read_unlock();
20367a674200SNicolas Dichtel 	}
20377a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
20387a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
20397a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
20407a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
20417a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
20427a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2043136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
20447a674200SNicolas Dichtel 			goto done;
20457a674200SNicolas Dichtel 		else
20467a674200SNicolas Dichtel 			h++;
20477a674200SNicolas Dichtel 	}
20487a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
20497a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
20507a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
20517a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
20527a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
20537a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2054136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
20557a674200SNicolas Dichtel 			goto done;
20567a674200SNicolas Dichtel 		else
20577a674200SNicolas Dichtel 			h++;
20587a674200SNicolas Dichtel 	}
20597a674200SNicolas Dichtel done:
20607a674200SNicolas Dichtel 	cb->args[0] = h;
20617a674200SNicolas Dichtel 	cb->args[1] = idx;
20627a674200SNicolas Dichtel 
20637a674200SNicolas Dichtel 	return skb->len;
20647a674200SNicolas Dichtel }
20657a674200SNicolas Dichtel 
20661da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20671da177e4SLinus Torvalds 
2068c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
206931be3085SHerbert Xu {
207031be3085SHerbert Xu 	struct net_device *dev;
207131be3085SHerbert Xu 
207231be3085SHerbert Xu 	rcu_read_lock();
2073c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2074c6d14c84SEric Dumazet 		struct in_device *in_dev;
2075c6d14c84SEric Dumazet 
207631be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
207731be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
20789355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2079c6d14c84SEric Dumazet 	}
208031be3085SHerbert Xu 	rcu_read_unlock();
208131be3085SHerbert Xu }
208231be3085SHerbert Xu 
2083c6d14c84SEric Dumazet /* called with RTNL locked */
2084c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
208568dd299bSPavel Emelyanov {
208668dd299bSPavel Emelyanov 	struct net_device *dev;
2087586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
208868dd299bSPavel Emelyanov 
2089586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
20909355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
20913b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
20923b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2093edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2094edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
20953b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
20963b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2097edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2098edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
209968dd299bSPavel Emelyanov 
2100c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
210168dd299bSPavel Emelyanov 		struct in_device *in_dev;
2102fa17806cSEric Dumazet 
21030187bdfbSBen Hutchings 		if (on)
21040187bdfbSBen Hutchings 			dev_disable_lro(dev);
2105fa17806cSEric Dumazet 
2106fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2107edc9e748SNicolas Dichtel 		if (in_dev) {
210868dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
21093b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21103b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2111edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2112edc9e748SNicolas Dichtel 		}
211368dd299bSPavel Emelyanov 	}
211468dd299bSPavel Emelyanov }
211568dd299bSPavel Emelyanov 
2116f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2117f085ff1cSstephen hemminger {
2118f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2119f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2120f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2121f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2122f085ff1cSstephen hemminger 	else {
2123f085ff1cSstephen hemminger 		struct in_device *idev
2124f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2125f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2126f085ff1cSstephen hemminger 	}
2127f085ff1cSstephen hemminger }
2128f085ff1cSstephen hemminger 
2129fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
21308d65af78SAlexey Dobriyan 			     void __user *buffer,
213131be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
213231be3085SHerbert Xu {
2133d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
21348d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2135d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
213631be3085SHerbert Xu 
213731be3085SHerbert Xu 	if (write) {
213831be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2139c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
214031be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2141f085ff1cSstephen hemminger 		int ifindex;
214231be3085SHerbert Xu 
214331be3085SHerbert Xu 		set_bit(i, cnf->state);
214431be3085SHerbert Xu 
21459355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2146c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2147d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2148d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2149d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
21504ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2151f085ff1cSstephen hemminger 
21525cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
21535cbf777cSXin Long 		    new_value != old_value)
21545cbf777cSXin Long 			rt_cache_flush(net);
21555cbf777cSXin Long 
2156cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2157cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2158f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
21593b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21603b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2161cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2162cc535dfbSNicolas Dichtel 		}
2163f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2164f085ff1cSstephen hemminger 		    new_value != old_value) {
2165f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
21663b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21673b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2168f085ff1cSstephen hemminger 						    ifindex, cnf);
2169f085ff1cSstephen hemminger 		}
2170974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2171974d7af5SAndy Gospodarek 		    new_value != old_value) {
2172974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
21733b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21743b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2175974d7af5SAndy Gospodarek 						    ifindex, cnf);
2176974d7af5SAndy Gospodarek 		}
217731be3085SHerbert Xu 	}
217831be3085SHerbert Xu 
217931be3085SHerbert Xu 	return ret;
218031be3085SHerbert Xu }
218131be3085SHerbert Xu 
2182fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
21838d65af78SAlexey Dobriyan 				  void __user *buffer,
21841da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
21851da177e4SLinus Torvalds {
21861da177e4SLinus Torvalds 	int *valp = ctl->data;
21871da177e4SLinus Torvalds 	int val = *valp;
218888af182eSEric W. Biederman 	loff_t pos = *ppos;
21898d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
21901da177e4SLinus Torvalds 
21911da177e4SLinus Torvalds 	if (write && *valp != val) {
2192c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2193c0ce9fb3SPavel Emelyanov 
21940187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
219588af182eSEric W. Biederman 			if (!rtnl_trylock()) {
219688af182eSEric W. Biederman 				/* Restore the original values before restarting */
219788af182eSEric W. Biederman 				*valp = val;
219888af182eSEric W. Biederman 				*ppos = pos;
21999b8adb5eSEric W. Biederman 				return restart_syscall();
220088af182eSEric W. Biederman 			}
22010187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2202c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2203edc9e748SNicolas Dichtel 			} else {
22040187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
22050187bdfbSBen Hutchings 				struct in_device *idev =
22060187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2207edc9e748SNicolas Dichtel 				if (*valp)
22080187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
22093b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2210edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2211edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2212edc9e748SNicolas Dichtel 							    cnf);
22130187bdfbSBen Hutchings 			}
22140187bdfbSBen Hutchings 			rtnl_unlock();
22154ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2216edc9e748SNicolas Dichtel 		} else
22173b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22183b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2219edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2220edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
22210187bdfbSBen Hutchings 	}
22221da177e4SLinus Torvalds 
22231da177e4SLinus Torvalds 	return ret;
22241da177e4SLinus Torvalds }
22251da177e4SLinus Torvalds 
2226fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
22278d65af78SAlexey Dobriyan 				void __user *buffer,
22281da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
22291da177e4SLinus Torvalds {
22301da177e4SLinus Torvalds 	int *valp = ctl->data;
22311da177e4SLinus Torvalds 	int val = *valp;
22328d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
223376e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
22341da177e4SLinus Torvalds 
22351da177e4SLinus Torvalds 	if (write && *valp != val)
22364ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
22371da177e4SLinus Torvalds 
22381da177e4SLinus Torvalds 	return ret;
22391da177e4SLinus Torvalds }
22401da177e4SLinus Torvalds 
2241f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
224242f811b8SHerbert Xu 	{ \
224342f811b8SHerbert Xu 		.procname	= name, \
224442f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
224502291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
224642f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
224742f811b8SHerbert Xu 		.mode		= mval, \
224842f811b8SHerbert Xu 		.proc_handler	= proc, \
224931be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
225042f811b8SHerbert Xu 	}
225142f811b8SHerbert Xu 
225242f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2253f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
225442f811b8SHerbert Xu 
225542f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2256f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
225742f811b8SHerbert Xu 
2258f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2259f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
226042f811b8SHerbert Xu 
226142f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2262f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
226342f811b8SHerbert Xu 
22641da177e4SLinus Torvalds static struct devinet_sysctl_table {
22651da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
226602291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
22671da177e4SLinus Torvalds } devinet_sysctl = {
22681da177e4SLinus Torvalds 	.devinet_vars = {
226942f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2270f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
227142f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
22725cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
227342f811b8SHerbert Xu 
227442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
227542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
227642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
227742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
227842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
227942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
228042f811b8SHerbert Xu 					"accept_source_route"),
22818153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
228228f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
228342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
228442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
228542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
228642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
228742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
228842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
228942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
229042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
229142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2292eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
229365324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
22945c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
22955c6fe01cSWilliam Manley 					"force_igmp_version"),
22962690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
22972690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
22982690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
22992690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
23000eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
23010eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
230297daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
230397daf331SJohannes Berg 					"drop_gratuitous_arp"),
230442f811b8SHerbert Xu 
230542f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
230642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
230742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
230842f811b8SHerbert Xu 					      "promote_secondaries"),
2309d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2310d0daebc3SThomas Graf 					      "route_localnet"),
231112b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
231212b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
23131da177e4SLinus Torvalds 	},
23141da177e4SLinus Torvalds };
23151da177e4SLinus Torvalds 
2316ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
231729c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
23181da177e4SLinus Torvalds {
23191da177e4SLinus Torvalds 	int i;
23209fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
23218607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2322bfada697SPavel Emelyanov 
23239fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
23241da177e4SLinus Torvalds 	if (!t)
23259fa89642SPavel Emelyanov 		goto out;
23269fa89642SPavel Emelyanov 
23271da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
23281da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
232931be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2330c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
23311da177e4SLinus Torvalds 	}
23321da177e4SLinus Torvalds 
23338607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
23341da177e4SLinus Torvalds 
23358607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
23361da177e4SLinus Torvalds 	if (!t->sysctl_header)
23378607ddb8SEric W. Biederman 		goto free;
23381da177e4SLinus Torvalds 
23391da177e4SLinus Torvalds 	p->sysctl = t;
234029c994e3SNicolas Dichtel 
23413b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
23423b022865SDavid Ahern 				    ifindex, p);
2343ea40b324SPavel Emelyanov 	return 0;
23441da177e4SLinus Torvalds 
23451da177e4SLinus Torvalds free:
23461da177e4SLinus Torvalds 	kfree(t);
23479fa89642SPavel Emelyanov out:
2348ea40b324SPavel Emelyanov 	return -ENOBUFS;
23491da177e4SLinus Torvalds }
23501da177e4SLinus Torvalds 
2351b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2352b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
235366f27a52SPavel Emelyanov {
235451602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
235566f27a52SPavel Emelyanov 
2356b5c9641dSDavid Ahern 	if (t) {
235751602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2358ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
23591da177e4SLinus Torvalds 		kfree(t);
23601da177e4SLinus Torvalds 	}
236151602b2aSPavel Emelyanov 
2362b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2363b5c9641dSDavid Ahern }
2364b5c9641dSDavid Ahern 
236520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
236651602b2aSPavel Emelyanov {
236720e61da7SWANG Cong 	int err;
236820e61da7SWANG Cong 
236920e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
237020e61da7SWANG Cong 		return -EINVAL;
237120e61da7SWANG Cong 
237220e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
237320e61da7SWANG Cong 	if (err)
237420e61da7SWANG Cong 		return err;
237520e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
237629c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
237720e61da7SWANG Cong 	if (err)
237820e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
237920e61da7SWANG Cong 	return err;
238051602b2aSPavel Emelyanov }
238151602b2aSPavel Emelyanov 
238251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
238351602b2aSPavel Emelyanov {
2384b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2385b5c9641dSDavid Ahern 
2386b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
238751602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
23881da177e4SLinus Torvalds }
23891da177e4SLinus Torvalds 
239068dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
239168dd299bSPavel Emelyanov 	{
239268dd299bSPavel Emelyanov 		.procname	= "ip_forward",
239368dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
239402291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
239568dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
239668dd299bSPavel Emelyanov 		.mode		= 0644,
239768dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
239868dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2399c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
240068dd299bSPavel Emelyanov 	},
240168dd299bSPavel Emelyanov 	{ },
240268dd299bSPavel Emelyanov };
24032a75de0cSEric Dumazet #endif
240468dd299bSPavel Emelyanov 
2405752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2406752d14dcSPavel Emelyanov {
2407752d14dcSPavel Emelyanov 	int err;
2408752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
24092a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
24102a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2411752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
24122a75de0cSEric Dumazet #endif
2413752d14dcSPavel Emelyanov 
2414752d14dcSPavel Emelyanov 	err = -ENOMEM;
2415752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2416752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2417752d14dcSPavel Emelyanov 
241809ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2419752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
242051456b29SIan Morris 		if (!all)
2421752d14dcSPavel Emelyanov 			goto err_alloc_all;
2422752d14dcSPavel Emelyanov 
2423752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
242451456b29SIan Morris 		if (!dflt)
2425752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2426752d14dcSPavel Emelyanov 
24272a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2428752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
242951456b29SIan Morris 		if (!tbl)
2430752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2431752d14dcSPavel Emelyanov 
243202291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2433752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2434752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
24352a75de0cSEric Dumazet #endif
2436752d14dcSPavel Emelyanov 	}
2437752d14dcSPavel Emelyanov 
2438752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
243929c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2440752d14dcSPavel Emelyanov 	if (err < 0)
2441752d14dcSPavel Emelyanov 		goto err_reg_all;
2442752d14dcSPavel Emelyanov 
244329c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
244429c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2445752d14dcSPavel Emelyanov 	if (err < 0)
2446752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2447752d14dcSPavel Emelyanov 
2448752d14dcSPavel Emelyanov 	err = -ENOMEM;
24498607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
245051456b29SIan Morris 	if (!forw_hdr)
2451752d14dcSPavel Emelyanov 		goto err_reg_ctl;
24522a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2453752d14dcSPavel Emelyanov #endif
2454752d14dcSPavel Emelyanov 
2455752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2456752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2457752d14dcSPavel Emelyanov 	return 0;
2458752d14dcSPavel Emelyanov 
2459752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2460752d14dcSPavel Emelyanov err_reg_ctl:
2461b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2462752d14dcSPavel Emelyanov err_reg_dflt:
2463b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2464752d14dcSPavel Emelyanov err_reg_all:
2465752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2466752d14dcSPavel Emelyanov 		kfree(tbl);
2467752d14dcSPavel Emelyanov err_alloc_ctl:
24682a75de0cSEric Dumazet #endif
2469752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2470752d14dcSPavel Emelyanov 		kfree(dflt);
2471752d14dcSPavel Emelyanov err_alloc_dflt:
2472752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2473752d14dcSPavel Emelyanov 		kfree(all);
2474752d14dcSPavel Emelyanov err_alloc_all:
2475752d14dcSPavel Emelyanov 	return err;
2476752d14dcSPavel Emelyanov }
2477752d14dcSPavel Emelyanov 
2478752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2479752d14dcSPavel Emelyanov {
24802a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2481752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2482752d14dcSPavel Emelyanov 
2483752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2484752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2485b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2486b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2487b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2488b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2489752d14dcSPavel Emelyanov 	kfree(tbl);
24902a75de0cSEric Dumazet #endif
2491752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2492752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2493752d14dcSPavel Emelyanov }
2494752d14dcSPavel Emelyanov 
2495752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2496752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2497752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2498752d14dcSPavel Emelyanov };
2499752d14dcSPavel Emelyanov 
2500207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
25019f0f7272SThomas Graf 	.family		  = AF_INET,
25029f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
25039f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2504cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2505cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
25069f0f7272SThomas Graf };
25079f0f7272SThomas Graf 
25081da177e4SLinus Torvalds void __init devinet_init(void)
25091da177e4SLinus Torvalds {
2510fd23c3b3SDavid S. Miller 	int i;
2511fd23c3b3SDavid S. Miller 
2512fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2513fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2514fd23c3b3SDavid S. Miller 
2515752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2516752d14dcSPavel Emelyanov 
25171da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
25181da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
251963f3444fSThomas Graf 
2520906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
25215c766d64SJiri Pirko 
25229f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
25239f0f7272SThomas Graf 
2524b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2525b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2526b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
25279e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2528b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
25291da177e4SLinus Torvalds }
2530