xref: /openbmc/linux/net/ipv4/devinet.c (revision addd383f)
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,
785dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
786dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
7871da177e4SLinus Torvalds {
7885c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7895c753978SThomas Graf 	struct in_ifaddr *ifa;
7905c753978SThomas Graf 	struct ifaddrmsg *ifm;
7911da177e4SLinus Torvalds 	struct net_device *dev;
7921da177e4SLinus Torvalds 	struct in_device *in_dev;
7937b218574SDenis V. Lunev 	int err;
7941da177e4SLinus Torvalds 
795fceb6435SJohannes Berg 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy,
796dac9c979SDavid Ahern 			  extack);
7975c753978SThomas Graf 	if (err < 0)
7985c753978SThomas Graf 		goto errout;
7991da177e4SLinus Torvalds 
8005c753978SThomas Graf 	ifm = nlmsg_data(nlh);
801c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
80251456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8035c753978SThomas Graf 		goto errout;
8041da177e4SLinus Torvalds 
8054b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8065c753978SThomas Graf 	err = -ENODEV;
80751456b29SIan Morris 	if (!dev)
8085c753978SThomas Graf 		goto errout;
8091da177e4SLinus Torvalds 
8105c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8115c753978SThomas Graf 	err = -ENOBUFS;
81251456b29SIan Morris 	if (!in_dev)
8135c753978SThomas Graf 		goto errout;
81471e27da9SHerbert Xu 
8155c753978SThomas Graf 	ifa = inet_alloc_ifa();
81651456b29SIan Morris 	if (!ifa)
8175c753978SThomas Graf 		/*
8185c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8195c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8205c753978SThomas Graf 		 */
8215c753978SThomas Graf 		goto errout;
8225c753978SThomas Graf 
823a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8241d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8255c753978SThomas Graf 	in_dev_hold(in_dev);
8265c753978SThomas Graf 
82751456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8285c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8295c753978SThomas Graf 
830fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8311da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8321da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
833ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
834ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8351da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8361da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8375c753978SThomas Graf 
83867b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
83967b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8405c753978SThomas Graf 
8415c753978SThomas Graf 	if (tb[IFA_BROADCAST])
84267b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8435c753978SThomas Graf 
8445c753978SThomas Graf 	if (tb[IFA_LABEL])
8455c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8461da177e4SLinus Torvalds 	else
8471da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8481da177e4SLinus Torvalds 
849af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
850af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
851af4d768aSDavid Ahern 
8525c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8535c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8545c766d64SJiri Pirko 
8555c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8565c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8575c766d64SJiri Pirko 			err = -EINVAL;
858446266b0SDaniel Borkmann 			goto errout_free;
8595c766d64SJiri Pirko 		}
8605c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8615c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8625c766d64SJiri Pirko 	}
8635c766d64SJiri Pirko 
8645c753978SThomas Graf 	return ifa;
8655c753978SThomas Graf 
866446266b0SDaniel Borkmann errout_free:
867446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8685c753978SThomas Graf errout:
8695c753978SThomas Graf 	return ERR_PTR(err);
8705c753978SThomas Graf }
8715c753978SThomas Graf 
8725c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
8735c766d64SJiri Pirko {
8745c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8755c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8765c766d64SJiri Pirko 
8775c766d64SJiri Pirko 	if (!ifa->ifa_local)
8785c766d64SJiri Pirko 		return NULL;
8795c766d64SJiri Pirko 
8805c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8815c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8825c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8835c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8845c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8855c766d64SJiri Pirko 			return ifa1;
8865c766d64SJiri Pirko 	}
8875c766d64SJiri Pirko 	return NULL;
8885c766d64SJiri Pirko }
8895c766d64SJiri Pirko 
890c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
891c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
8925c753978SThomas Graf {
8933b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8945c753978SThomas Graf 	struct in_ifaddr *ifa;
8955c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8965c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8975c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8985c753978SThomas Graf 
8995c753978SThomas Graf 	ASSERT_RTNL();
9005c753978SThomas Graf 
901dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9025c753978SThomas Graf 	if (IS_ERR(ifa))
9035c753978SThomas Graf 		return PTR_ERR(ifa);
9045c753978SThomas Graf 
9055c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9065c766d64SJiri Pirko 	if (!ifa_existing) {
9075c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
908614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9095c766d64SJiri Pirko 		 */
9105c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
91193a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
91293a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
91393a714d6SMadhu Challa 					       true, ifa);
91493a714d6SMadhu Challa 
91593a714d6SMadhu Challa 			if (ret < 0) {
91693a714d6SMadhu Challa 				inet_free_ifa(ifa);
91793a714d6SMadhu Challa 				return ret;
91893a714d6SMadhu Challa 			}
91993a714d6SMadhu Challa 		}
920de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
921de95e047SDavid Ahern 					 extack);
9225c766d64SJiri Pirko 	} else {
923af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
924af4d768aSDavid Ahern 
9255c766d64SJiri Pirko 		inet_free_ifa(ifa);
9265c766d64SJiri Pirko 
9275c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9285c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9295c766d64SJiri Pirko 			return -EEXIST;
93034e2ed34SJiri Pirko 		ifa = ifa_existing;
931af4d768aSDavid Ahern 
932af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
933af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
934af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
935af4d768aSDavid Ahern 		}
936af4d768aSDavid Ahern 
93734e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
93805a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
939906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
940906e073fSviresh kumar 				&check_lifetime_work, 0);
94134e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9425c766d64SJiri Pirko 	}
9435c766d64SJiri Pirko 	return 0;
9441da177e4SLinus Torvalds }
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds /*
9471da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9481da177e4SLinus Torvalds  */
9491da177e4SLinus Torvalds 
95040384999SEric Dumazet static int inet_abc_len(__be32 addr)
9511da177e4SLinus Torvalds {
9521da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9531da177e4SLinus Torvalds 
954f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
9551da177e4SLinus Torvalds 		rc = 0;
9561da177e4SLinus Torvalds 	else {
957714e85beSAl Viro 		__u32 haddr = ntohl(addr);
9581da177e4SLinus Torvalds 
959714e85beSAl Viro 		if (IN_CLASSA(haddr))
9601da177e4SLinus Torvalds 			rc = 8;
961714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9621da177e4SLinus Torvalds 			rc = 16;
963714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9641da177e4SLinus Torvalds 			rc = 24;
9651da177e4SLinus Torvalds 	}
9661da177e4SLinus Torvalds 
9671da177e4SLinus Torvalds 	return rc;
9681da177e4SLinus Torvalds }
9691da177e4SLinus Torvalds 
9701da177e4SLinus Torvalds 
97103aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
9721da177e4SLinus Torvalds {
9731da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
97403aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
9751da177e4SLinus Torvalds 	struct in_device *in_dev;
9761da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
9771da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
9781da177e4SLinus Torvalds 	struct net_device *dev;
9791da177e4SLinus Torvalds 	char *colon;
9801da177e4SLinus Torvalds 	int ret = -EFAULT;
9811da177e4SLinus Torvalds 	int tryaddrmatch = 0;
9821da177e4SLinus Torvalds 
98303aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 	/* save original address for comparison */
9861da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9871da177e4SLinus Torvalds 
98803aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
9891da177e4SLinus Torvalds 	if (colon)
9901da177e4SLinus Torvalds 		*colon = 0;
9911da177e4SLinus Torvalds 
99203aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
9931da177e4SLinus Torvalds 
9941da177e4SLinus Torvalds 	switch (cmd) {
9951da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9961da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9971da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9981da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9991da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10001da177e4SLinus Torvalds 		   so that we do not impose a lock.
10011da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10021da177e4SLinus Torvalds 		 */
10031da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10041da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10051da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10061da177e4SLinus Torvalds 		break;
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1009bf5b30b8SZhao Hongjiang 		ret = -EPERM;
101052e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10111da177e4SLinus Torvalds 			goto out;
10121da177e4SLinus Torvalds 		break;
10131da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10141da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10151da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10161da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1017bf5b30b8SZhao Hongjiang 		ret = -EPERM;
101852e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10191da177e4SLinus Torvalds 			goto out;
10201da177e4SLinus Torvalds 		ret = -EINVAL;
10211da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10221da177e4SLinus Torvalds 			goto out;
10231da177e4SLinus Torvalds 		break;
10241da177e4SLinus Torvalds 	default:
10251da177e4SLinus Torvalds 		ret = -EINVAL;
10261da177e4SLinus Torvalds 		goto out;
10271da177e4SLinus Torvalds 	}
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds 	rtnl_lock();
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds 	ret = -ENODEV;
103203aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10339f9354b9SEric Dumazet 	if (!dev)
10341da177e4SLinus Torvalds 		goto done;
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 	if (colon)
10371da177e4SLinus Torvalds 		*colon = ':';
10381da177e4SLinus Torvalds 
10399f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10409f9354b9SEric Dumazet 	if (in_dev) {
10411da177e4SLinus Torvalds 		if (tryaddrmatch) {
10421da177e4SLinus Torvalds 			/* Matthias Andree */
10431da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10441da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10451da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10461da177e4SLinus Torvalds 			   This is checked above. */
10471da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10481da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
104903aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10501da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10516c91afe1SDavid S. Miller 							ifa->ifa_local) {
10521da177e4SLinus Torvalds 					break; /* found */
10531da177e4SLinus Torvalds 				}
10541da177e4SLinus Torvalds 			}
10551da177e4SLinus Torvalds 		}
10561da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10571da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10581da177e4SLinus Torvalds 		   comparing just the label */
10591da177e4SLinus Torvalds 		if (!ifa) {
10601da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10611da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
106203aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
10631da177e4SLinus Torvalds 					break;
10641da177e4SLinus Torvalds 		}
10651da177e4SLinus Torvalds 	}
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10681da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
10691da177e4SLinus Torvalds 		goto done;
10701da177e4SLinus Torvalds 
10711da177e4SLinus Torvalds 	switch (cmd) {
10721da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
107330e948a3STonghao Zhang 		ret = 0;
10741da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
107503aef17bSAl Viro 		break;
10761da177e4SLinus Torvalds 
10771da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
107830e948a3STonghao Zhang 		ret = 0;
10791da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
108003aef17bSAl Viro 		break;
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
108330e948a3STonghao Zhang 		ret = 0;
10841da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
108503aef17bSAl Viro 		break;
10861da177e4SLinus Torvalds 
10871da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
108830e948a3STonghao Zhang 		ret = 0;
10891da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
109003aef17bSAl Viro 		break;
10911da177e4SLinus Torvalds 
10921da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10931da177e4SLinus Torvalds 		if (colon) {
10941da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
10951da177e4SLinus Torvalds 			if (!ifa)
10961da177e4SLinus Torvalds 				break;
10971da177e4SLinus Torvalds 			ret = 0;
109803aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
10991da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11001da177e4SLinus Torvalds 			break;
11011da177e4SLinus Torvalds 		}
110203aef17bSAl Viro 		ret = dev_change_flags(dev, ifr->ifr_flags);
11031da177e4SLinus Torvalds 		break;
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11061da177e4SLinus Torvalds 		ret = -EINVAL;
11071da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11081da177e4SLinus Torvalds 			break;
11091da177e4SLinus Torvalds 
11101da177e4SLinus Torvalds 		if (!ifa) {
11111da177e4SLinus Torvalds 			ret = -ENOBUFS;
11129f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11139f9354b9SEric Dumazet 			if (!ifa)
11141da177e4SLinus Torvalds 				break;
1115c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11161da177e4SLinus Torvalds 			if (colon)
111703aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11181da177e4SLinus Torvalds 			else
11191da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11201da177e4SLinus Torvalds 		} else {
11211da177e4SLinus Torvalds 			ret = 0;
11221da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11231da177e4SLinus Torvalds 				break;
11241da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11251da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1126148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11271da177e4SLinus Torvalds 		}
11281da177e4SLinus Torvalds 
11291da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11321da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11331da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11341da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11351da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11361da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11371da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11381da177e4SLinus Torvalds 		} else {
11391da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11401da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11411da177e4SLinus Torvalds 		}
11425c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11431da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11441da177e4SLinus Torvalds 		break;
11451da177e4SLinus Torvalds 
11461da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11471da177e4SLinus Torvalds 		ret = 0;
11481da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11491da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11501da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11511da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11521da177e4SLinus Torvalds 		}
11531da177e4SLinus Torvalds 		break;
11541da177e4SLinus Torvalds 
11551da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11561da177e4SLinus Torvalds 		ret = 0;
11571da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11581da177e4SLinus Torvalds 			break;
11591da177e4SLinus Torvalds 		ret = -EINVAL;
11601da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11611da177e4SLinus Torvalds 			break;
11621da177e4SLinus Torvalds 		ret = 0;
11631da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11641da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11651da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11661da177e4SLinus Torvalds 		break;
11671da177e4SLinus Torvalds 
11681da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 		/*
11711da177e4SLinus Torvalds 		 *	The mask we set must be legal.
11721da177e4SLinus Torvalds 		 */
11731da177e4SLinus Torvalds 		ret = -EINVAL;
11741da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
11751da177e4SLinus Torvalds 			break;
11761da177e4SLinus Torvalds 		ret = 0;
11771da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1178a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
11791da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11801da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
11811da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 			/* See if current broadcast address matches
11841da177e4SLinus Torvalds 			 * with current netmask, then recalculate
11851da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
11861da177e4SLinus Torvalds 			 * funny address, so don't touch it since
11871da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
11881da177e4SLinus Torvalds 			 */
11891da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11901da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11911da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1192dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11931da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
11941da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
11951da177e4SLinus Torvalds 			}
11961da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11971da177e4SLinus Torvalds 		}
11981da177e4SLinus Torvalds 		break;
11991da177e4SLinus Torvalds 	}
12001da177e4SLinus Torvalds done:
12011da177e4SLinus Torvalds 	rtnl_unlock();
12021da177e4SLinus Torvalds out:
12031da177e4SLinus Torvalds 	return ret;
12041da177e4SLinus Torvalds }
12051da177e4SLinus Torvalds 
120636fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12071da177e4SLinus Torvalds {
1208e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
12091da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12101da177e4SLinus Torvalds 	struct ifreq ifr;
12111da177e4SLinus Torvalds 	int done = 0;
12121da177e4SLinus Torvalds 
121336fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
121436fd633eSAl Viro 		goto out;
121536fd633eSAl Viro 
12169f9354b9SEric Dumazet 	if (!in_dev)
12171da177e4SLinus Torvalds 		goto out;
12181da177e4SLinus Torvalds 
12199f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12201da177e4SLinus Torvalds 		if (!buf) {
122136fd633eSAl Viro 			done += size;
12221da177e4SLinus Torvalds 			continue;
12231da177e4SLinus Torvalds 		}
122436fd633eSAl Viro 		if (len < size)
12251da177e4SLinus Torvalds 			break;
12261da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12271da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12301da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12311da177e4SLinus Torvalds 								ifa->ifa_local;
12321da177e4SLinus Torvalds 
123336fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12341da177e4SLinus Torvalds 			done = -EFAULT;
12351da177e4SLinus Torvalds 			break;
12361da177e4SLinus Torvalds 		}
123736fd633eSAl Viro 		len  -= size;
123836fd633eSAl Viro 		done += size;
12391da177e4SLinus Torvalds 	}
12401da177e4SLinus Torvalds out:
12411da177e4SLinus Torvalds 	return done;
12421da177e4SLinus Torvalds }
12431da177e4SLinus Torvalds 
12448b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12458b57fd1eSGao Feng 				 int scope)
12468b57fd1eSGao Feng {
12478b57fd1eSGao Feng 	for_primary_ifa(in_dev) {
12488b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12498b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12508b57fd1eSGao Feng 			return ifa->ifa_local;
12518b57fd1eSGao Feng 	} endfor_ifa(in_dev);
12528b57fd1eSGao Feng 
12538b57fd1eSGao Feng 	return 0;
12548b57fd1eSGao Feng }
12558b57fd1eSGao Feng 
1256a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
12571da177e4SLinus Torvalds {
1258a61ced5dSAl Viro 	__be32 addr = 0;
12591da177e4SLinus Torvalds 	struct in_device *in_dev;
1260c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12613f2fb9a8SDavid Ahern 	int master_idx;
12621da177e4SLinus Torvalds 
12631da177e4SLinus Torvalds 	rcu_read_lock();
1264e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
12651da177e4SLinus Torvalds 	if (!in_dev)
12661da177e4SLinus Torvalds 		goto no_in_dev;
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
12691da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
12701da177e4SLinus Torvalds 			continue;
12711da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
12721da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12731da177e4SLinus Torvalds 			break;
12741da177e4SLinus Torvalds 		}
12751da177e4SLinus Torvalds 		if (!addr)
12761da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12771da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12781da177e4SLinus Torvalds 
12791da177e4SLinus Torvalds 	if (addr)
1280c6d14c84SEric Dumazet 		goto out_unlock;
12819f9354b9SEric Dumazet no_in_dev:
12823f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
12831da177e4SLinus Torvalds 
128417b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
128517b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
128617b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
128717b693cdSDavid Lamparter 	 * equality check in the loop below.
128817b693cdSDavid Lamparter 	 */
128917b693cdSDavid Lamparter 	if (master_idx &&
129017b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
129117b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
12928b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
12938b57fd1eSGao Feng 		if (addr)
129417b693cdSDavid Lamparter 			goto out_unlock;
129517b693cdSDavid Lamparter 	}
129617b693cdSDavid Lamparter 
12971da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1298ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
12991da177e4SLinus Torvalds 	   in dev_base list.
13001da177e4SLinus Torvalds 	 */
1301c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13023f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13033f2fb9a8SDavid Ahern 			continue;
13043f2fb9a8SDavid Ahern 
13059f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13069f9354b9SEric Dumazet 		if (!in_dev)
13071da177e4SLinus Torvalds 			continue;
13081da177e4SLinus Torvalds 
13098b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13108b57fd1eSGao Feng 		if (addr)
1311c6d14c84SEric Dumazet 			goto out_unlock;
13121da177e4SLinus Torvalds 	}
1313c6d14c84SEric Dumazet out_unlock:
13141da177e4SLinus Torvalds 	rcu_read_unlock();
13151da177e4SLinus Torvalds 	return addr;
13161da177e4SLinus Torvalds }
13179f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13181da177e4SLinus Torvalds 
131960cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
132060cad5daSAl Viro 			      __be32 local, int scope)
13211da177e4SLinus Torvalds {
13221da177e4SLinus Torvalds 	int same = 0;
1323a144ea4bSAl Viro 	__be32 addr = 0;
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds 	for_ifa(in_dev) {
13261da177e4SLinus Torvalds 		if (!addr &&
13271da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
13281da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
13291da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13301da177e4SLinus Torvalds 			if (same)
13311da177e4SLinus Torvalds 				break;
13321da177e4SLinus Torvalds 		}
13331da177e4SLinus Torvalds 		if (!same) {
13341da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13351da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13361da177e4SLinus Torvalds 			if (same && addr) {
13371da177e4SLinus Torvalds 				if (local || !dst)
13381da177e4SLinus Torvalds 					break;
13391da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
13401da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
13411da177e4SLinus Torvalds 					break;
13421da177e4SLinus Torvalds 				/* No, then can we use new local src? */
13431da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
13441da177e4SLinus Torvalds 					addr = ifa->ifa_local;
13451da177e4SLinus Torvalds 					break;
13461da177e4SLinus Torvalds 				}
13471da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
13481da177e4SLinus Torvalds 				same = 0;
13491da177e4SLinus Torvalds 			}
13501da177e4SLinus Torvalds 		}
13511da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
13521da177e4SLinus Torvalds 
13531da177e4SLinus Torvalds 	return same ? addr : 0;
13541da177e4SLinus Torvalds }
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds /*
13571da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1358b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1359b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
13601da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
13611da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
13621da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
13631da177e4SLinus Torvalds  */
1364b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
13659bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
13661da177e4SLinus Torvalds {
136760cad5daSAl Viro 	__be32 addr = 0;
13689bd85e32SDenis V. Lunev 	struct net_device *dev;
13691da177e4SLinus Torvalds 
137000db4124SIan Morris 	if (in_dev)
13719bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
13721da177e4SLinus Torvalds 
13731da177e4SLinus Torvalds 	rcu_read_lock();
1374c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13759f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13769f9354b9SEric Dumazet 		if (in_dev) {
13771da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
13781da177e4SLinus Torvalds 			if (addr)
13791da177e4SLinus Torvalds 				break;
13801da177e4SLinus Torvalds 		}
13811da177e4SLinus Torvalds 	}
13821da177e4SLinus Torvalds 	rcu_read_unlock();
13831da177e4SLinus Torvalds 
13841da177e4SLinus Torvalds 	return addr;
13851da177e4SLinus Torvalds }
1386eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
13871da177e4SLinus Torvalds 
13881da177e4SLinus Torvalds /*
13891da177e4SLinus Torvalds  *	Device notifier
13901da177e4SLinus Torvalds  */
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
13931da177e4SLinus Torvalds {
1394e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
13951da177e4SLinus Torvalds }
13969f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
13991da177e4SLinus Torvalds {
1400e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14011da177e4SLinus Torvalds }
14029f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14031da177e4SLinus Torvalds 
14043ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14053ad7d246SKrister Johansen {
14063ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14073ad7d246SKrister Johansen }
14083ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14093ad7d246SKrister Johansen 
14103ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14113ad7d246SKrister Johansen {
14123ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14133ad7d246SKrister Johansen 	    nb);
14143ad7d246SKrister Johansen }
14153ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14163ad7d246SKrister Johansen 
14179f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14189f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14191da177e4SLinus Torvalds */
14201da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14211da177e4SLinus Torvalds {
14221da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14231da177e4SLinus Torvalds 	int named = 0;
14241da177e4SLinus Torvalds 
14251da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
14261da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14271da177e4SLinus Torvalds 
14281da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14291da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14301da177e4SLinus Torvalds 		if (named++ == 0)
1431573bf470SThomas Graf 			goto skip;
143244344b2aSMark McLoughlin 		dot = strchr(old, ':');
143351456b29SIan Morris 		if (!dot) {
14341da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14351da177e4SLinus Torvalds 			dot = old;
14361da177e4SLinus Torvalds 		}
14379f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14381da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14399f9354b9SEric Dumazet 		else
14401da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1441573bf470SThomas Graf skip:
1442573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
14431da177e4SLinus Torvalds 	}
14441da177e4SLinus Torvalds }
14451da177e4SLinus Torvalds 
144640384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
144706770843SBreno Leitao {
1448b5476022SEric Dumazet 	return mtu >= IPV4_MIN_MTU;
144906770843SBreno Leitao }
145006770843SBreno Leitao 
1451d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1452d11327adSIan Campbell 					struct in_device *in_dev)
1453d11327adSIan Campbell 
1454d11327adSIan Campbell {
1455b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1456d11327adSIan Campbell 
1457b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1458b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1459d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
14606c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
14616c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1462d11327adSIan Campbell 			 dev->dev_addr, NULL);
1463d11327adSIan Campbell 	}
1464b76d0789SZoltan Kiss }
1465d11327adSIan Campbell 
14661da177e4SLinus Torvalds /* Called only under RTNL semaphore */
14671da177e4SLinus Torvalds 
14681da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
14691da177e4SLinus Torvalds 			 void *ptr)
14701da177e4SLinus Torvalds {
1471351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1472748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds 	ASSERT_RTNL();
14751da177e4SLinus Torvalds 
14761da177e4SLinus Torvalds 	if (!in_dev) {
14778030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
14781da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
147920e61da7SWANG Cong 			if (IS_ERR(in_dev))
148020e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
14810cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
148242f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
148342f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
14841da177e4SLinus Torvalds 			}
148506770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
148606770843SBreno Leitao 			/* Re-enabling IP */
148706770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
148806770843SBreno Leitao 				in_dev = inetdev_init(dev);
14898030f544SHerbert Xu 		}
14901da177e4SLinus Torvalds 		goto out;
14911da177e4SLinus Torvalds 	}
14921da177e4SLinus Torvalds 
14931da177e4SLinus Torvalds 	switch (event) {
14941da177e4SLinus Torvalds 	case NETDEV_REGISTER:
149591df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1496a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
14971da177e4SLinus Torvalds 		break;
14981da177e4SLinus Torvalds 	case NETDEV_UP:
149906770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15001da177e4SLinus Torvalds 			break;
15010cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15029f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15039f9354b9SEric Dumazet 
15049f9354b9SEric Dumazet 			if (ifa) {
1505fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15061da177e4SLinus Torvalds 				ifa->ifa_local =
15071da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15081da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15091da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15101da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15111da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15121da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15131da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15145c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15155c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1516dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1517dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15181da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15191da177e4SLinus Torvalds 			}
15201da177e4SLinus Torvalds 		}
15211da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1522eefef1cfSStephen Hemminger 		/* fall through */
1523eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1524d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1525d11327adSIan Campbell 			break;
1526d11327adSIan Campbell 		/* fall through */
1527d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1528a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1529d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15301da177e4SLinus Torvalds 		break;
15311da177e4SLinus Torvalds 	case NETDEV_DOWN:
15321da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15331da177e4SLinus Torvalds 		break;
153493d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
153575c78500SMoni Shoua 		ip_mc_unmap(in_dev);
153675c78500SMoni Shoua 		break;
153793d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
153875c78500SMoni Shoua 		ip_mc_remap(in_dev);
153975c78500SMoni Shoua 		break;
15401da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
154106770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15421da177e4SLinus Torvalds 			break;
154306770843SBreno Leitao 		/* disable IP when MTU is not enough */
1544fcfd6dfaSGustavo A. R. Silva 		/* fall through */
15451da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
15461da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
15471da177e4SLinus Torvalds 		break;
15481da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
15491da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
15501da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
15511da177e4SLinus Torvalds 		 */
15521da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
15531da177e4SLinus Torvalds 
155451602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
155566f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
15561da177e4SLinus Torvalds 		break;
15571da177e4SLinus Torvalds 	}
15581da177e4SLinus Torvalds out:
15591da177e4SLinus Torvalds 	return NOTIFY_DONE;
15601da177e4SLinus Torvalds }
15611da177e4SLinus Torvalds 
15621da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
15631da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
15641da177e4SLinus Torvalds };
15651da177e4SLinus Torvalds 
156640384999SEric Dumazet static size_t inet_nlmsg_size(void)
1567339bf98fSThomas Graf {
1568339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1569339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1570339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1571339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1572ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
157363b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1574af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
157563b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1576339bf98fSThomas Graf }
1577339bf98fSThomas Graf 
15785c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
15795c766d64SJiri Pirko {
15805c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
15815c766d64SJiri Pirko }
15825c766d64SJiri Pirko 
15835c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
15845c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
15855c766d64SJiri Pirko {
15865c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
15875c766d64SJiri Pirko 
15885c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
15895c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
15905c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
15915c766d64SJiri Pirko 	ci.ifa_valid = valid;
15925c766d64SJiri Pirko 
15935c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
15945c766d64SJiri Pirko }
15955c766d64SJiri Pirko 
15961da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1597978a46faSChristian Brauner 			    struct inet_fill_args *args)
15981da177e4SLinus Torvalds {
15991da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16001da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16015c766d64SJiri Pirko 	u32 preferred, valid;
16021da177e4SLinus Torvalds 
1603978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1604978a46faSChristian Brauner 			args->flags);
160551456b29SIan Morris 	if (!nlh)
160626932566SPatrick McHardy 		return -EMSGSIZE;
160747f68512SThomas Graf 
160847f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16091da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16101da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16115c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16121da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16131da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16141da177e4SLinus Torvalds 
1615978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1616978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1617d3807145SChristian Brauner 		goto nla_put_failure;
1618d3807145SChristian Brauner 
16195c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16205c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16215c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16225c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16235c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16245c766d64SJiri Pirko 
16255c766d64SJiri Pirko 			if (preferred > tval)
16265c766d64SJiri Pirko 				preferred -= tval;
16275c766d64SJiri Pirko 			else
16285c766d64SJiri Pirko 				preferred = 0;
16295c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16305c766d64SJiri Pirko 				if (valid > tval)
16315c766d64SJiri Pirko 					valid -= tval;
16325c766d64SJiri Pirko 				else
16335c766d64SJiri Pirko 					valid = 0;
16345c766d64SJiri Pirko 			}
16355c766d64SJiri Pirko 		}
16365c766d64SJiri Pirko 	} else {
16375c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16385c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16395c766d64SJiri Pirko 	}
1640f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1641930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1642f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1643930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1644f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1645930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1646f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
16475c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1648ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1649af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1650af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
16515c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
16525c766d64SJiri Pirko 			  preferred, valid))
1653f3756b79SDavid S. Miller 		goto nla_put_failure;
165447f68512SThomas Graf 
1655053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1656053c095aSJohannes Berg 	return 0;
165747f68512SThomas Graf 
165847f68512SThomas Graf nla_put_failure:
165926932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
166026932566SPatrick McHardy 	return -EMSGSIZE;
16611da177e4SLinus Torvalds }
16621da177e4SLinus Torvalds 
1663c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1664c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1665c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
1666c33078e3SDavid Ahern 				      struct netlink_ext_ack *extack)
1667c33078e3SDavid Ahern {
1668c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1669c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1670c33078e3SDavid Ahern 	int err, i;
1671c33078e3SDavid Ahern 
1672c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1673c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1674c33078e3SDavid Ahern 		return -EINVAL;
1675c33078e3SDavid Ahern 	}
1676c33078e3SDavid Ahern 
1677c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1678c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1679c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1680c33078e3SDavid Ahern 		return -EINVAL;
1681c33078e3SDavid Ahern 	}
1682c33078e3SDavid Ahern 	if (ifm->ifa_index) {
1683c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Filter by device index not supported for address dump");
1684c33078e3SDavid Ahern 		return -EINVAL;
1685c33078e3SDavid Ahern 	}
1686c33078e3SDavid Ahern 
1687c33078e3SDavid Ahern 	err = nlmsg_parse_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1688c33078e3SDavid Ahern 				 ifa_ipv4_policy, extack);
1689c33078e3SDavid Ahern 	if (err < 0)
1690c33078e3SDavid Ahern 		return err;
1691c33078e3SDavid Ahern 
1692c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1693c33078e3SDavid Ahern 		if (!tb[i])
1694c33078e3SDavid Ahern 			continue;
1695c33078e3SDavid Ahern 
1696c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1697c33078e3SDavid Ahern 			struct net *net;
1698c33078e3SDavid Ahern 
1699c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1700c33078e3SDavid Ahern 
1701c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1702c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1703c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1704c33078e3SDavid Ahern 				return PTR_ERR(net);
1705c33078e3SDavid Ahern 			}
1706c33078e3SDavid Ahern 			*tgt_net = net;
1707c33078e3SDavid Ahern 		} else {
1708c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1709c33078e3SDavid Ahern 			return -EINVAL;
1710c33078e3SDavid Ahern 		}
1711c33078e3SDavid Ahern 	}
1712c33078e3SDavid Ahern 
1713c33078e3SDavid Ahern 	return 0;
1714c33078e3SDavid Ahern }
1715c33078e3SDavid Ahern 
17161da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
17171da177e4SLinus Torvalds {
1718c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1719978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1720978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1721c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1722978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1723978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1724978a46faSChristian Brauner 		.netnsid = -1,
1725978a46faSChristian Brauner 	};
17263b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1727d3807145SChristian Brauner 	struct net *tgt_net = net;
1728eec4df98SEric Dumazet 	int h, s_h;
1729eec4df98SEric Dumazet 	int idx, s_idx;
1730eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
17311da177e4SLinus Torvalds 	struct net_device *dev;
17321da177e4SLinus Torvalds 	struct in_device *in_dev;
17331da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1734eec4df98SEric Dumazet 	struct hlist_head *head;
17351da177e4SLinus Torvalds 
1736eec4df98SEric Dumazet 	s_h = cb->args[0];
1737eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1738eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1739eec4df98SEric Dumazet 
1740c33078e3SDavid Ahern 	if (cb->strict_check) {
1741c33078e3SDavid Ahern 		int err;
1742d3807145SChristian Brauner 
1743c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
1744c33078e3SDavid Ahern 						 skb->sk, cb->extack);
1745c33078e3SDavid Ahern 		if (err < 0)
1746c33078e3SDavid Ahern 			return err;
1747d3807145SChristian Brauner 	}
1748d3807145SChristian Brauner 
1749eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
17507562f876SPavel Emelianov 		idx = 0;
1751d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1752eec4df98SEric Dumazet 		rcu_read_lock();
1753d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1754d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1755b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
17561da177e4SLinus Torvalds 			if (idx < s_idx)
17577562f876SPavel Emelianov 				goto cont;
17584b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
17591da177e4SLinus Torvalds 				s_ip_idx = 0;
1760eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
17619f9354b9SEric Dumazet 			if (!in_dev)
17627562f876SPavel Emelianov 				goto cont;
17631da177e4SLinus Torvalds 
17641da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
17651da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
17661da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1767596e4150SStephen Hemminger 					continue;
1768978a46faSChristian Brauner 				if (inet_fill_ifaddr(skb, ifa, &fillargs) < 0) {
1769eec4df98SEric Dumazet 					rcu_read_unlock();
17701da177e4SLinus Torvalds 					goto done;
17711da177e4SLinus Torvalds 				}
17720465277fSNicolas Dichtel 				nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1773eec4df98SEric Dumazet 			}
17747562f876SPavel Emelianov cont:
17757562f876SPavel Emelianov 			idx++;
17761da177e4SLinus Torvalds 		}
1777eec4df98SEric Dumazet 		rcu_read_unlock();
1778eec4df98SEric Dumazet 	}
17791da177e4SLinus Torvalds 
17801da177e4SLinus Torvalds done:
1781eec4df98SEric Dumazet 	cb->args[0] = h;
1782eec4df98SEric Dumazet 	cb->args[1] = idx;
1783eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
1784978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1785d3807145SChristian Brauner 		put_net(tgt_net);
17861da177e4SLinus Torvalds 
17871da177e4SLinus Torvalds 	return skb->len;
17881da177e4SLinus Torvalds }
17891da177e4SLinus Torvalds 
1790d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
179115e47304SEric W. Biederman 		      u32 portid)
17921da177e4SLinus Torvalds {
1793978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1794978a46faSChristian Brauner 		.portid = portid,
1795978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1796978a46faSChristian Brauner 		.event = event,
1797978a46faSChristian Brauner 		.flags = 0,
1798978a46faSChristian Brauner 		.netnsid = -1,
1799978a46faSChristian Brauner 	};
180047f68512SThomas Graf 	struct sk_buff *skb;
1801d6062cbbSThomas Graf 	int err = -ENOBUFS;
18024b8aa9abSDenis V. Lunev 	struct net *net;
18031da177e4SLinus Torvalds 
1804c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1805339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
180651456b29SIan Morris 	if (!skb)
1807d6062cbbSThomas Graf 		goto errout;
1808d6062cbbSThomas Graf 
1809978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
181026932566SPatrick McHardy 	if (err < 0) {
181126932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
181226932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
181326932566SPatrick McHardy 		kfree_skb(skb);
181426932566SPatrick McHardy 		goto errout;
181526932566SPatrick McHardy 	}
181615e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
18171ce85fe4SPablo Neira Ayuso 	return;
1818d6062cbbSThomas Graf errout:
1819d6062cbbSThomas Graf 	if (err < 0)
18204b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
18211da177e4SLinus Torvalds }
18221da177e4SLinus Torvalds 
1823b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1824b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
18259f0f7272SThomas Graf {
18261fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
18279f0f7272SThomas Graf 
18289f0f7272SThomas Graf 	if (!in_dev)
18299f0f7272SThomas Graf 		return 0;
18309f0f7272SThomas Graf 
18319f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
18329f0f7272SThomas Graf }
18339f0f7272SThomas Graf 
1834d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1835d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
18369f0f7272SThomas Graf {
18371fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
18389f0f7272SThomas Graf 	struct nlattr *nla;
18399f0f7272SThomas Graf 	int i;
18409f0f7272SThomas Graf 
18419f0f7272SThomas Graf 	if (!in_dev)
18429f0f7272SThomas Graf 		return -ENODATA;
18439f0f7272SThomas Graf 
18449f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
184551456b29SIan Morris 	if (!nla)
18469f0f7272SThomas Graf 		return -EMSGSIZE;
18479f0f7272SThomas Graf 
18489f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
18499f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
18509f0f7272SThomas Graf 
18519f0f7272SThomas Graf 	return 0;
18529f0f7272SThomas Graf }
18539f0f7272SThomas Graf 
18549f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
18559f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
18569f0f7272SThomas Graf };
18579f0f7272SThomas Graf 
1858cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1859cf7afbfeSThomas Graf 				 const struct nlattr *nla)
18609f0f7272SThomas Graf {
18619f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
18629f0f7272SThomas Graf 	int err, rem;
18639f0f7272SThomas Graf 
18645fa85a09SFlorian Westphal 	if (dev && !__in_dev_get_rcu(dev))
1865cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
18669f0f7272SThomas Graf 
1867fceb6435SJohannes Berg 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy, NULL);
18689f0f7272SThomas Graf 	if (err < 0)
18699f0f7272SThomas Graf 		return err;
18709f0f7272SThomas Graf 
18719f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
18729f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
18739f0f7272SThomas Graf 			int cfgid = nla_type(a);
18749f0f7272SThomas Graf 
18759f0f7272SThomas Graf 			if (nla_len(a) < 4)
18769f0f7272SThomas Graf 				return -EINVAL;
18779f0f7272SThomas Graf 
18789f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
18799f0f7272SThomas Graf 				return -EINVAL;
18809f0f7272SThomas Graf 		}
18819f0f7272SThomas Graf 	}
18829f0f7272SThomas Graf 
1883cf7afbfeSThomas Graf 	return 0;
1884cf7afbfeSThomas Graf }
1885cf7afbfeSThomas Graf 
1886cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1887cf7afbfeSThomas Graf {
18885fa85a09SFlorian Westphal 	struct in_device *in_dev = __in_dev_get_rcu(dev);
1889cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1890cf7afbfeSThomas Graf 	int rem;
1891cf7afbfeSThomas Graf 
1892cf7afbfeSThomas Graf 	if (!in_dev)
1893cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1894cf7afbfeSThomas Graf 
1895fceb6435SJohannes Berg 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
1896cf7afbfeSThomas Graf 		BUG();
1897cf7afbfeSThomas Graf 
18989f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
18999f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
19009f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
19019f0f7272SThomas Graf 	}
19029f0f7272SThomas Graf 
19039f0f7272SThomas Graf 	return 0;
19049f0f7272SThomas Graf }
19059f0f7272SThomas Graf 
1906edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1907edc9e748SNicolas Dichtel {
1908edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1909edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1910136ba622SZhang Shengju 	bool all = false;
1911edc9e748SNicolas Dichtel 
1912136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1913136ba622SZhang Shengju 		all = true;
1914136ba622SZhang Shengju 
1915136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
1916edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1917136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
1918cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1919136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
1920d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
19215cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
19225cbf777cSXin Long 		size += nla_total_size(4);
1923136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
1924f085ff1cSstephen hemminger 		size += nla_total_size(4);
1925136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
1926974d7af5SAndy Gospodarek 		size += nla_total_size(4);
1927edc9e748SNicolas Dichtel 
1928edc9e748SNicolas Dichtel 	return size;
1929edc9e748SNicolas Dichtel }
1930edc9e748SNicolas Dichtel 
1931edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1932edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1933edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1934edc9e748SNicolas Dichtel 				     int type)
1935edc9e748SNicolas Dichtel {
1936edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1937edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1938136ba622SZhang Shengju 	bool all = false;
1939edc9e748SNicolas Dichtel 
1940edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1941edc9e748SNicolas Dichtel 			flags);
194251456b29SIan Morris 	if (!nlh)
1943edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1944edc9e748SNicolas Dichtel 
1945136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1946136ba622SZhang Shengju 		all = true;
1947136ba622SZhang Shengju 
1948edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1949edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1950edc9e748SNicolas Dichtel 
1951edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1952edc9e748SNicolas Dichtel 		goto nla_put_failure;
1953edc9e748SNicolas Dichtel 
1954b5c9641dSDavid Ahern 	if (!devconf)
1955b5c9641dSDavid Ahern 		goto out;
1956b5c9641dSDavid Ahern 
1957136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
1958edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1959edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1960edc9e748SNicolas Dichtel 		goto nla_put_failure;
1961136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
1962cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1963cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1964cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1965136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
1966d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1967d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1968d67b8c61SNicolas Dichtel 		goto nla_put_failure;
19695cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
19705cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
19715cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
19725cbf777cSXin Long 		goto nla_put_failure;
1973136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
197409aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
1975f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
1976f085ff1cSstephen hemminger 		goto nla_put_failure;
1977136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
1978974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
1979974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
1980974d7af5SAndy Gospodarek 		goto nla_put_failure;
1981edc9e748SNicolas Dichtel 
1982b5c9641dSDavid Ahern out:
1983053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1984053c095aSJohannes Berg 	return 0;
1985edc9e748SNicolas Dichtel 
1986edc9e748SNicolas Dichtel nla_put_failure:
1987edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1988edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1989edc9e748SNicolas Dichtel }
1990edc9e748SNicolas Dichtel 
19913b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
19923b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
1993edc9e748SNicolas Dichtel {
1994edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1995edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1996edc9e748SNicolas Dichtel 
1997fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
199851456b29SIan Morris 	if (!skb)
1999edc9e748SNicolas Dichtel 		goto errout;
2000edc9e748SNicolas Dichtel 
2001edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
20023b022865SDavid Ahern 					event, 0, type);
2003edc9e748SNicolas Dichtel 	if (err < 0) {
2004edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2005edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2006edc9e748SNicolas Dichtel 		kfree_skb(skb);
2007edc9e748SNicolas Dichtel 		goto errout;
2008edc9e748SNicolas Dichtel 	}
2009fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2010edc9e748SNicolas Dichtel 	return;
2011edc9e748SNicolas Dichtel errout:
2012edc9e748SNicolas Dichtel 	if (err < 0)
2013edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2014edc9e748SNicolas Dichtel }
2015edc9e748SNicolas Dichtel 
20169e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
20179e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
20189e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2019cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
202009aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2021974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
20229e551110SNicolas Dichtel };
20239e551110SNicolas Dichtel 
20249e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2025c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2026c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
20279e551110SNicolas Dichtel {
20289e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
20299e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
20309e551110SNicolas Dichtel 	struct netconfmsg *ncm;
20319e551110SNicolas Dichtel 	struct sk_buff *skb;
20329e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
20339e551110SNicolas Dichtel 	struct in_device *in_dev;
20349e551110SNicolas Dichtel 	struct net_device *dev;
20359e551110SNicolas Dichtel 	int ifindex;
20369e551110SNicolas Dichtel 	int err;
20379e551110SNicolas Dichtel 
20389e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
2039c21ef3e3SDavid Ahern 			  devconf_ipv4_policy, extack);
20409e551110SNicolas Dichtel 	if (err < 0)
20419e551110SNicolas Dichtel 		goto errout;
20429e551110SNicolas Dichtel 
2043a97eb33fSAnton Protopopov 	err = -EINVAL;
20449e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
20459e551110SNicolas Dichtel 		goto errout;
20469e551110SNicolas Dichtel 
20479e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
20489e551110SNicolas Dichtel 	switch (ifindex) {
20499e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
20509e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
20519e551110SNicolas Dichtel 		break;
20529e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
20539e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
20549e551110SNicolas Dichtel 		break;
20559e551110SNicolas Dichtel 	default:
20569e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
205751456b29SIan Morris 		if (!dev)
20589e551110SNicolas Dichtel 			goto errout;
20599e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
206051456b29SIan Morris 		if (!in_dev)
20619e551110SNicolas Dichtel 			goto errout;
20629e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
20639e551110SNicolas Dichtel 		break;
20649e551110SNicolas Dichtel 	}
20659e551110SNicolas Dichtel 
20669e551110SNicolas Dichtel 	err = -ENOBUFS;
2067fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
206851456b29SIan Morris 	if (!skb)
20699e551110SNicolas Dichtel 		goto errout;
20709e551110SNicolas Dichtel 
20719e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
20729e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
20739e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2074136ba622SZhang Shengju 					NETCONFA_ALL);
20759e551110SNicolas Dichtel 	if (err < 0) {
20769e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
20779e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
20789e551110SNicolas Dichtel 		kfree_skb(skb);
20799e551110SNicolas Dichtel 		goto errout;
20809e551110SNicolas Dichtel 	}
20819e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
20829e551110SNicolas Dichtel errout:
20839e551110SNicolas Dichtel 	return err;
20849e551110SNicolas Dichtel }
20859e551110SNicolas Dichtel 
20867a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
20877a674200SNicolas Dichtel 				     struct netlink_callback *cb)
20887a674200SNicolas Dichtel {
2089addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
20907a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
20917a674200SNicolas Dichtel 	int h, s_h;
20927a674200SNicolas Dichtel 	int idx, s_idx;
20937a674200SNicolas Dichtel 	struct net_device *dev;
20947a674200SNicolas Dichtel 	struct in_device *in_dev;
20957a674200SNicolas Dichtel 	struct hlist_head *head;
20967a674200SNicolas Dichtel 
2097addd383fSDavid Ahern 	if (cb->strict_check) {
2098addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2099addd383fSDavid Ahern 		struct netconfmsg *ncm;
2100addd383fSDavid Ahern 
2101addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2102addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2103addd383fSDavid Ahern 			return -EINVAL;
2104addd383fSDavid Ahern 		}
2105addd383fSDavid Ahern 
2106addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2107addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2108addd383fSDavid Ahern 			return -EINVAL;
2109addd383fSDavid Ahern 		}
2110addd383fSDavid Ahern 	}
2111addd383fSDavid Ahern 
21127a674200SNicolas Dichtel 	s_h = cb->args[0];
21137a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
21147a674200SNicolas Dichtel 
21157a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
21167a674200SNicolas Dichtel 		idx = 0;
21177a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
21187a674200SNicolas Dichtel 		rcu_read_lock();
21190465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
21200465277fSNicolas Dichtel 			  net->dev_base_seq;
21217a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
21227a674200SNicolas Dichtel 			if (idx < s_idx)
21237a674200SNicolas Dichtel 				goto cont;
21247a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
21257a674200SNicolas Dichtel 			if (!in_dev)
21267a674200SNicolas Dichtel 				goto cont;
21277a674200SNicolas Dichtel 
21287a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
21297a674200SNicolas Dichtel 						      &in_dev->cnf,
21307a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2131addd383fSDavid Ahern 						      nlh->nlmsg_seq,
21327a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
21337a674200SNicolas Dichtel 						      NLM_F_MULTI,
2134136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
21357a674200SNicolas Dichtel 				rcu_read_unlock();
21367a674200SNicolas Dichtel 				goto done;
21377a674200SNicolas Dichtel 			}
21380465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
21397a674200SNicolas Dichtel cont:
21407a674200SNicolas Dichtel 			idx++;
21417a674200SNicolas Dichtel 		}
21427a674200SNicolas Dichtel 		rcu_read_unlock();
21437a674200SNicolas Dichtel 	}
21447a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
21457a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
21467a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
21477a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2148addd383fSDavid Ahern 					      nlh->nlmsg_seq,
21497a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2150136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
21517a674200SNicolas Dichtel 			goto done;
21527a674200SNicolas Dichtel 		else
21537a674200SNicolas Dichtel 			h++;
21547a674200SNicolas Dichtel 	}
21557a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
21567a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
21577a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
21587a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2159addd383fSDavid Ahern 					      nlh->nlmsg_seq,
21607a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2161136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
21627a674200SNicolas Dichtel 			goto done;
21637a674200SNicolas Dichtel 		else
21647a674200SNicolas Dichtel 			h++;
21657a674200SNicolas Dichtel 	}
21667a674200SNicolas Dichtel done:
21677a674200SNicolas Dichtel 	cb->args[0] = h;
21687a674200SNicolas Dichtel 	cb->args[1] = idx;
21697a674200SNicolas Dichtel 
21707a674200SNicolas Dichtel 	return skb->len;
21717a674200SNicolas Dichtel }
21727a674200SNicolas Dichtel 
21731da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
21741da177e4SLinus Torvalds 
2175c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
217631be3085SHerbert Xu {
217731be3085SHerbert Xu 	struct net_device *dev;
217831be3085SHerbert Xu 
217931be3085SHerbert Xu 	rcu_read_lock();
2180c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2181c6d14c84SEric Dumazet 		struct in_device *in_dev;
2182c6d14c84SEric Dumazet 
218331be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
218431be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
21859355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2186c6d14c84SEric Dumazet 	}
218731be3085SHerbert Xu 	rcu_read_unlock();
218831be3085SHerbert Xu }
218931be3085SHerbert Xu 
2190c6d14c84SEric Dumazet /* called with RTNL locked */
2191c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
219268dd299bSPavel Emelyanov {
219368dd299bSPavel Emelyanov 	struct net_device *dev;
2194586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
219568dd299bSPavel Emelyanov 
2196586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
21979355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
21983b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
21993b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2200edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2201edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
22023b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22033b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2204edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2205edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
220668dd299bSPavel Emelyanov 
2207c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
220868dd299bSPavel Emelyanov 		struct in_device *in_dev;
2209fa17806cSEric Dumazet 
22100187bdfbSBen Hutchings 		if (on)
22110187bdfbSBen Hutchings 			dev_disable_lro(dev);
2212fa17806cSEric Dumazet 
2213fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2214edc9e748SNicolas Dichtel 		if (in_dev) {
221568dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
22163b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22173b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2218edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2219edc9e748SNicolas Dichtel 		}
222068dd299bSPavel Emelyanov 	}
222168dd299bSPavel Emelyanov }
222268dd299bSPavel Emelyanov 
2223f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2224f085ff1cSstephen hemminger {
2225f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2226f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2227f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2228f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2229f085ff1cSstephen hemminger 	else {
2230f085ff1cSstephen hemminger 		struct in_device *idev
2231f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2232f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2233f085ff1cSstephen hemminger 	}
2234f085ff1cSstephen hemminger }
2235f085ff1cSstephen hemminger 
2236fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
22378d65af78SAlexey Dobriyan 			     void __user *buffer,
223831be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
223931be3085SHerbert Xu {
2240d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
22418d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2242d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
224331be3085SHerbert Xu 
224431be3085SHerbert Xu 	if (write) {
224531be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2246c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
224731be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2248f085ff1cSstephen hemminger 		int ifindex;
224931be3085SHerbert Xu 
225031be3085SHerbert Xu 		set_bit(i, cnf->state);
225131be3085SHerbert Xu 
22529355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2253c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2254d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2255d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2256d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
22574ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2258f085ff1cSstephen hemminger 
22595cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
22605cbf777cSXin Long 		    new_value != old_value)
22615cbf777cSXin Long 			rt_cache_flush(net);
22625cbf777cSXin Long 
2263cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2264cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2265f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
22663b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22673b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2268cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2269cc535dfbSNicolas Dichtel 		}
2270f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2271f085ff1cSstephen hemminger 		    new_value != old_value) {
2272f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
22733b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22743b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2275f085ff1cSstephen hemminger 						    ifindex, cnf);
2276f085ff1cSstephen hemminger 		}
2277974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2278974d7af5SAndy Gospodarek 		    new_value != old_value) {
2279974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
22803b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22813b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2282974d7af5SAndy Gospodarek 						    ifindex, cnf);
2283974d7af5SAndy Gospodarek 		}
228431be3085SHerbert Xu 	}
228531be3085SHerbert Xu 
228631be3085SHerbert Xu 	return ret;
228731be3085SHerbert Xu }
228831be3085SHerbert Xu 
2289fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
22908d65af78SAlexey Dobriyan 				  void __user *buffer,
22911da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
22921da177e4SLinus Torvalds {
22931da177e4SLinus Torvalds 	int *valp = ctl->data;
22941da177e4SLinus Torvalds 	int val = *valp;
229588af182eSEric W. Biederman 	loff_t pos = *ppos;
22968d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
22971da177e4SLinus Torvalds 
22981da177e4SLinus Torvalds 	if (write && *valp != val) {
2299c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2300c0ce9fb3SPavel Emelyanov 
23010187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
230288af182eSEric W. Biederman 			if (!rtnl_trylock()) {
230388af182eSEric W. Biederman 				/* Restore the original values before restarting */
230488af182eSEric W. Biederman 				*valp = val;
230588af182eSEric W. Biederman 				*ppos = pos;
23069b8adb5eSEric W. Biederman 				return restart_syscall();
230788af182eSEric W. Biederman 			}
23080187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2309c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2310edc9e748SNicolas Dichtel 			} else {
23110187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
23120187bdfbSBen Hutchings 				struct in_device *idev =
23130187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2314edc9e748SNicolas Dichtel 				if (*valp)
23150187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
23163b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2317edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2318edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2319edc9e748SNicolas Dichtel 							    cnf);
23200187bdfbSBen Hutchings 			}
23210187bdfbSBen Hutchings 			rtnl_unlock();
23224ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2323edc9e748SNicolas Dichtel 		} else
23243b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23253b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2326edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2327edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
23280187bdfbSBen Hutchings 	}
23291da177e4SLinus Torvalds 
23301da177e4SLinus Torvalds 	return ret;
23311da177e4SLinus Torvalds }
23321da177e4SLinus Torvalds 
2333fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
23348d65af78SAlexey Dobriyan 				void __user *buffer,
23351da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
23361da177e4SLinus Torvalds {
23371da177e4SLinus Torvalds 	int *valp = ctl->data;
23381da177e4SLinus Torvalds 	int val = *valp;
23398d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
234076e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
23411da177e4SLinus Torvalds 
23421da177e4SLinus Torvalds 	if (write && *valp != val)
23434ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
23441da177e4SLinus Torvalds 
23451da177e4SLinus Torvalds 	return ret;
23461da177e4SLinus Torvalds }
23471da177e4SLinus Torvalds 
2348f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
234942f811b8SHerbert Xu 	{ \
235042f811b8SHerbert Xu 		.procname	= name, \
235142f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
235202291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
235342f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
235442f811b8SHerbert Xu 		.mode		= mval, \
235542f811b8SHerbert Xu 		.proc_handler	= proc, \
235631be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
235742f811b8SHerbert Xu 	}
235842f811b8SHerbert Xu 
235942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2360f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
236142f811b8SHerbert Xu 
236242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2363f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
236442f811b8SHerbert Xu 
2365f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2366f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
236742f811b8SHerbert Xu 
236842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2369f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
237042f811b8SHerbert Xu 
23711da177e4SLinus Torvalds static struct devinet_sysctl_table {
23721da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
237302291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
23741da177e4SLinus Torvalds } devinet_sysctl = {
23751da177e4SLinus Torvalds 	.devinet_vars = {
237642f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2377f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
237842f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
23795cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
238042f811b8SHerbert Xu 
238142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
238242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
238342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
238442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
238542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
238642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
238742f811b8SHerbert Xu 					"accept_source_route"),
23888153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
238928f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
239042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
239142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
239242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
239342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
239442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
239542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
239642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
239742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
239842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2399eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
240065324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
24015c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
24025c6fe01cSWilliam Manley 					"force_igmp_version"),
24032690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
24042690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
24052690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
24062690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
24070eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
24080eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
240997daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
241097daf331SJohannes Berg 					"drop_gratuitous_arp"),
241142f811b8SHerbert Xu 
241242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
241342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
241442f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
241542f811b8SHerbert Xu 					      "promote_secondaries"),
2416d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2417d0daebc3SThomas Graf 					      "route_localnet"),
241812b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
241912b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
24201da177e4SLinus Torvalds 	},
24211da177e4SLinus Torvalds };
24221da177e4SLinus Torvalds 
2423ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
242429c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
24251da177e4SLinus Torvalds {
24261da177e4SLinus Torvalds 	int i;
24279fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
24288607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2429bfada697SPavel Emelyanov 
24309fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
24311da177e4SLinus Torvalds 	if (!t)
24329fa89642SPavel Emelyanov 		goto out;
24339fa89642SPavel Emelyanov 
24341da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
24351da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
243631be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2437c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
24381da177e4SLinus Torvalds 	}
24391da177e4SLinus Torvalds 
24408607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
24411da177e4SLinus Torvalds 
24428607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
24431da177e4SLinus Torvalds 	if (!t->sysctl_header)
24448607ddb8SEric W. Biederman 		goto free;
24451da177e4SLinus Torvalds 
24461da177e4SLinus Torvalds 	p->sysctl = t;
244729c994e3SNicolas Dichtel 
24483b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
24493b022865SDavid Ahern 				    ifindex, p);
2450ea40b324SPavel Emelyanov 	return 0;
24511da177e4SLinus Torvalds 
24521da177e4SLinus Torvalds free:
24531da177e4SLinus Torvalds 	kfree(t);
24549fa89642SPavel Emelyanov out:
2455ea40b324SPavel Emelyanov 	return -ENOBUFS;
24561da177e4SLinus Torvalds }
24571da177e4SLinus Torvalds 
2458b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2459b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
246066f27a52SPavel Emelyanov {
246151602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
246266f27a52SPavel Emelyanov 
2463b5c9641dSDavid Ahern 	if (t) {
246451602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2465ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
24661da177e4SLinus Torvalds 		kfree(t);
24671da177e4SLinus Torvalds 	}
246851602b2aSPavel Emelyanov 
2469b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2470b5c9641dSDavid Ahern }
2471b5c9641dSDavid Ahern 
247220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
247351602b2aSPavel Emelyanov {
247420e61da7SWANG Cong 	int err;
247520e61da7SWANG Cong 
247620e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
247720e61da7SWANG Cong 		return -EINVAL;
247820e61da7SWANG Cong 
247920e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
248020e61da7SWANG Cong 	if (err)
248120e61da7SWANG Cong 		return err;
248220e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
248329c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
248420e61da7SWANG Cong 	if (err)
248520e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
248620e61da7SWANG Cong 	return err;
248751602b2aSPavel Emelyanov }
248851602b2aSPavel Emelyanov 
248951602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
249051602b2aSPavel Emelyanov {
2491b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2492b5c9641dSDavid Ahern 
2493b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
249451602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
24951da177e4SLinus Torvalds }
24961da177e4SLinus Torvalds 
249768dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
249868dd299bSPavel Emelyanov 	{
249968dd299bSPavel Emelyanov 		.procname	= "ip_forward",
250068dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
250102291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
250268dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
250368dd299bSPavel Emelyanov 		.mode		= 0644,
250468dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
250568dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2506c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
250768dd299bSPavel Emelyanov 	},
250868dd299bSPavel Emelyanov 	{ },
250968dd299bSPavel Emelyanov };
25102a75de0cSEric Dumazet #endif
251168dd299bSPavel Emelyanov 
2512752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2513752d14dcSPavel Emelyanov {
2514752d14dcSPavel Emelyanov 	int err;
2515752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
25162a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
25172a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2518752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
25192a75de0cSEric Dumazet #endif
2520752d14dcSPavel Emelyanov 
2521752d14dcSPavel Emelyanov 	err = -ENOMEM;
2522752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2523752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2524752d14dcSPavel Emelyanov 
252509ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2526752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
252751456b29SIan Morris 		if (!all)
2528752d14dcSPavel Emelyanov 			goto err_alloc_all;
2529752d14dcSPavel Emelyanov 
2530752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
253151456b29SIan Morris 		if (!dflt)
2532752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2533752d14dcSPavel Emelyanov 
25342a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2535752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
253651456b29SIan Morris 		if (!tbl)
2537752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2538752d14dcSPavel Emelyanov 
253902291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2540752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2541752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
25422a75de0cSEric Dumazet #endif
2543752d14dcSPavel Emelyanov 	}
2544752d14dcSPavel Emelyanov 
2545752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
254629c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2547752d14dcSPavel Emelyanov 	if (err < 0)
2548752d14dcSPavel Emelyanov 		goto err_reg_all;
2549752d14dcSPavel Emelyanov 
255029c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
255129c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2552752d14dcSPavel Emelyanov 	if (err < 0)
2553752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2554752d14dcSPavel Emelyanov 
2555752d14dcSPavel Emelyanov 	err = -ENOMEM;
25568607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
255751456b29SIan Morris 	if (!forw_hdr)
2558752d14dcSPavel Emelyanov 		goto err_reg_ctl;
25592a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2560752d14dcSPavel Emelyanov #endif
2561752d14dcSPavel Emelyanov 
2562752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2563752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2564752d14dcSPavel Emelyanov 	return 0;
2565752d14dcSPavel Emelyanov 
2566752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2567752d14dcSPavel Emelyanov err_reg_ctl:
2568b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2569752d14dcSPavel Emelyanov err_reg_dflt:
2570b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2571752d14dcSPavel Emelyanov err_reg_all:
2572752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2573752d14dcSPavel Emelyanov 		kfree(tbl);
2574752d14dcSPavel Emelyanov err_alloc_ctl:
25752a75de0cSEric Dumazet #endif
2576752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2577752d14dcSPavel Emelyanov 		kfree(dflt);
2578752d14dcSPavel Emelyanov err_alloc_dflt:
2579752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2580752d14dcSPavel Emelyanov 		kfree(all);
2581752d14dcSPavel Emelyanov err_alloc_all:
2582752d14dcSPavel Emelyanov 	return err;
2583752d14dcSPavel Emelyanov }
2584752d14dcSPavel Emelyanov 
2585752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2586752d14dcSPavel Emelyanov {
25872a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2588752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2589752d14dcSPavel Emelyanov 
2590752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2591752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2592b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2593b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2594b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2595b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2596752d14dcSPavel Emelyanov 	kfree(tbl);
25972a75de0cSEric Dumazet #endif
2598752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2599752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2600752d14dcSPavel Emelyanov }
2601752d14dcSPavel Emelyanov 
2602752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2603752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2604752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2605752d14dcSPavel Emelyanov };
2606752d14dcSPavel Emelyanov 
2607207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
26089f0f7272SThomas Graf 	.family		  = AF_INET,
26099f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
26109f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2611cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2612cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
26139f0f7272SThomas Graf };
26149f0f7272SThomas Graf 
26151da177e4SLinus Torvalds void __init devinet_init(void)
26161da177e4SLinus Torvalds {
2617fd23c3b3SDavid S. Miller 	int i;
2618fd23c3b3SDavid S. Miller 
2619fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2620fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2621fd23c3b3SDavid S. Miller 
2622752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2623752d14dcSPavel Emelyanov 
26241da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
26251da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
262663f3444fSThomas Graf 
2627906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
26285c766d64SJiri Pirko 
26299f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
26309f0f7272SThomas Graf 
2631b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2632b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2633b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
26349e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2635b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
26361da177e4SLinus Torvalds }
2637