xref: /openbmc/linux/net/ipv4/devinet.c (revision 978a46fa)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	NET3	IP device support routines.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
1002c30a84SJesper Juhl  * 		Authors:	Ross Biro
111da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
121da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Additional Authors:
151da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
161da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *	Changes:
191da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
201da177e4SLinus Torvalds  *					lists.
211da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
221da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
231da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
241da177e4SLinus Torvalds  *					fall back to comparing just the label
251da177e4SLinus Torvalds  *					if no match found.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds 
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
301da177e4SLinus Torvalds #include <linux/bitops.h>
314fc268d2SRandy Dunlap #include <linux/capability.h>
321da177e4SLinus Torvalds #include <linux/module.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/kernel.h>
35174cd4b1SIngo Molnar #include <linux/sched/signal.h>
361da177e4SLinus Torvalds #include <linux/string.h>
371da177e4SLinus Torvalds #include <linux/mm.h>
381da177e4SLinus Torvalds #include <linux/socket.h>
391da177e4SLinus Torvalds #include <linux/sockios.h>
401da177e4SLinus Torvalds #include <linux/in.h>
411da177e4SLinus Torvalds #include <linux/errno.h>
421da177e4SLinus Torvalds #include <linux/interrupt.h>
431823730fSThomas Graf #include <linux/if_addr.h>
441da177e4SLinus Torvalds #include <linux/if_ether.h>
451da177e4SLinus Torvalds #include <linux/inet.h>
461da177e4SLinus Torvalds #include <linux/netdevice.h>
471da177e4SLinus Torvalds #include <linux/etherdevice.h>
481da177e4SLinus Torvalds #include <linux/skbuff.h>
491da177e4SLinus Torvalds #include <linux/init.h>
501da177e4SLinus Torvalds #include <linux/notifier.h>
511da177e4SLinus Torvalds #include <linux/inetdevice.h>
521da177e4SLinus Torvalds #include <linux/igmp.h>
535a0e3ad6STejun Heo #include <linux/slab.h>
54fd23c3b3SDavid S. Miller #include <linux/hash.h>
551da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
561da177e4SLinus Torvalds #include <linux/sysctl.h>
571da177e4SLinus Torvalds #endif
581da177e4SLinus Torvalds #include <linux/kmod.h>
59edc9e748SNicolas Dichtel #include <linux/netconf.h>
601da177e4SLinus Torvalds 
6114c85021SArnaldo Carvalho de Melo #include <net/arp.h>
621da177e4SLinus Torvalds #include <net/ip.h>
631da177e4SLinus Torvalds #include <net/route.h>
641da177e4SLinus Torvalds #include <net/ip_fib.h>
6563f3444fSThomas Graf #include <net/rtnetlink.h>
66752d14dcSPavel Emelyanov #include <net/net_namespace.h>
675c766d64SJiri Pirko #include <net/addrconf.h>
681da177e4SLinus Torvalds 
690027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7042f811b8SHerbert Xu 	.data = {
7102291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7202291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
752690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
7742f811b8SHerbert Xu 	},
781da177e4SLinus Torvalds };
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8142f811b8SHerbert Xu 	.data = {
8202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
872690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
882690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
8942f811b8SHerbert Xu 	},
901da177e4SLinus Torvalds };
911da177e4SLinus Torvalds 
929355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
939355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9442f811b8SHerbert Xu 
95ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
965c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
975c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
985c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
995176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1005c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
101ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
102af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
103d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
1045c753978SThomas Graf };
1055c753978SThomas Graf 
106978a46faSChristian Brauner struct inet_fill_args {
107978a46faSChristian Brauner 	u32 portid;
108978a46faSChristian Brauner 	u32 seq;
109978a46faSChristian Brauner 	int event;
110978a46faSChristian Brauner 	unsigned int flags;
111978a46faSChristian Brauner 	int netnsid;
112978a46faSChristian Brauner };
113978a46faSChristian Brauner 
11440384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
11540384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
11640384999SEric Dumazet 
117fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
118fd23c3b3SDavid S. Miller 
1196eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
120fd23c3b3SDavid S. Miller {
12140384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
122fd23c3b3SDavid S. Miller 
12340384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
124fd23c3b3SDavid S. Miller }
125fd23c3b3SDavid S. Miller 
126fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
127fd23c3b3SDavid S. Miller {
12840384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
129fd23c3b3SDavid S. Miller 
13032a4be48SWANG Cong 	ASSERT_RTNL();
131fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
132fd23c3b3SDavid S. Miller }
133fd23c3b3SDavid S. Miller 
134fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
135fd23c3b3SDavid S. Miller {
13632a4be48SWANG Cong 	ASSERT_RTNL();
137fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
138fd23c3b3SDavid S. Miller }
139fd23c3b3SDavid S. Miller 
1409435eb1cSDavid S. Miller /**
1419435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1429435eb1cSDavid S. Miller  * @net: the net namespace
1439435eb1cSDavid S. Miller  * @addr: the source address
1449435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1459435eb1cSDavid S. Miller  *
1469435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1479435eb1cSDavid S. Miller  */
1489435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1499435eb1cSDavid S. Miller {
1509435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1519435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1529435eb1cSDavid S. Miller 
1539435eb1cSDavid S. Miller 	rcu_read_lock();
1546e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1556e617de8SPaolo Abeni 	if (!ifa) {
156406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
157406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
158406b6f97SDavid S. Miller 		struct fib_table *local;
159406b6f97SDavid S. Miller 
160406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
161406b6f97SDavid S. Miller 		 * over loopback subnets work.
162406b6f97SDavid S. Miller 		 */
163406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
164406b6f97SDavid S. Miller 		if (local &&
165406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
166406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
167406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1686e617de8SPaolo Abeni 	} else {
1696e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
170406b6f97SDavid S. Miller 	}
1719435eb1cSDavid S. Miller 	if (result && devref)
1729435eb1cSDavid S. Miller 		dev_hold(result);
1739435eb1cSDavid S. Miller 	rcu_read_unlock();
1749435eb1cSDavid S. Miller 	return result;
1759435eb1cSDavid S. Miller }
1769435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1779435eb1cSDavid S. Miller 
1786e617de8SPaolo Abeni /* called under RCU lock */
1796e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1806e617de8SPaolo Abeni {
1816e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1826e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1836e617de8SPaolo Abeni 
1846e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1856e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1866e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1876e617de8SPaolo Abeni 			return ifa;
1886e617de8SPaolo Abeni 
1896e617de8SPaolo Abeni 	return NULL;
1906e617de8SPaolo Abeni }
1916e617de8SPaolo Abeni 
192d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1931da177e4SLinus Torvalds 
194e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1953ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
1961da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1971da177e4SLinus Torvalds 			 int destroy);
1981da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
19920e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20051602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20151602b2aSPavel Emelyanov #else
20220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20351602b2aSPavel Emelyanov {
20420e61da7SWANG Cong 	return 0;
20551602b2aSPavel Emelyanov }
20640384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
20751602b2aSPavel Emelyanov {
20851602b2aSPavel Emelyanov }
2091da177e4SLinus Torvalds #endif
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds /* Locks all the inet devices. */
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2141da177e4SLinus Torvalds {
21593adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2191da177e4SLinus Torvalds {
2201da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2211da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2221da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2231da177e4SLinus Torvalds 	kfree(ifa);
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds 
22640384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2321da177e4SLinus Torvalds {
2331da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2341da177e4SLinus Torvalds 
235547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
236547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
237e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2381da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
23991df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2401da177e4SLinus Torvalds #endif
2411da177e4SLinus Torvalds 	dev_put(dev);
2421da177e4SLinus Torvalds 	if (!idev->dead)
2439f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2449f9354b9SEric Dumazet 	else
2451da177e4SLinus Torvalds 		kfree(idev);
2461da177e4SLinus Torvalds }
2479f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2481da177e4SLinus Torvalds 
24971e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds 	struct in_device *in_dev;
25220e61da7SWANG Cong 	int err = -ENOMEM;
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	ASSERT_RTNL();
2551da177e4SLinus Torvalds 
2560da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2571da177e4SLinus Torvalds 	if (!in_dev)
2581da177e4SLinus Torvalds 		goto out;
259c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2609355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2611da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2621da177e4SLinus Torvalds 	in_dev->dev = dev;
2639f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2649f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2651da177e4SLinus Torvalds 		goto out_kfree;
2660187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2670187bdfbSBen Hutchings 		dev_disable_lro(dev);
2681da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2691da177e4SLinus Torvalds 	dev_hold(dev);
27030c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2717658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2721da177e4SLinus Torvalds 
27320e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
27420e61da7SWANG Cong 	if (err) {
27520e61da7SWANG Cong 		in_dev->dead = 1;
27620e61da7SWANG Cong 		in_dev_put(in_dev);
27720e61da7SWANG Cong 		in_dev = NULL;
27820e61da7SWANG Cong 		goto out;
27920e61da7SWANG Cong 	}
2801da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2811da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2821da177e4SLinus Torvalds 		ip_mc_up(in_dev);
283483479ecSJarek Poplawski 
28430c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
285cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
286483479ecSJarek Poplawski out:
28720e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2881da177e4SLinus Torvalds out_kfree:
2891da177e4SLinus Torvalds 	kfree(in_dev);
2901da177e4SLinus Torvalds 	in_dev = NULL;
2911da177e4SLinus Torvalds 	goto out;
2921da177e4SLinus Torvalds }
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2951da177e4SLinus Torvalds {
2961da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2971da177e4SLinus Torvalds 	in_dev_put(idev);
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
3031da177e4SLinus Torvalds 	struct net_device *dev;
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	ASSERT_RTNL();
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	dev = in_dev->dev;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	in_dev->dead = 1;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
3141da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3151da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3161da177e4SLinus Torvalds 	}
3171da177e4SLinus Torvalds 
318a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3191da177e4SLinus Torvalds 
32051602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3211da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3221da177e4SLinus Torvalds 	arp_ifdown(dev);
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3251da177e4SLinus Torvalds }
3261da177e4SLinus Torvalds 
327ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3281da177e4SLinus Torvalds {
3291da177e4SLinus Torvalds 	rcu_read_lock();
3301da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3311da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3321da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3331da177e4SLinus Torvalds 				rcu_read_unlock();
3341da177e4SLinus Torvalds 				return 1;
3351da177e4SLinus Torvalds 			}
3361da177e4SLinus Torvalds 		}
3371da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3381da177e4SLinus Torvalds 	rcu_read_unlock();
3391da177e4SLinus Torvalds 	return 0;
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds 
342d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
34315e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3441da177e4SLinus Torvalds {
3458f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3460ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3470ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3480ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3490ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	ASSERT_RTNL();
3521da177e4SLinus Torvalds 
353fbd40ea0SDavid S. Miller 	if (in_dev->dead)
354fbd40ea0SDavid S. Miller 		goto no_promotions;
355fbd40ea0SDavid S. Miller 
3568f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3578f937c60SHarald Welte 	 * unless alias promotion is set
3588f937c60SHarald Welte 	 **/
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3611da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3640ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3650ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3660ff60a45SJamal Hadi Salim 				last_prim = ifa;
3670ff60a45SJamal Hadi Salim 
3681da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3691da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3701da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3711da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3720ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3731da177e4SLinus Torvalds 				continue;
3741da177e4SLinus Torvalds 			}
3751da177e4SLinus Torvalds 
3760ff60a45SJamal Hadi Salim 			if (!do_promote) {
377fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3781da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3791da177e4SLinus Torvalds 
38015e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
381e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
382e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3831da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3848f937c60SHarald Welte 			} else {
3858f937c60SHarald Welte 				promote = ifa;
3868f937c60SHarald Welte 				break;
3878f937c60SHarald Welte 			}
3881da177e4SLinus Torvalds 		}
3891da177e4SLinus Torvalds 	}
3901da177e4SLinus Torvalds 
3912d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3922d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3932d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3942d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3952d230e2bSJulian Anastasov 	 */
3962d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3972d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3982d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3992d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4002d230e2bSJulian Anastasov 	}
4012d230e2bSJulian Anastasov 
402fbd40ea0SDavid S. Miller no_promotions:
4031da177e4SLinus Torvalds 	/* 2. Unlink it */
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
406fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4111da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4121da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4131da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4141da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4151da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4161da177e4SLinus Torvalds 	   So that, this order is correct.
4171da177e4SLinus Torvalds 	 */
41815e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
419e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4200ff60a45SJamal Hadi Salim 
4210ff60a45SJamal Hadi Salim 	if (promote) {
42204024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
4230ff60a45SJamal Hadi Salim 
4240ff60a45SJamal Hadi Salim 		if (prev_prom) {
4250ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
4260ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
4270ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4280ff60a45SJamal Hadi Salim 		}
4290ff60a45SJamal Hadi Salim 
4300ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
43115e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
432e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
433e041c683SAlan Stern 				NETDEV_UP, promote);
43404024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4350ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4360ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4370ff60a45SJamal Hadi Salim 					continue;
4380ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4390ff60a45SJamal Hadi Salim 		}
4400ff60a45SJamal Hadi Salim 
4410ff60a45SJamal Hadi Salim 	}
4426363097cSHerbert Xu 	if (destroy)
4431da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds 
446d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
447d6062cbbSThomas Graf 			 int destroy)
448d6062cbbSThomas Graf {
449d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
450d6062cbbSThomas Graf }
451d6062cbbSThomas Graf 
4525c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4535c766d64SJiri Pirko 
4545c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4555c766d64SJiri Pirko 
456d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
457de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4581da177e4SLinus Torvalds {
4591da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4601da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4613ad7d246SKrister Johansen 	struct in_validator_info ivi;
4623ad7d246SKrister Johansen 	int ret;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	ASSERT_RTNL();
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4671da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4681da177e4SLinus Torvalds 		return 0;
4691da177e4SLinus Torvalds 	}
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4721da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4751da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4761da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4771da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4781da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4791da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4801da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4811da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4821da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4831da177e4SLinus Torvalds 				return -EEXIST;
4841da177e4SLinus Torvalds 			}
4851da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4861da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4871da177e4SLinus Torvalds 				return -EINVAL;
4881da177e4SLinus Torvalds 			}
4891da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4901da177e4SLinus Torvalds 		}
4911da177e4SLinus Torvalds 	}
4921da177e4SLinus Torvalds 
4933ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
4943ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
4953ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
4963ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
4973ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
4983ad7d246SKrister Johansen 	 * not the flags.
4993ad7d246SKrister Johansen 	 */
5003ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5013ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
502de95e047SDavid Ahern 	ivi.extack = extack;
5033ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5043ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5053ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5063ad7d246SKrister Johansen 	if (ret) {
5073ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5083ad7d246SKrister Johansen 		return ret;
5093ad7d246SKrister Johansen 	}
5103ad7d246SKrister Johansen 
5111da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
51263862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
5131da177e4SLinus Torvalds 		ifap = last_primary;
5141da177e4SLinus Torvalds 	}
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
5171da177e4SLinus Torvalds 	*ifap = ifa;
5181da177e4SLinus Torvalds 
519fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
520fd23c3b3SDavid S. Miller 
5215c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
522906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5235c766d64SJiri Pirko 
5241da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5251da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5261da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
52715e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
528e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 	return 0;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds 
533d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
534d6062cbbSThomas Graf {
535de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
536d6062cbbSThomas Graf }
537d6062cbbSThomas Graf 
5381da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5391da177e4SLinus Torvalds {
540e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 	ASSERT_RTNL();
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	if (!in_dev) {
5451da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5461da177e4SLinus Torvalds 		return -ENOBUFS;
5471da177e4SLinus Torvalds 	}
54871e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5491d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5501da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
551547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5521da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5531da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5541da177e4SLinus Torvalds 	}
555f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5561da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5571da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5581da177e4SLinus Torvalds }
5591da177e4SLinus Torvalds 
5608723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5618723e1b4SEric Dumazet  * We dont take a reference on found in_device
5628723e1b4SEric Dumazet  */
5637fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5641da177e4SLinus Torvalds {
5651da177e4SLinus Torvalds 	struct net_device *dev;
5661da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
567c148fc2eSEric Dumazet 
568c148fc2eSEric Dumazet 	rcu_read_lock();
569c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5701da177e4SLinus Torvalds 	if (dev)
5718723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
572c148fc2eSEric Dumazet 	rcu_read_unlock();
5731da177e4SLinus Torvalds 	return in_dev;
5741da177e4SLinus Torvalds }
5759f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5761da177e4SLinus Torvalds 
5771da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5781da177e4SLinus Torvalds 
57960cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
58060cad5daSAl Viro 				    __be32 mask)
5811da177e4SLinus Torvalds {
5821da177e4SLinus Torvalds 	ASSERT_RTNL();
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5851da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5861da177e4SLinus Torvalds 			return ifa;
5871da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5881da177e4SLinus Torvalds 	return NULL;
5891da177e4SLinus Torvalds }
5901da177e4SLinus Torvalds 
59193a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
59293a714d6SMadhu Challa {
59393a714d6SMadhu Challa 	struct ip_mreqn mreq = {
59493a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
59593a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
59693a714d6SMadhu Challa 	};
59793a714d6SMadhu Challa 	int ret;
59893a714d6SMadhu Challa 
59993a714d6SMadhu Challa 	ASSERT_RTNL();
60093a714d6SMadhu Challa 
60193a714d6SMadhu Challa 	lock_sock(sk);
60293a714d6SMadhu Challa 	if (join)
60354ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
60493a714d6SMadhu Challa 	else
60554ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
60693a714d6SMadhu Challa 	release_sock(sk);
60793a714d6SMadhu Challa 
60893a714d6SMadhu Challa 	return ret;
60993a714d6SMadhu Challa }
61093a714d6SMadhu Challa 
611c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
612c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6131da177e4SLinus Torvalds {
6143b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
615dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6161da177e4SLinus Torvalds 	struct in_device *in_dev;
617dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6181da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
619dfdd5fd4SThomas Graf 	int err = -EINVAL;
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	ASSERT_RTNL();
6221da177e4SLinus Torvalds 
623fceb6435SJohannes Berg 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy,
624c21ef3e3SDavid Ahern 			  extack);
625dfdd5fd4SThomas Graf 	if (err < 0)
626dfdd5fd4SThomas Graf 		goto errout;
627dfdd5fd4SThomas Graf 
628dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6297fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
63051456b29SIan Morris 	if (!in_dev) {
631dfdd5fd4SThomas Graf 		err = -ENODEV;
632dfdd5fd4SThomas Graf 		goto errout;
633dfdd5fd4SThomas Graf 	}
634dfdd5fd4SThomas Graf 
6351da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
6361da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
637dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
63867b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6391da177e4SLinus Torvalds 			continue;
640dfdd5fd4SThomas Graf 
641dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
642dfdd5fd4SThomas Graf 			continue;
643dfdd5fd4SThomas Graf 
644dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
645dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
64667b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
647dfdd5fd4SThomas Graf 			continue;
648dfdd5fd4SThomas Graf 
64993a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
65093a714d6SMadhu Challa 			ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
65115e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6521da177e4SLinus Torvalds 		return 0;
6531da177e4SLinus Torvalds 	}
654dfdd5fd4SThomas Graf 
655dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
656dfdd5fd4SThomas Graf errout:
657dfdd5fd4SThomas Graf 	return err;
6581da177e4SLinus Torvalds }
6591da177e4SLinus Torvalds 
6605c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6615c766d64SJiri Pirko 
6625c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6635c766d64SJiri Pirko {
6645c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6655c766d64SJiri Pirko 	struct in_ifaddr *ifa;
666c988d1e8SJiri Pirko 	struct hlist_node *n;
6675c766d64SJiri Pirko 	int i;
6685c766d64SJiri Pirko 
6695c766d64SJiri Pirko 	now = jiffies;
6705c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6715c766d64SJiri Pirko 
6725c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
673c988d1e8SJiri Pirko 		bool change_needed = false;
674c988d1e8SJiri Pirko 
675c988d1e8SJiri Pirko 		rcu_read_lock();
676b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6775c766d64SJiri Pirko 			unsigned long age;
6785c766d64SJiri Pirko 
6795c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6805c766d64SJiri Pirko 				continue;
6815c766d64SJiri Pirko 
6825c766d64SJiri Pirko 			/* We try to batch several events at once. */
6835c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6845c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6855c766d64SJiri Pirko 
6865c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6875c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
688c988d1e8SJiri Pirko 				change_needed = true;
689c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
690c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
691c988d1e8SJiri Pirko 				continue;
692c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
693c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
694c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
695c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
696c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
697c988d1e8SJiri Pirko 
698c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
699c988d1e8SJiri Pirko 					change_needed = true;
700c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
701c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
702c988d1e8SJiri Pirko 					       next)) {
703c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
704c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
705c988d1e8SJiri Pirko 			}
706c988d1e8SJiri Pirko 		}
707c988d1e8SJiri Pirko 		rcu_read_unlock();
708c988d1e8SJiri Pirko 		if (!change_needed)
709c988d1e8SJiri Pirko 			continue;
710c988d1e8SJiri Pirko 		rtnl_lock();
711c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
712c988d1e8SJiri Pirko 			unsigned long age;
713c988d1e8SJiri Pirko 
714c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
715c988d1e8SJiri Pirko 				continue;
716c988d1e8SJiri Pirko 
717c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
718c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
719c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
720c988d1e8SJiri Pirko 
721c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
722c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7235c766d64SJiri Pirko 				struct in_ifaddr **ifap;
7245c766d64SJiri Pirko 
7255c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
726c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
727c988d1e8SJiri Pirko 					if (*ifap == ifa) {
7285c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7295c766d64SJiri Pirko 							     ifap, 1);
730c988d1e8SJiri Pirko 						break;
7315c766d64SJiri Pirko 					}
732c988d1e8SJiri Pirko 				}
733c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
734c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
735c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
736c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7375c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7385c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7395c766d64SJiri Pirko 			}
7405c766d64SJiri Pirko 		}
741c988d1e8SJiri Pirko 		rtnl_unlock();
7425c766d64SJiri Pirko 	}
7435c766d64SJiri Pirko 
7445c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7455c766d64SJiri Pirko 	next_sched = next;
7465c766d64SJiri Pirko 
7475c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7485c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7495c766d64SJiri Pirko 		next_sched = next_sec;
7505c766d64SJiri Pirko 
7515c766d64SJiri Pirko 	now = jiffies;
7525c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7535c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7545c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7555c766d64SJiri Pirko 
756906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
757906e073fSviresh kumar 			next_sched - now);
7585c766d64SJiri Pirko }
7595c766d64SJiri Pirko 
7605c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7615c766d64SJiri Pirko 			     __u32 prefered_lft)
7625c766d64SJiri Pirko {
7635c766d64SJiri Pirko 	unsigned long timeout;
7645c766d64SJiri Pirko 
7655c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
7665c766d64SJiri Pirko 
7675c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
7685c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
7695c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7705c766d64SJiri Pirko 	else
7715c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
7725c766d64SJiri Pirko 
7735c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
7745c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
7755c766d64SJiri Pirko 		if (timeout == 0)
7765c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7775c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7785c766d64SJiri Pirko 	}
7795c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7805c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7815c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7825c766d64SJiri Pirko }
7835c766d64SJiri Pirko 
7845c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
7855c766d64SJiri Pirko 				       __u32 *pvalid_lft, __u32 *pprefered_lft)
7861da177e4SLinus Torvalds {
7875c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7885c753978SThomas Graf 	struct in_ifaddr *ifa;
7895c753978SThomas Graf 	struct ifaddrmsg *ifm;
7901da177e4SLinus Torvalds 	struct net_device *dev;
7911da177e4SLinus Torvalds 	struct in_device *in_dev;
7927b218574SDenis V. Lunev 	int err;
7931da177e4SLinus Torvalds 
794fceb6435SJohannes Berg 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy,
795fceb6435SJohannes Berg 			  NULL);
7965c753978SThomas Graf 	if (err < 0)
7975c753978SThomas Graf 		goto errout;
7981da177e4SLinus Torvalds 
7995c753978SThomas Graf 	ifm = nlmsg_data(nlh);
800c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
80151456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8025c753978SThomas Graf 		goto errout;
8031da177e4SLinus Torvalds 
8044b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8055c753978SThomas Graf 	err = -ENODEV;
80651456b29SIan Morris 	if (!dev)
8075c753978SThomas Graf 		goto errout;
8081da177e4SLinus Torvalds 
8095c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8105c753978SThomas Graf 	err = -ENOBUFS;
81151456b29SIan Morris 	if (!in_dev)
8125c753978SThomas Graf 		goto errout;
81371e27da9SHerbert Xu 
8145c753978SThomas Graf 	ifa = inet_alloc_ifa();
81551456b29SIan Morris 	if (!ifa)
8165c753978SThomas Graf 		/*
8175c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8185c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8195c753978SThomas Graf 		 */
8205c753978SThomas Graf 		goto errout;
8215c753978SThomas Graf 
822a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8231d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8245c753978SThomas Graf 	in_dev_hold(in_dev);
8255c753978SThomas Graf 
82651456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8275c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8285c753978SThomas Graf 
829fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8301da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8311da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
832ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
833ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8341da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8351da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8365c753978SThomas Graf 
83767b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
83867b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8395c753978SThomas Graf 
8405c753978SThomas Graf 	if (tb[IFA_BROADCAST])
84167b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8425c753978SThomas Graf 
8435c753978SThomas Graf 	if (tb[IFA_LABEL])
8445c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8451da177e4SLinus Torvalds 	else
8461da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8471da177e4SLinus Torvalds 
848af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
849af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
850af4d768aSDavid Ahern 
8515c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8525c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8535c766d64SJiri Pirko 
8545c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8555c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8565c766d64SJiri Pirko 			err = -EINVAL;
857446266b0SDaniel Borkmann 			goto errout_free;
8585c766d64SJiri Pirko 		}
8595c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8605c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8615c766d64SJiri Pirko 	}
8625c766d64SJiri Pirko 
8635c753978SThomas Graf 	return ifa;
8645c753978SThomas Graf 
865446266b0SDaniel Borkmann errout_free:
866446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8675c753978SThomas Graf errout:
8685c753978SThomas Graf 	return ERR_PTR(err);
8695c753978SThomas Graf }
8705c753978SThomas Graf 
8715c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
8725c766d64SJiri Pirko {
8735c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8745c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8755c766d64SJiri Pirko 
8765c766d64SJiri Pirko 	if (!ifa->ifa_local)
8775c766d64SJiri Pirko 		return NULL;
8785c766d64SJiri Pirko 
8795c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8805c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8815c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8825c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8835c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8845c766d64SJiri Pirko 			return ifa1;
8855c766d64SJiri Pirko 	}
8865c766d64SJiri Pirko 	return NULL;
8875c766d64SJiri Pirko }
8885c766d64SJiri Pirko 
889c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
890c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
8915c753978SThomas Graf {
8923b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8935c753978SThomas Graf 	struct in_ifaddr *ifa;
8945c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8955c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8965c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8975c753978SThomas Graf 
8985c753978SThomas Graf 	ASSERT_RTNL();
8995c753978SThomas Graf 
9005c766d64SJiri Pirko 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
9015c753978SThomas Graf 	if (IS_ERR(ifa))
9025c753978SThomas Graf 		return PTR_ERR(ifa);
9035c753978SThomas Graf 
9045c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9055c766d64SJiri Pirko 	if (!ifa_existing) {
9065c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
907614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9085c766d64SJiri Pirko 		 */
9095c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
91093a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
91193a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
91293a714d6SMadhu Challa 					       true, ifa);
91393a714d6SMadhu Challa 
91493a714d6SMadhu Challa 			if (ret < 0) {
91593a714d6SMadhu Challa 				inet_free_ifa(ifa);
91693a714d6SMadhu Challa 				return ret;
91793a714d6SMadhu Challa 			}
91893a714d6SMadhu Challa 		}
919de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
920de95e047SDavid Ahern 					 extack);
9215c766d64SJiri Pirko 	} else {
922af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
923af4d768aSDavid Ahern 
9245c766d64SJiri Pirko 		inet_free_ifa(ifa);
9255c766d64SJiri Pirko 
9265c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9275c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9285c766d64SJiri Pirko 			return -EEXIST;
92934e2ed34SJiri Pirko 		ifa = ifa_existing;
930af4d768aSDavid Ahern 
931af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
932af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
933af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
934af4d768aSDavid Ahern 		}
935af4d768aSDavid Ahern 
93634e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
93705a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
938906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
939906e073fSviresh kumar 				&check_lifetime_work, 0);
94034e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9415c766d64SJiri Pirko 	}
9425c766d64SJiri Pirko 	return 0;
9431da177e4SLinus Torvalds }
9441da177e4SLinus Torvalds 
9451da177e4SLinus Torvalds /*
9461da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9471da177e4SLinus Torvalds  */
9481da177e4SLinus Torvalds 
94940384999SEric Dumazet static int inet_abc_len(__be32 addr)
9501da177e4SLinus Torvalds {
9511da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9521da177e4SLinus Torvalds 
953f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
9541da177e4SLinus Torvalds 		rc = 0;
9551da177e4SLinus Torvalds 	else {
956714e85beSAl Viro 		__u32 haddr = ntohl(addr);
9571da177e4SLinus Torvalds 
958714e85beSAl Viro 		if (IN_CLASSA(haddr))
9591da177e4SLinus Torvalds 			rc = 8;
960714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9611da177e4SLinus Torvalds 			rc = 16;
962714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9631da177e4SLinus Torvalds 			rc = 24;
9641da177e4SLinus Torvalds 	}
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds 	return rc;
9671da177e4SLinus Torvalds }
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds 
97003aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
9711da177e4SLinus Torvalds {
9721da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
97303aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
9741da177e4SLinus Torvalds 	struct in_device *in_dev;
9751da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
9761da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
9771da177e4SLinus Torvalds 	struct net_device *dev;
9781da177e4SLinus Torvalds 	char *colon;
9791da177e4SLinus Torvalds 	int ret = -EFAULT;
9801da177e4SLinus Torvalds 	int tryaddrmatch = 0;
9811da177e4SLinus Torvalds 
98203aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	/* save original address for comparison */
9851da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9861da177e4SLinus Torvalds 
98703aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
9881da177e4SLinus Torvalds 	if (colon)
9891da177e4SLinus Torvalds 		*colon = 0;
9901da177e4SLinus Torvalds 
99103aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	switch (cmd) {
9941da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9951da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9961da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9971da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9981da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
9991da177e4SLinus Torvalds 		   so that we do not impose a lock.
10001da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10011da177e4SLinus Torvalds 		 */
10021da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10031da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10041da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10051da177e4SLinus Torvalds 		break;
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1008bf5b30b8SZhao Hongjiang 		ret = -EPERM;
100952e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10101da177e4SLinus Torvalds 			goto out;
10111da177e4SLinus Torvalds 		break;
10121da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10131da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10141da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10151da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1016bf5b30b8SZhao Hongjiang 		ret = -EPERM;
101752e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10181da177e4SLinus Torvalds 			goto out;
10191da177e4SLinus Torvalds 		ret = -EINVAL;
10201da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10211da177e4SLinus Torvalds 			goto out;
10221da177e4SLinus Torvalds 		break;
10231da177e4SLinus Torvalds 	default:
10241da177e4SLinus Torvalds 		ret = -EINVAL;
10251da177e4SLinus Torvalds 		goto out;
10261da177e4SLinus Torvalds 	}
10271da177e4SLinus Torvalds 
10281da177e4SLinus Torvalds 	rtnl_lock();
10291da177e4SLinus Torvalds 
10301da177e4SLinus Torvalds 	ret = -ENODEV;
103103aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10329f9354b9SEric Dumazet 	if (!dev)
10331da177e4SLinus Torvalds 		goto done;
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	if (colon)
10361da177e4SLinus Torvalds 		*colon = ':';
10371da177e4SLinus Torvalds 
10389f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10399f9354b9SEric Dumazet 	if (in_dev) {
10401da177e4SLinus Torvalds 		if (tryaddrmatch) {
10411da177e4SLinus Torvalds 			/* Matthias Andree */
10421da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10431da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10441da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10451da177e4SLinus Torvalds 			   This is checked above. */
10461da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10471da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
104803aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10491da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10506c91afe1SDavid S. Miller 							ifa->ifa_local) {
10511da177e4SLinus Torvalds 					break; /* found */
10521da177e4SLinus Torvalds 				}
10531da177e4SLinus Torvalds 			}
10541da177e4SLinus Torvalds 		}
10551da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10561da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10571da177e4SLinus Torvalds 		   comparing just the label */
10581da177e4SLinus Torvalds 		if (!ifa) {
10591da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10601da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
106103aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
10621da177e4SLinus Torvalds 					break;
10631da177e4SLinus Torvalds 		}
10641da177e4SLinus Torvalds 	}
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10671da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
10681da177e4SLinus Torvalds 		goto done;
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	switch (cmd) {
10711da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
107230e948a3STonghao Zhang 		ret = 0;
10731da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
107403aef17bSAl Viro 		break;
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
107730e948a3STonghao Zhang 		ret = 0;
10781da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
107903aef17bSAl Viro 		break;
10801da177e4SLinus Torvalds 
10811da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
108230e948a3STonghao Zhang 		ret = 0;
10831da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
108403aef17bSAl Viro 		break;
10851da177e4SLinus Torvalds 
10861da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
108730e948a3STonghao Zhang 		ret = 0;
10881da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
108903aef17bSAl Viro 		break;
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10921da177e4SLinus Torvalds 		if (colon) {
10931da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
10941da177e4SLinus Torvalds 			if (!ifa)
10951da177e4SLinus Torvalds 				break;
10961da177e4SLinus Torvalds 			ret = 0;
109703aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
10981da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
10991da177e4SLinus Torvalds 			break;
11001da177e4SLinus Torvalds 		}
110103aef17bSAl Viro 		ret = dev_change_flags(dev, ifr->ifr_flags);
11021da177e4SLinus Torvalds 		break;
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11051da177e4SLinus Torvalds 		ret = -EINVAL;
11061da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11071da177e4SLinus Torvalds 			break;
11081da177e4SLinus Torvalds 
11091da177e4SLinus Torvalds 		if (!ifa) {
11101da177e4SLinus Torvalds 			ret = -ENOBUFS;
11119f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11129f9354b9SEric Dumazet 			if (!ifa)
11131da177e4SLinus Torvalds 				break;
1114c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11151da177e4SLinus Torvalds 			if (colon)
111603aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11171da177e4SLinus Torvalds 			else
11181da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11191da177e4SLinus Torvalds 		} else {
11201da177e4SLinus Torvalds 			ret = 0;
11211da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11221da177e4SLinus Torvalds 				break;
11231da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11241da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1125148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11261da177e4SLinus Torvalds 		}
11271da177e4SLinus Torvalds 
11281da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11291da177e4SLinus Torvalds 
11301da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11311da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11321da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11331da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11341da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11351da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11361da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11371da177e4SLinus Torvalds 		} else {
11381da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11391da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11401da177e4SLinus Torvalds 		}
11415c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11421da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11431da177e4SLinus Torvalds 		break;
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11461da177e4SLinus Torvalds 		ret = 0;
11471da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11481da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11491da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11501da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11511da177e4SLinus Torvalds 		}
11521da177e4SLinus Torvalds 		break;
11531da177e4SLinus Torvalds 
11541da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11551da177e4SLinus Torvalds 		ret = 0;
11561da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11571da177e4SLinus Torvalds 			break;
11581da177e4SLinus Torvalds 		ret = -EINVAL;
11591da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11601da177e4SLinus Torvalds 			break;
11611da177e4SLinus Torvalds 		ret = 0;
11621da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11631da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11641da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11651da177e4SLinus Torvalds 		break;
11661da177e4SLinus Torvalds 
11671da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 		/*
11701da177e4SLinus Torvalds 		 *	The mask we set must be legal.
11711da177e4SLinus Torvalds 		 */
11721da177e4SLinus Torvalds 		ret = -EINVAL;
11731da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
11741da177e4SLinus Torvalds 			break;
11751da177e4SLinus Torvalds 		ret = 0;
11761da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1177a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
11781da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11791da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
11801da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds 			/* See if current broadcast address matches
11831da177e4SLinus Torvalds 			 * with current netmask, then recalculate
11841da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
11851da177e4SLinus Torvalds 			 * funny address, so don't touch it since
11861da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
11871da177e4SLinus Torvalds 			 */
11881da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11891da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11901da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1191dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11921da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
11931da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
11941da177e4SLinus Torvalds 			}
11951da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11961da177e4SLinus Torvalds 		}
11971da177e4SLinus Torvalds 		break;
11981da177e4SLinus Torvalds 	}
11991da177e4SLinus Torvalds done:
12001da177e4SLinus Torvalds 	rtnl_unlock();
12011da177e4SLinus Torvalds out:
12021da177e4SLinus Torvalds 	return ret;
12031da177e4SLinus Torvalds }
12041da177e4SLinus Torvalds 
120536fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12061da177e4SLinus Torvalds {
1207e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
12081da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12091da177e4SLinus Torvalds 	struct ifreq ifr;
12101da177e4SLinus Torvalds 	int done = 0;
12111da177e4SLinus Torvalds 
121236fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
121336fd633eSAl Viro 		goto out;
121436fd633eSAl Viro 
12159f9354b9SEric Dumazet 	if (!in_dev)
12161da177e4SLinus Torvalds 		goto out;
12171da177e4SLinus Torvalds 
12189f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12191da177e4SLinus Torvalds 		if (!buf) {
122036fd633eSAl Viro 			done += size;
12211da177e4SLinus Torvalds 			continue;
12221da177e4SLinus Torvalds 		}
122336fd633eSAl Viro 		if (len < size)
12241da177e4SLinus Torvalds 			break;
12251da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12261da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12291da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12301da177e4SLinus Torvalds 								ifa->ifa_local;
12311da177e4SLinus Torvalds 
123236fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12331da177e4SLinus Torvalds 			done = -EFAULT;
12341da177e4SLinus Torvalds 			break;
12351da177e4SLinus Torvalds 		}
123636fd633eSAl Viro 		len  -= size;
123736fd633eSAl Viro 		done += size;
12381da177e4SLinus Torvalds 	}
12391da177e4SLinus Torvalds out:
12401da177e4SLinus Torvalds 	return done;
12411da177e4SLinus Torvalds }
12421da177e4SLinus Torvalds 
12438b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12448b57fd1eSGao Feng 				 int scope)
12458b57fd1eSGao Feng {
12468b57fd1eSGao Feng 	for_primary_ifa(in_dev) {
12478b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12488b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12498b57fd1eSGao Feng 			return ifa->ifa_local;
12508b57fd1eSGao Feng 	} endfor_ifa(in_dev);
12518b57fd1eSGao Feng 
12528b57fd1eSGao Feng 	return 0;
12538b57fd1eSGao Feng }
12548b57fd1eSGao Feng 
1255a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
12561da177e4SLinus Torvalds {
1257a61ced5dSAl Viro 	__be32 addr = 0;
12581da177e4SLinus Torvalds 	struct in_device *in_dev;
1259c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12603f2fb9a8SDavid Ahern 	int master_idx;
12611da177e4SLinus Torvalds 
12621da177e4SLinus Torvalds 	rcu_read_lock();
1263e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
12641da177e4SLinus Torvalds 	if (!in_dev)
12651da177e4SLinus Torvalds 		goto no_in_dev;
12661da177e4SLinus Torvalds 
12671da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
12681da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
12691da177e4SLinus Torvalds 			continue;
12701da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
12711da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12721da177e4SLinus Torvalds 			break;
12731da177e4SLinus Torvalds 		}
12741da177e4SLinus Torvalds 		if (!addr)
12751da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12761da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds 	if (addr)
1279c6d14c84SEric Dumazet 		goto out_unlock;
12809f9354b9SEric Dumazet no_in_dev:
12813f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
12821da177e4SLinus Torvalds 
128317b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
128417b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
128517b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
128617b693cdSDavid Lamparter 	 * equality check in the loop below.
128717b693cdSDavid Lamparter 	 */
128817b693cdSDavid Lamparter 	if (master_idx &&
128917b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
129017b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
12918b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
12928b57fd1eSGao Feng 		if (addr)
129317b693cdSDavid Lamparter 			goto out_unlock;
129417b693cdSDavid Lamparter 	}
129517b693cdSDavid Lamparter 
12961da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1297ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
12981da177e4SLinus Torvalds 	   in dev_base list.
12991da177e4SLinus Torvalds 	 */
1300c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13013f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13023f2fb9a8SDavid Ahern 			continue;
13033f2fb9a8SDavid Ahern 
13049f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13059f9354b9SEric Dumazet 		if (!in_dev)
13061da177e4SLinus Torvalds 			continue;
13071da177e4SLinus Torvalds 
13088b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13098b57fd1eSGao Feng 		if (addr)
1310c6d14c84SEric Dumazet 			goto out_unlock;
13111da177e4SLinus Torvalds 	}
1312c6d14c84SEric Dumazet out_unlock:
13131da177e4SLinus Torvalds 	rcu_read_unlock();
13141da177e4SLinus Torvalds 	return addr;
13151da177e4SLinus Torvalds }
13169f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13171da177e4SLinus Torvalds 
131860cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
131960cad5daSAl Viro 			      __be32 local, int scope)
13201da177e4SLinus Torvalds {
13211da177e4SLinus Torvalds 	int same = 0;
1322a144ea4bSAl Viro 	__be32 addr = 0;
13231da177e4SLinus Torvalds 
13241da177e4SLinus Torvalds 	for_ifa(in_dev) {
13251da177e4SLinus Torvalds 		if (!addr &&
13261da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
13271da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
13281da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13291da177e4SLinus Torvalds 			if (same)
13301da177e4SLinus Torvalds 				break;
13311da177e4SLinus Torvalds 		}
13321da177e4SLinus Torvalds 		if (!same) {
13331da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13341da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13351da177e4SLinus Torvalds 			if (same && addr) {
13361da177e4SLinus Torvalds 				if (local || !dst)
13371da177e4SLinus Torvalds 					break;
13381da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
13391da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
13401da177e4SLinus Torvalds 					break;
13411da177e4SLinus Torvalds 				/* No, then can we use new local src? */
13421da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
13431da177e4SLinus Torvalds 					addr = ifa->ifa_local;
13441da177e4SLinus Torvalds 					break;
13451da177e4SLinus Torvalds 				}
13461da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
13471da177e4SLinus Torvalds 				same = 0;
13481da177e4SLinus Torvalds 			}
13491da177e4SLinus Torvalds 		}
13501da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds 	return same ? addr : 0;
13531da177e4SLinus Torvalds }
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds /*
13561da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1357b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1358b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
13591da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
13601da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
13611da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
13621da177e4SLinus Torvalds  */
1363b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
13649bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
13651da177e4SLinus Torvalds {
136660cad5daSAl Viro 	__be32 addr = 0;
13679bd85e32SDenis V. Lunev 	struct net_device *dev;
13681da177e4SLinus Torvalds 
136900db4124SIan Morris 	if (in_dev)
13709bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
13711da177e4SLinus Torvalds 
13721da177e4SLinus Torvalds 	rcu_read_lock();
1373c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13749f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13759f9354b9SEric Dumazet 		if (in_dev) {
13761da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
13771da177e4SLinus Torvalds 			if (addr)
13781da177e4SLinus Torvalds 				break;
13791da177e4SLinus Torvalds 		}
13801da177e4SLinus Torvalds 	}
13811da177e4SLinus Torvalds 	rcu_read_unlock();
13821da177e4SLinus Torvalds 
13831da177e4SLinus Torvalds 	return addr;
13841da177e4SLinus Torvalds }
1385eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
13861da177e4SLinus Torvalds 
13871da177e4SLinus Torvalds /*
13881da177e4SLinus Torvalds  *	Device notifier
13891da177e4SLinus Torvalds  */
13901da177e4SLinus Torvalds 
13911da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
13921da177e4SLinus Torvalds {
1393e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
13941da177e4SLinus Torvalds }
13959f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
13981da177e4SLinus Torvalds {
1399e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14001da177e4SLinus Torvalds }
14019f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14021da177e4SLinus Torvalds 
14033ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14043ad7d246SKrister Johansen {
14053ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14063ad7d246SKrister Johansen }
14073ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14083ad7d246SKrister Johansen 
14093ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14103ad7d246SKrister Johansen {
14113ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14123ad7d246SKrister Johansen 	    nb);
14133ad7d246SKrister Johansen }
14143ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14153ad7d246SKrister Johansen 
14169f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14179f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14181da177e4SLinus Torvalds */
14191da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14201da177e4SLinus Torvalds {
14211da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14221da177e4SLinus Torvalds 	int named = 0;
14231da177e4SLinus Torvalds 
14241da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
14251da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14261da177e4SLinus Torvalds 
14271da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14281da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14291da177e4SLinus Torvalds 		if (named++ == 0)
1430573bf470SThomas Graf 			goto skip;
143144344b2aSMark McLoughlin 		dot = strchr(old, ':');
143251456b29SIan Morris 		if (!dot) {
14331da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14341da177e4SLinus Torvalds 			dot = old;
14351da177e4SLinus Torvalds 		}
14369f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14371da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14389f9354b9SEric Dumazet 		else
14391da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1440573bf470SThomas Graf skip:
1441573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
14421da177e4SLinus Torvalds 	}
14431da177e4SLinus Torvalds }
14441da177e4SLinus Torvalds 
144540384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
144606770843SBreno Leitao {
1447b5476022SEric Dumazet 	return mtu >= IPV4_MIN_MTU;
144806770843SBreno Leitao }
144906770843SBreno Leitao 
1450d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1451d11327adSIan Campbell 					struct in_device *in_dev)
1452d11327adSIan Campbell 
1453d11327adSIan Campbell {
1454b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1455d11327adSIan Campbell 
1456b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1457b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1458d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
14596c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
14606c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1461d11327adSIan Campbell 			 dev->dev_addr, NULL);
1462d11327adSIan Campbell 	}
1463b76d0789SZoltan Kiss }
1464d11327adSIan Campbell 
14651da177e4SLinus Torvalds /* Called only under RTNL semaphore */
14661da177e4SLinus Torvalds 
14671da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
14681da177e4SLinus Torvalds 			 void *ptr)
14691da177e4SLinus Torvalds {
1470351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1471748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
14721da177e4SLinus Torvalds 
14731da177e4SLinus Torvalds 	ASSERT_RTNL();
14741da177e4SLinus Torvalds 
14751da177e4SLinus Torvalds 	if (!in_dev) {
14768030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
14771da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
147820e61da7SWANG Cong 			if (IS_ERR(in_dev))
147920e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
14800cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
148142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
148242f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
14831da177e4SLinus Torvalds 			}
148406770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
148506770843SBreno Leitao 			/* Re-enabling IP */
148606770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
148706770843SBreno Leitao 				in_dev = inetdev_init(dev);
14888030f544SHerbert Xu 		}
14891da177e4SLinus Torvalds 		goto out;
14901da177e4SLinus Torvalds 	}
14911da177e4SLinus Torvalds 
14921da177e4SLinus Torvalds 	switch (event) {
14931da177e4SLinus Torvalds 	case NETDEV_REGISTER:
149491df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1495a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
14961da177e4SLinus Torvalds 		break;
14971da177e4SLinus Torvalds 	case NETDEV_UP:
149806770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
14991da177e4SLinus Torvalds 			break;
15000cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15019f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15029f9354b9SEric Dumazet 
15039f9354b9SEric Dumazet 			if (ifa) {
1504fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15051da177e4SLinus Torvalds 				ifa->ifa_local =
15061da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15071da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15081da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15091da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15101da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15111da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15121da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15135c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15145c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1515dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1516dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15171da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15181da177e4SLinus Torvalds 			}
15191da177e4SLinus Torvalds 		}
15201da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1521eefef1cfSStephen Hemminger 		/* fall through */
1522eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1523d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1524d11327adSIan Campbell 			break;
1525d11327adSIan Campbell 		/* fall through */
1526d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1527a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1528d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15291da177e4SLinus Torvalds 		break;
15301da177e4SLinus Torvalds 	case NETDEV_DOWN:
15311da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15321da177e4SLinus Torvalds 		break;
153393d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
153475c78500SMoni Shoua 		ip_mc_unmap(in_dev);
153575c78500SMoni Shoua 		break;
153693d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
153775c78500SMoni Shoua 		ip_mc_remap(in_dev);
153875c78500SMoni Shoua 		break;
15391da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
154006770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15411da177e4SLinus Torvalds 			break;
154206770843SBreno Leitao 		/* disable IP when MTU is not enough */
1543fcfd6dfaSGustavo A. R. Silva 		/* fall through */
15441da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
15451da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
15461da177e4SLinus Torvalds 		break;
15471da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
15481da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
15491da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
15501da177e4SLinus Torvalds 		 */
15511da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
15521da177e4SLinus Torvalds 
155351602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
155466f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
15551da177e4SLinus Torvalds 		break;
15561da177e4SLinus Torvalds 	}
15571da177e4SLinus Torvalds out:
15581da177e4SLinus Torvalds 	return NOTIFY_DONE;
15591da177e4SLinus Torvalds }
15601da177e4SLinus Torvalds 
15611da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
15621da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
15631da177e4SLinus Torvalds };
15641da177e4SLinus Torvalds 
156540384999SEric Dumazet static size_t inet_nlmsg_size(void)
1566339bf98fSThomas Graf {
1567339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1568339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1569339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1570339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1571ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
157263b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1573af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
157463b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1575339bf98fSThomas Graf }
1576339bf98fSThomas Graf 
15775c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
15785c766d64SJiri Pirko {
15795c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
15805c766d64SJiri Pirko }
15815c766d64SJiri Pirko 
15825c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
15835c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
15845c766d64SJiri Pirko {
15855c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
15865c766d64SJiri Pirko 
15875c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
15885c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
15895c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
15905c766d64SJiri Pirko 	ci.ifa_valid = valid;
15915c766d64SJiri Pirko 
15925c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
15935c766d64SJiri Pirko }
15945c766d64SJiri Pirko 
15951da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1596978a46faSChristian Brauner 			    struct inet_fill_args *args)
15971da177e4SLinus Torvalds {
15981da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
15991da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16005c766d64SJiri Pirko 	u32 preferred, valid;
16011da177e4SLinus Torvalds 
1602978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1603978a46faSChristian Brauner 			args->flags);
160451456b29SIan Morris 	if (!nlh)
160526932566SPatrick McHardy 		return -EMSGSIZE;
160647f68512SThomas Graf 
160747f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16081da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16091da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16105c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16111da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16121da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16131da177e4SLinus Torvalds 
1614978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1615978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1616d3807145SChristian Brauner 		goto nla_put_failure;
1617d3807145SChristian Brauner 
16185c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16195c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16205c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16215c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16225c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16235c766d64SJiri Pirko 
16245c766d64SJiri Pirko 			if (preferred > tval)
16255c766d64SJiri Pirko 				preferred -= tval;
16265c766d64SJiri Pirko 			else
16275c766d64SJiri Pirko 				preferred = 0;
16285c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16295c766d64SJiri Pirko 				if (valid > tval)
16305c766d64SJiri Pirko 					valid -= tval;
16315c766d64SJiri Pirko 				else
16325c766d64SJiri Pirko 					valid = 0;
16335c766d64SJiri Pirko 			}
16345c766d64SJiri Pirko 		}
16355c766d64SJiri Pirko 	} else {
16365c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16375c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16385c766d64SJiri Pirko 	}
1639f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1640930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1641f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1642930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1643f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1644930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1645f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
16465c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1647ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1648af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1649af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
16505c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
16515c766d64SJiri Pirko 			  preferred, valid))
1652f3756b79SDavid S. Miller 		goto nla_put_failure;
165347f68512SThomas Graf 
1654053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1655053c095aSJohannes Berg 	return 0;
165647f68512SThomas Graf 
165747f68512SThomas Graf nla_put_failure:
165826932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
165926932566SPatrick McHardy 	return -EMSGSIZE;
16601da177e4SLinus Torvalds }
16611da177e4SLinus Torvalds 
16621da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
16631da177e4SLinus Torvalds {
1664978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1665978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1666978a46faSChristian Brauner 		.seq = cb->nlh->nlmsg_seq,
1667978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1668978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1669978a46faSChristian Brauner 		.netnsid = -1,
1670978a46faSChristian Brauner 	};
16713b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1672d3807145SChristian Brauner 	struct nlattr *tb[IFA_MAX+1];
1673d3807145SChristian Brauner 	struct net *tgt_net = net;
1674eec4df98SEric Dumazet 	int h, s_h;
1675eec4df98SEric Dumazet 	int idx, s_idx;
1676eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
16771da177e4SLinus Torvalds 	struct net_device *dev;
16781da177e4SLinus Torvalds 	struct in_device *in_dev;
16791da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1680eec4df98SEric Dumazet 	struct hlist_head *head;
16811da177e4SLinus Torvalds 
1682eec4df98SEric Dumazet 	s_h = cb->args[0];
1683eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1684eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1685eec4df98SEric Dumazet 
1686d3807145SChristian Brauner 	if (nlmsg_parse(cb->nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX,
1687d3807145SChristian Brauner 			ifa_ipv4_policy, NULL) >= 0) {
1688d3807145SChristian Brauner 		if (tb[IFA_TARGET_NETNSID]) {
1689978a46faSChristian Brauner 			fillargs.netnsid = nla_get_s32(tb[IFA_TARGET_NETNSID]);
1690d3807145SChristian Brauner 
1691978a46faSChristian Brauner 			tgt_net = rtnl_get_net_ns_capable(skb->sk,
1692978a46faSChristian Brauner 							  fillargs.netnsid);
1693d3807145SChristian Brauner 			if (IS_ERR(tgt_net))
1694d3807145SChristian Brauner 				return PTR_ERR(tgt_net);
1695d3807145SChristian Brauner 		}
1696d3807145SChristian Brauner 	}
1697d3807145SChristian Brauner 
1698eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
16997562f876SPavel Emelianov 		idx = 0;
1700d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1701eec4df98SEric Dumazet 		rcu_read_lock();
1702d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1703d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1704b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
17051da177e4SLinus Torvalds 			if (idx < s_idx)
17067562f876SPavel Emelianov 				goto cont;
17074b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
17081da177e4SLinus Torvalds 				s_ip_idx = 0;
1709eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
17109f9354b9SEric Dumazet 			if (!in_dev)
17117562f876SPavel Emelianov 				goto cont;
17121da177e4SLinus Torvalds 
17131da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
17141da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
17151da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1716596e4150SStephen Hemminger 					continue;
1717978a46faSChristian Brauner 				if (inet_fill_ifaddr(skb, ifa, &fillargs) < 0) {
1718eec4df98SEric Dumazet 					rcu_read_unlock();
17191da177e4SLinus Torvalds 					goto done;
17201da177e4SLinus Torvalds 				}
17210465277fSNicolas Dichtel 				nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1722eec4df98SEric Dumazet 			}
17237562f876SPavel Emelianov cont:
17247562f876SPavel Emelianov 			idx++;
17251da177e4SLinus Torvalds 		}
1726eec4df98SEric Dumazet 		rcu_read_unlock();
1727eec4df98SEric Dumazet 	}
17281da177e4SLinus Torvalds 
17291da177e4SLinus Torvalds done:
1730eec4df98SEric Dumazet 	cb->args[0] = h;
1731eec4df98SEric Dumazet 	cb->args[1] = idx;
1732eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
1733978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1734d3807145SChristian Brauner 		put_net(tgt_net);
17351da177e4SLinus Torvalds 
17361da177e4SLinus Torvalds 	return skb->len;
17371da177e4SLinus Torvalds }
17381da177e4SLinus Torvalds 
1739d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
174015e47304SEric W. Biederman 		      u32 portid)
17411da177e4SLinus Torvalds {
1742978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1743978a46faSChristian Brauner 		.portid = portid,
1744978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1745978a46faSChristian Brauner 		.event = event,
1746978a46faSChristian Brauner 		.flags = 0,
1747978a46faSChristian Brauner 		.netnsid = -1,
1748978a46faSChristian Brauner 	};
174947f68512SThomas Graf 	struct sk_buff *skb;
1750d6062cbbSThomas Graf 	int err = -ENOBUFS;
17514b8aa9abSDenis V. Lunev 	struct net *net;
17521da177e4SLinus Torvalds 
1753c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1754339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
175551456b29SIan Morris 	if (!skb)
1756d6062cbbSThomas Graf 		goto errout;
1757d6062cbbSThomas Graf 
1758978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
175926932566SPatrick McHardy 	if (err < 0) {
176026932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
176126932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
176226932566SPatrick McHardy 		kfree_skb(skb);
176326932566SPatrick McHardy 		goto errout;
176426932566SPatrick McHardy 	}
176515e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
17661ce85fe4SPablo Neira Ayuso 	return;
1767d6062cbbSThomas Graf errout:
1768d6062cbbSThomas Graf 	if (err < 0)
17694b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
17701da177e4SLinus Torvalds }
17711da177e4SLinus Torvalds 
1772b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1773b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
17749f0f7272SThomas Graf {
17751fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
17769f0f7272SThomas Graf 
17779f0f7272SThomas Graf 	if (!in_dev)
17789f0f7272SThomas Graf 		return 0;
17799f0f7272SThomas Graf 
17809f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
17819f0f7272SThomas Graf }
17829f0f7272SThomas Graf 
1783d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1784d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
17859f0f7272SThomas Graf {
17861fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
17879f0f7272SThomas Graf 	struct nlattr *nla;
17889f0f7272SThomas Graf 	int i;
17899f0f7272SThomas Graf 
17909f0f7272SThomas Graf 	if (!in_dev)
17919f0f7272SThomas Graf 		return -ENODATA;
17929f0f7272SThomas Graf 
17939f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
179451456b29SIan Morris 	if (!nla)
17959f0f7272SThomas Graf 		return -EMSGSIZE;
17969f0f7272SThomas Graf 
17979f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
17989f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
17999f0f7272SThomas Graf 
18009f0f7272SThomas Graf 	return 0;
18019f0f7272SThomas Graf }
18029f0f7272SThomas Graf 
18039f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
18049f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
18059f0f7272SThomas Graf };
18069f0f7272SThomas Graf 
1807cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1808cf7afbfeSThomas Graf 				 const struct nlattr *nla)
18099f0f7272SThomas Graf {
18109f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
18119f0f7272SThomas Graf 	int err, rem;
18129f0f7272SThomas Graf 
18135fa85a09SFlorian Westphal 	if (dev && !__in_dev_get_rcu(dev))
1814cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
18159f0f7272SThomas Graf 
1816fceb6435SJohannes Berg 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy, NULL);
18179f0f7272SThomas Graf 	if (err < 0)
18189f0f7272SThomas Graf 		return err;
18199f0f7272SThomas Graf 
18209f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
18219f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
18229f0f7272SThomas Graf 			int cfgid = nla_type(a);
18239f0f7272SThomas Graf 
18249f0f7272SThomas Graf 			if (nla_len(a) < 4)
18259f0f7272SThomas Graf 				return -EINVAL;
18269f0f7272SThomas Graf 
18279f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
18289f0f7272SThomas Graf 				return -EINVAL;
18299f0f7272SThomas Graf 		}
18309f0f7272SThomas Graf 	}
18319f0f7272SThomas Graf 
1832cf7afbfeSThomas Graf 	return 0;
1833cf7afbfeSThomas Graf }
1834cf7afbfeSThomas Graf 
1835cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1836cf7afbfeSThomas Graf {
18375fa85a09SFlorian Westphal 	struct in_device *in_dev = __in_dev_get_rcu(dev);
1838cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1839cf7afbfeSThomas Graf 	int rem;
1840cf7afbfeSThomas Graf 
1841cf7afbfeSThomas Graf 	if (!in_dev)
1842cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1843cf7afbfeSThomas Graf 
1844fceb6435SJohannes Berg 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
1845cf7afbfeSThomas Graf 		BUG();
1846cf7afbfeSThomas Graf 
18479f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
18489f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
18499f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
18509f0f7272SThomas Graf 	}
18519f0f7272SThomas Graf 
18529f0f7272SThomas Graf 	return 0;
18539f0f7272SThomas Graf }
18549f0f7272SThomas Graf 
1855edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1856edc9e748SNicolas Dichtel {
1857edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1858edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1859136ba622SZhang Shengju 	bool all = false;
1860edc9e748SNicolas Dichtel 
1861136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1862136ba622SZhang Shengju 		all = true;
1863136ba622SZhang Shengju 
1864136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
1865edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1866136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
1867cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1868136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
1869d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
18705cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
18715cbf777cSXin Long 		size += nla_total_size(4);
1872136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
1873f085ff1cSstephen hemminger 		size += nla_total_size(4);
1874136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
1875974d7af5SAndy Gospodarek 		size += nla_total_size(4);
1876edc9e748SNicolas Dichtel 
1877edc9e748SNicolas Dichtel 	return size;
1878edc9e748SNicolas Dichtel }
1879edc9e748SNicolas Dichtel 
1880edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1881edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1882edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1883edc9e748SNicolas Dichtel 				     int type)
1884edc9e748SNicolas Dichtel {
1885edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1886edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1887136ba622SZhang Shengju 	bool all = false;
1888edc9e748SNicolas Dichtel 
1889edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1890edc9e748SNicolas Dichtel 			flags);
189151456b29SIan Morris 	if (!nlh)
1892edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1893edc9e748SNicolas Dichtel 
1894136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1895136ba622SZhang Shengju 		all = true;
1896136ba622SZhang Shengju 
1897edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1898edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1899edc9e748SNicolas Dichtel 
1900edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1901edc9e748SNicolas Dichtel 		goto nla_put_failure;
1902edc9e748SNicolas Dichtel 
1903b5c9641dSDavid Ahern 	if (!devconf)
1904b5c9641dSDavid Ahern 		goto out;
1905b5c9641dSDavid Ahern 
1906136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
1907edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1908edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1909edc9e748SNicolas Dichtel 		goto nla_put_failure;
1910136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
1911cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1912cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1913cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1914136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
1915d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1916d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1917d67b8c61SNicolas Dichtel 		goto nla_put_failure;
19185cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
19195cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
19205cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
19215cbf777cSXin Long 		goto nla_put_failure;
1922136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
192309aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
1924f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
1925f085ff1cSstephen hemminger 		goto nla_put_failure;
1926136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
1927974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
1928974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
1929974d7af5SAndy Gospodarek 		goto nla_put_failure;
1930edc9e748SNicolas Dichtel 
1931b5c9641dSDavid Ahern out:
1932053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1933053c095aSJohannes Berg 	return 0;
1934edc9e748SNicolas Dichtel 
1935edc9e748SNicolas Dichtel nla_put_failure:
1936edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1937edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1938edc9e748SNicolas Dichtel }
1939edc9e748SNicolas Dichtel 
19403b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
19413b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
1942edc9e748SNicolas Dichtel {
1943edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1944edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1945edc9e748SNicolas Dichtel 
1946fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
194751456b29SIan Morris 	if (!skb)
1948edc9e748SNicolas Dichtel 		goto errout;
1949edc9e748SNicolas Dichtel 
1950edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
19513b022865SDavid Ahern 					event, 0, type);
1952edc9e748SNicolas Dichtel 	if (err < 0) {
1953edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1954edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1955edc9e748SNicolas Dichtel 		kfree_skb(skb);
1956edc9e748SNicolas Dichtel 		goto errout;
1957edc9e748SNicolas Dichtel 	}
1958fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
1959edc9e748SNicolas Dichtel 	return;
1960edc9e748SNicolas Dichtel errout:
1961edc9e748SNicolas Dichtel 	if (err < 0)
1962edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1963edc9e748SNicolas Dichtel }
1964edc9e748SNicolas Dichtel 
19659e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
19669e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
19679e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1968cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
196909aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
1970974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
19719e551110SNicolas Dichtel };
19729e551110SNicolas Dichtel 
19739e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
1974c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
1975c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
19769e551110SNicolas Dichtel {
19779e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
19789e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
19799e551110SNicolas Dichtel 	struct netconfmsg *ncm;
19809e551110SNicolas Dichtel 	struct sk_buff *skb;
19819e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
19829e551110SNicolas Dichtel 	struct in_device *in_dev;
19839e551110SNicolas Dichtel 	struct net_device *dev;
19849e551110SNicolas Dichtel 	int ifindex;
19859e551110SNicolas Dichtel 	int err;
19869e551110SNicolas Dichtel 
19879e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
1988c21ef3e3SDavid Ahern 			  devconf_ipv4_policy, extack);
19899e551110SNicolas Dichtel 	if (err < 0)
19909e551110SNicolas Dichtel 		goto errout;
19919e551110SNicolas Dichtel 
1992a97eb33fSAnton Protopopov 	err = -EINVAL;
19939e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
19949e551110SNicolas Dichtel 		goto errout;
19959e551110SNicolas Dichtel 
19969e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
19979e551110SNicolas Dichtel 	switch (ifindex) {
19989e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
19999e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
20009e551110SNicolas Dichtel 		break;
20019e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
20029e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
20039e551110SNicolas Dichtel 		break;
20049e551110SNicolas Dichtel 	default:
20059e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
200651456b29SIan Morris 		if (!dev)
20079e551110SNicolas Dichtel 			goto errout;
20089e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
200951456b29SIan Morris 		if (!in_dev)
20109e551110SNicolas Dichtel 			goto errout;
20119e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
20129e551110SNicolas Dichtel 		break;
20139e551110SNicolas Dichtel 	}
20149e551110SNicolas Dichtel 
20159e551110SNicolas Dichtel 	err = -ENOBUFS;
2016fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
201751456b29SIan Morris 	if (!skb)
20189e551110SNicolas Dichtel 		goto errout;
20199e551110SNicolas Dichtel 
20209e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
20219e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
20229e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2023136ba622SZhang Shengju 					NETCONFA_ALL);
20249e551110SNicolas Dichtel 	if (err < 0) {
20259e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
20269e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
20279e551110SNicolas Dichtel 		kfree_skb(skb);
20289e551110SNicolas Dichtel 		goto errout;
20299e551110SNicolas Dichtel 	}
20309e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
20319e551110SNicolas Dichtel errout:
20329e551110SNicolas Dichtel 	return err;
20339e551110SNicolas Dichtel }
20349e551110SNicolas Dichtel 
20357a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
20367a674200SNicolas Dichtel 				     struct netlink_callback *cb)
20377a674200SNicolas Dichtel {
20387a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
20397a674200SNicolas Dichtel 	int h, s_h;
20407a674200SNicolas Dichtel 	int idx, s_idx;
20417a674200SNicolas Dichtel 	struct net_device *dev;
20427a674200SNicolas Dichtel 	struct in_device *in_dev;
20437a674200SNicolas Dichtel 	struct hlist_head *head;
20447a674200SNicolas Dichtel 
20457a674200SNicolas Dichtel 	s_h = cb->args[0];
20467a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
20477a674200SNicolas Dichtel 
20487a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
20497a674200SNicolas Dichtel 		idx = 0;
20507a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
20517a674200SNicolas Dichtel 		rcu_read_lock();
20520465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
20530465277fSNicolas Dichtel 			  net->dev_base_seq;
20547a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
20557a674200SNicolas Dichtel 			if (idx < s_idx)
20567a674200SNicolas Dichtel 				goto cont;
20577a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
20587a674200SNicolas Dichtel 			if (!in_dev)
20597a674200SNicolas Dichtel 				goto cont;
20607a674200SNicolas Dichtel 
20617a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
20627a674200SNicolas Dichtel 						      &in_dev->cnf,
20637a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
20647a674200SNicolas Dichtel 						      cb->nlh->nlmsg_seq,
20657a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
20667a674200SNicolas Dichtel 						      NLM_F_MULTI,
2067136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
20687a674200SNicolas Dichtel 				rcu_read_unlock();
20697a674200SNicolas Dichtel 				goto done;
20707a674200SNicolas Dichtel 			}
20710465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
20727a674200SNicolas Dichtel cont:
20737a674200SNicolas Dichtel 			idx++;
20747a674200SNicolas Dichtel 		}
20757a674200SNicolas Dichtel 		rcu_read_unlock();
20767a674200SNicolas Dichtel 	}
20777a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
20787a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
20797a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
20807a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
20817a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
20827a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2083136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
20847a674200SNicolas Dichtel 			goto done;
20857a674200SNicolas Dichtel 		else
20867a674200SNicolas Dichtel 			h++;
20877a674200SNicolas Dichtel 	}
20887a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
20897a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
20907a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
20917a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
20927a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
20937a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2094136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
20957a674200SNicolas Dichtel 			goto done;
20967a674200SNicolas Dichtel 		else
20977a674200SNicolas Dichtel 			h++;
20987a674200SNicolas Dichtel 	}
20997a674200SNicolas Dichtel done:
21007a674200SNicolas Dichtel 	cb->args[0] = h;
21017a674200SNicolas Dichtel 	cb->args[1] = idx;
21027a674200SNicolas Dichtel 
21037a674200SNicolas Dichtel 	return skb->len;
21047a674200SNicolas Dichtel }
21057a674200SNicolas Dichtel 
21061da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
21071da177e4SLinus Torvalds 
2108c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
210931be3085SHerbert Xu {
211031be3085SHerbert Xu 	struct net_device *dev;
211131be3085SHerbert Xu 
211231be3085SHerbert Xu 	rcu_read_lock();
2113c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2114c6d14c84SEric Dumazet 		struct in_device *in_dev;
2115c6d14c84SEric Dumazet 
211631be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
211731be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
21189355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2119c6d14c84SEric Dumazet 	}
212031be3085SHerbert Xu 	rcu_read_unlock();
212131be3085SHerbert Xu }
212231be3085SHerbert Xu 
2123c6d14c84SEric Dumazet /* called with RTNL locked */
2124c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
212568dd299bSPavel Emelyanov {
212668dd299bSPavel Emelyanov 	struct net_device *dev;
2127586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
212868dd299bSPavel Emelyanov 
2129586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
21309355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
21313b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21323b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2133edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2134edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
21353b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21363b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2137edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2138edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
213968dd299bSPavel Emelyanov 
2140c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
214168dd299bSPavel Emelyanov 		struct in_device *in_dev;
2142fa17806cSEric Dumazet 
21430187bdfbSBen Hutchings 		if (on)
21440187bdfbSBen Hutchings 			dev_disable_lro(dev);
2145fa17806cSEric Dumazet 
2146fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2147edc9e748SNicolas Dichtel 		if (in_dev) {
214868dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
21493b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21503b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2151edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2152edc9e748SNicolas Dichtel 		}
215368dd299bSPavel Emelyanov 	}
215468dd299bSPavel Emelyanov }
215568dd299bSPavel Emelyanov 
2156f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2157f085ff1cSstephen hemminger {
2158f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2159f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2160f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2161f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2162f085ff1cSstephen hemminger 	else {
2163f085ff1cSstephen hemminger 		struct in_device *idev
2164f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2165f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2166f085ff1cSstephen hemminger 	}
2167f085ff1cSstephen hemminger }
2168f085ff1cSstephen hemminger 
2169fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
21708d65af78SAlexey Dobriyan 			     void __user *buffer,
217131be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
217231be3085SHerbert Xu {
2173d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
21748d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2175d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
217631be3085SHerbert Xu 
217731be3085SHerbert Xu 	if (write) {
217831be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2179c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
218031be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2181f085ff1cSstephen hemminger 		int ifindex;
218231be3085SHerbert Xu 
218331be3085SHerbert Xu 		set_bit(i, cnf->state);
218431be3085SHerbert Xu 
21859355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2186c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2187d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2188d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2189d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
21904ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2191f085ff1cSstephen hemminger 
21925cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
21935cbf777cSXin Long 		    new_value != old_value)
21945cbf777cSXin Long 			rt_cache_flush(net);
21955cbf777cSXin Long 
2196cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2197cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2198f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
21993b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22003b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2201cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2202cc535dfbSNicolas Dichtel 		}
2203f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2204f085ff1cSstephen hemminger 		    new_value != old_value) {
2205f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
22063b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22073b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2208f085ff1cSstephen hemminger 						    ifindex, cnf);
2209f085ff1cSstephen hemminger 		}
2210974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2211974d7af5SAndy Gospodarek 		    new_value != old_value) {
2212974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
22133b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22143b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2215974d7af5SAndy Gospodarek 						    ifindex, cnf);
2216974d7af5SAndy Gospodarek 		}
221731be3085SHerbert Xu 	}
221831be3085SHerbert Xu 
221931be3085SHerbert Xu 	return ret;
222031be3085SHerbert Xu }
222131be3085SHerbert Xu 
2222fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
22238d65af78SAlexey Dobriyan 				  void __user *buffer,
22241da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
22251da177e4SLinus Torvalds {
22261da177e4SLinus Torvalds 	int *valp = ctl->data;
22271da177e4SLinus Torvalds 	int val = *valp;
222888af182eSEric W. Biederman 	loff_t pos = *ppos;
22298d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
22301da177e4SLinus Torvalds 
22311da177e4SLinus Torvalds 	if (write && *valp != val) {
2232c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2233c0ce9fb3SPavel Emelyanov 
22340187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
223588af182eSEric W. Biederman 			if (!rtnl_trylock()) {
223688af182eSEric W. Biederman 				/* Restore the original values before restarting */
223788af182eSEric W. Biederman 				*valp = val;
223888af182eSEric W. Biederman 				*ppos = pos;
22399b8adb5eSEric W. Biederman 				return restart_syscall();
224088af182eSEric W. Biederman 			}
22410187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2242c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2243edc9e748SNicolas Dichtel 			} else {
22440187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
22450187bdfbSBen Hutchings 				struct in_device *idev =
22460187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2247edc9e748SNicolas Dichtel 				if (*valp)
22480187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
22493b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2250edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2251edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2252edc9e748SNicolas Dichtel 							    cnf);
22530187bdfbSBen Hutchings 			}
22540187bdfbSBen Hutchings 			rtnl_unlock();
22554ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2256edc9e748SNicolas Dichtel 		} else
22573b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22583b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2259edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2260edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
22610187bdfbSBen Hutchings 	}
22621da177e4SLinus Torvalds 
22631da177e4SLinus Torvalds 	return ret;
22641da177e4SLinus Torvalds }
22651da177e4SLinus Torvalds 
2266fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
22678d65af78SAlexey Dobriyan 				void __user *buffer,
22681da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
22691da177e4SLinus Torvalds {
22701da177e4SLinus Torvalds 	int *valp = ctl->data;
22711da177e4SLinus Torvalds 	int val = *valp;
22728d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
227376e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
22741da177e4SLinus Torvalds 
22751da177e4SLinus Torvalds 	if (write && *valp != val)
22764ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
22771da177e4SLinus Torvalds 
22781da177e4SLinus Torvalds 	return ret;
22791da177e4SLinus Torvalds }
22801da177e4SLinus Torvalds 
2281f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
228242f811b8SHerbert Xu 	{ \
228342f811b8SHerbert Xu 		.procname	= name, \
228442f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
228502291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
228642f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
228742f811b8SHerbert Xu 		.mode		= mval, \
228842f811b8SHerbert Xu 		.proc_handler	= proc, \
228931be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
229042f811b8SHerbert Xu 	}
229142f811b8SHerbert Xu 
229242f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2293f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
229442f811b8SHerbert Xu 
229542f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2296f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
229742f811b8SHerbert Xu 
2298f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2299f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
230042f811b8SHerbert Xu 
230142f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2302f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
230342f811b8SHerbert Xu 
23041da177e4SLinus Torvalds static struct devinet_sysctl_table {
23051da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
230602291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
23071da177e4SLinus Torvalds } devinet_sysctl = {
23081da177e4SLinus Torvalds 	.devinet_vars = {
230942f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2310f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
231142f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
23125cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
231342f811b8SHerbert Xu 
231442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
231542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
231642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
231742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
231842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
231942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
232042f811b8SHerbert Xu 					"accept_source_route"),
23218153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
232228f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
232342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
232442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
232542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
232642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
232742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
232842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
232942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
233042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
233142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2332eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
233365324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
23345c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
23355c6fe01cSWilliam Manley 					"force_igmp_version"),
23362690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
23372690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
23382690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
23392690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
23400eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
23410eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
234297daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
234397daf331SJohannes Berg 					"drop_gratuitous_arp"),
234442f811b8SHerbert Xu 
234542f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
234642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
234742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
234842f811b8SHerbert Xu 					      "promote_secondaries"),
2349d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2350d0daebc3SThomas Graf 					      "route_localnet"),
235112b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
235212b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
23531da177e4SLinus Torvalds 	},
23541da177e4SLinus Torvalds };
23551da177e4SLinus Torvalds 
2356ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
235729c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
23581da177e4SLinus Torvalds {
23591da177e4SLinus Torvalds 	int i;
23609fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
23618607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2362bfada697SPavel Emelyanov 
23639fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
23641da177e4SLinus Torvalds 	if (!t)
23659fa89642SPavel Emelyanov 		goto out;
23669fa89642SPavel Emelyanov 
23671da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
23681da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
236931be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2370c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
23711da177e4SLinus Torvalds 	}
23721da177e4SLinus Torvalds 
23738607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
23741da177e4SLinus Torvalds 
23758607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
23761da177e4SLinus Torvalds 	if (!t->sysctl_header)
23778607ddb8SEric W. Biederman 		goto free;
23781da177e4SLinus Torvalds 
23791da177e4SLinus Torvalds 	p->sysctl = t;
238029c994e3SNicolas Dichtel 
23813b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
23823b022865SDavid Ahern 				    ifindex, p);
2383ea40b324SPavel Emelyanov 	return 0;
23841da177e4SLinus Torvalds 
23851da177e4SLinus Torvalds free:
23861da177e4SLinus Torvalds 	kfree(t);
23879fa89642SPavel Emelyanov out:
2388ea40b324SPavel Emelyanov 	return -ENOBUFS;
23891da177e4SLinus Torvalds }
23901da177e4SLinus Torvalds 
2391b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2392b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
239366f27a52SPavel Emelyanov {
239451602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
239566f27a52SPavel Emelyanov 
2396b5c9641dSDavid Ahern 	if (t) {
239751602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2398ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
23991da177e4SLinus Torvalds 		kfree(t);
24001da177e4SLinus Torvalds 	}
240151602b2aSPavel Emelyanov 
2402b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2403b5c9641dSDavid Ahern }
2404b5c9641dSDavid Ahern 
240520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
240651602b2aSPavel Emelyanov {
240720e61da7SWANG Cong 	int err;
240820e61da7SWANG Cong 
240920e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
241020e61da7SWANG Cong 		return -EINVAL;
241120e61da7SWANG Cong 
241220e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
241320e61da7SWANG Cong 	if (err)
241420e61da7SWANG Cong 		return err;
241520e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
241629c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
241720e61da7SWANG Cong 	if (err)
241820e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
241920e61da7SWANG Cong 	return err;
242051602b2aSPavel Emelyanov }
242151602b2aSPavel Emelyanov 
242251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
242351602b2aSPavel Emelyanov {
2424b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2425b5c9641dSDavid Ahern 
2426b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
242751602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
24281da177e4SLinus Torvalds }
24291da177e4SLinus Torvalds 
243068dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
243168dd299bSPavel Emelyanov 	{
243268dd299bSPavel Emelyanov 		.procname	= "ip_forward",
243368dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
243402291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
243568dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
243668dd299bSPavel Emelyanov 		.mode		= 0644,
243768dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
243868dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2439c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
244068dd299bSPavel Emelyanov 	},
244168dd299bSPavel Emelyanov 	{ },
244268dd299bSPavel Emelyanov };
24432a75de0cSEric Dumazet #endif
244468dd299bSPavel Emelyanov 
2445752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2446752d14dcSPavel Emelyanov {
2447752d14dcSPavel Emelyanov 	int err;
2448752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
24492a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
24502a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2451752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
24522a75de0cSEric Dumazet #endif
2453752d14dcSPavel Emelyanov 
2454752d14dcSPavel Emelyanov 	err = -ENOMEM;
2455752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2456752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2457752d14dcSPavel Emelyanov 
245809ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2459752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
246051456b29SIan Morris 		if (!all)
2461752d14dcSPavel Emelyanov 			goto err_alloc_all;
2462752d14dcSPavel Emelyanov 
2463752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
246451456b29SIan Morris 		if (!dflt)
2465752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2466752d14dcSPavel Emelyanov 
24672a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2468752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
246951456b29SIan Morris 		if (!tbl)
2470752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2471752d14dcSPavel Emelyanov 
247202291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2473752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2474752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
24752a75de0cSEric Dumazet #endif
2476752d14dcSPavel Emelyanov 	}
2477752d14dcSPavel Emelyanov 
2478752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
247929c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2480752d14dcSPavel Emelyanov 	if (err < 0)
2481752d14dcSPavel Emelyanov 		goto err_reg_all;
2482752d14dcSPavel Emelyanov 
248329c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
248429c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2485752d14dcSPavel Emelyanov 	if (err < 0)
2486752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2487752d14dcSPavel Emelyanov 
2488752d14dcSPavel Emelyanov 	err = -ENOMEM;
24898607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
249051456b29SIan Morris 	if (!forw_hdr)
2491752d14dcSPavel Emelyanov 		goto err_reg_ctl;
24922a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2493752d14dcSPavel Emelyanov #endif
2494752d14dcSPavel Emelyanov 
2495752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2496752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2497752d14dcSPavel Emelyanov 	return 0;
2498752d14dcSPavel Emelyanov 
2499752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2500752d14dcSPavel Emelyanov err_reg_ctl:
2501b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2502752d14dcSPavel Emelyanov err_reg_dflt:
2503b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2504752d14dcSPavel Emelyanov err_reg_all:
2505752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2506752d14dcSPavel Emelyanov 		kfree(tbl);
2507752d14dcSPavel Emelyanov err_alloc_ctl:
25082a75de0cSEric Dumazet #endif
2509752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2510752d14dcSPavel Emelyanov 		kfree(dflt);
2511752d14dcSPavel Emelyanov err_alloc_dflt:
2512752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2513752d14dcSPavel Emelyanov 		kfree(all);
2514752d14dcSPavel Emelyanov err_alloc_all:
2515752d14dcSPavel Emelyanov 	return err;
2516752d14dcSPavel Emelyanov }
2517752d14dcSPavel Emelyanov 
2518752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2519752d14dcSPavel Emelyanov {
25202a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2521752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2522752d14dcSPavel Emelyanov 
2523752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2524752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2525b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2526b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2527b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2528b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2529752d14dcSPavel Emelyanov 	kfree(tbl);
25302a75de0cSEric Dumazet #endif
2531752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2532752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2533752d14dcSPavel Emelyanov }
2534752d14dcSPavel Emelyanov 
2535752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2536752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2537752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2538752d14dcSPavel Emelyanov };
2539752d14dcSPavel Emelyanov 
2540207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
25419f0f7272SThomas Graf 	.family		  = AF_INET,
25429f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
25439f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2544cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2545cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
25469f0f7272SThomas Graf };
25479f0f7272SThomas Graf 
25481da177e4SLinus Torvalds void __init devinet_init(void)
25491da177e4SLinus Torvalds {
2550fd23c3b3SDavid S. Miller 	int i;
2551fd23c3b3SDavid S. Miller 
2552fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2553fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2554fd23c3b3SDavid S. Miller 
2555752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2556752d14dcSPavel Emelyanov 
25571da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
25581da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
255963f3444fSThomas Graf 
2560906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
25615c766d64SJiri Pirko 
25629f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
25639f0f7272SThomas Graf 
2564b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2565b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2566b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
25679e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2568b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
25691da177e4SLinus Torvalds }
2570