xref: /openbmc/linux/net/ipv4/devinet.c (revision c04438f5)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	NET3	IP device support routines.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
602c30a84SJesper Juhl  * 		Authors:	Ross Biro
71da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
81da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *	Additional Authors:
111da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
121da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Changes:
151da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
161da177e4SLinus Torvalds  *					lists.
171da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
181da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
191da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
201da177e4SLinus Torvalds  *					fall back to comparing just the label
211da177e4SLinus Torvalds  *					if no match found.
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
261da177e4SLinus Torvalds #include <linux/bitops.h>
274fc268d2SRandy Dunlap #include <linux/capability.h>
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds #include <linux/types.h>
301da177e4SLinus Torvalds #include <linux/kernel.h>
31174cd4b1SIngo Molnar #include <linux/sched/signal.h>
321da177e4SLinus Torvalds #include <linux/string.h>
331da177e4SLinus Torvalds #include <linux/mm.h>
341da177e4SLinus Torvalds #include <linux/socket.h>
351da177e4SLinus Torvalds #include <linux/sockios.h>
361da177e4SLinus Torvalds #include <linux/in.h>
371da177e4SLinus Torvalds #include <linux/errno.h>
381da177e4SLinus Torvalds #include <linux/interrupt.h>
391823730fSThomas Graf #include <linux/if_addr.h>
401da177e4SLinus Torvalds #include <linux/if_ether.h>
411da177e4SLinus Torvalds #include <linux/inet.h>
421da177e4SLinus Torvalds #include <linux/netdevice.h>
431da177e4SLinus Torvalds #include <linux/etherdevice.h>
441da177e4SLinus Torvalds #include <linux/skbuff.h>
451da177e4SLinus Torvalds #include <linux/init.h>
461da177e4SLinus Torvalds #include <linux/notifier.h>
471da177e4SLinus Torvalds #include <linux/inetdevice.h>
481da177e4SLinus Torvalds #include <linux/igmp.h>
495a0e3ad6STejun Heo #include <linux/slab.h>
50fd23c3b3SDavid S. Miller #include <linux/hash.h>
511da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
521da177e4SLinus Torvalds #include <linux/sysctl.h>
531da177e4SLinus Torvalds #endif
541da177e4SLinus Torvalds #include <linux/kmod.h>
55edc9e748SNicolas Dichtel #include <linux/netconf.h>
561da177e4SLinus Torvalds 
5714c85021SArnaldo Carvalho de Melo #include <net/arp.h>
581da177e4SLinus Torvalds #include <net/ip.h>
591da177e4SLinus Torvalds #include <net/route.h>
601da177e4SLinus Torvalds #include <net/ip_fib.h>
6163f3444fSThomas Graf #include <net/rtnetlink.h>
62752d14dcSPavel Emelyanov #include <net/net_namespace.h>
635c766d64SJiri Pirko #include <net/addrconf.h>
641da177e4SLinus Torvalds 
652e605463SMatteo Croce #define IPV6ONLY_FLAGS	\
662e605463SMatteo Croce 		(IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \
672e605463SMatteo Croce 		 IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \
682e605463SMatteo Croce 		 IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY)
692e605463SMatteo Croce 
700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7142f811b8SHerbert Xu 	.data = {
7202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
772690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
78fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
7942f811b8SHerbert Xu 	},
801da177e4SLinus Torvalds };
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8342f811b8SHerbert Xu 	.data = {
8402291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8802291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
902690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
91fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
9242f811b8SHerbert Xu 	},
931da177e4SLinus Torvalds };
941da177e4SLinus Torvalds 
959355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
969355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9742f811b8SHerbert Xu 
98ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
995c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
1005c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
1015c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1025176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1035c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
104ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
105af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
106d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
1075c753978SThomas Graf };
1085c753978SThomas Graf 
109978a46faSChristian Brauner struct inet_fill_args {
110978a46faSChristian Brauner 	u32 portid;
111978a46faSChristian Brauner 	u32 seq;
112978a46faSChristian Brauner 	int event;
113978a46faSChristian Brauner 	unsigned int flags;
114978a46faSChristian Brauner 	int netnsid;
1155fcd266aSDavid Ahern 	int ifindex;
116978a46faSChristian Brauner };
117978a46faSChristian Brauner 
11840384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
11940384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
12040384999SEric Dumazet 
121fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
122fd23c3b3SDavid S. Miller 
1236eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
124fd23c3b3SDavid S. Miller {
12540384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
126fd23c3b3SDavid S. Miller 
12740384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
128fd23c3b3SDavid S. Miller }
129fd23c3b3SDavid S. Miller 
130fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
131fd23c3b3SDavid S. Miller {
13240384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
133fd23c3b3SDavid S. Miller 
13432a4be48SWANG Cong 	ASSERT_RTNL();
135fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
136fd23c3b3SDavid S. Miller }
137fd23c3b3SDavid S. Miller 
138fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
139fd23c3b3SDavid S. Miller {
14032a4be48SWANG Cong 	ASSERT_RTNL();
141fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
142fd23c3b3SDavid S. Miller }
143fd23c3b3SDavid S. Miller 
1449435eb1cSDavid S. Miller /**
1459435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1469435eb1cSDavid S. Miller  * @net: the net namespace
1479435eb1cSDavid S. Miller  * @addr: the source address
1489435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1499435eb1cSDavid S. Miller  *
1509435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1519435eb1cSDavid S. Miller  */
1529435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1539435eb1cSDavid S. Miller {
1549435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1559435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1569435eb1cSDavid S. Miller 
1579435eb1cSDavid S. Miller 	rcu_read_lock();
1586e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1596e617de8SPaolo Abeni 	if (!ifa) {
160406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
161406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
162406b6f97SDavid S. Miller 		struct fib_table *local;
163406b6f97SDavid S. Miller 
164406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
165406b6f97SDavid S. Miller 		 * over loopback subnets work.
166406b6f97SDavid S. Miller 		 */
167406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
168406b6f97SDavid S. Miller 		if (local &&
169406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
170406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
171406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1726e617de8SPaolo Abeni 	} else {
1736e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
174406b6f97SDavid S. Miller 	}
1759435eb1cSDavid S. Miller 	if (result && devref)
1769435eb1cSDavid S. Miller 		dev_hold(result);
1779435eb1cSDavid S. Miller 	rcu_read_unlock();
1789435eb1cSDavid S. Miller 	return result;
1799435eb1cSDavid S. Miller }
1809435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1819435eb1cSDavid S. Miller 
1826e617de8SPaolo Abeni /* called under RCU lock */
1836e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1846e617de8SPaolo Abeni {
1856e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1866e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1876e617de8SPaolo Abeni 
1886e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1896e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1906e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1916e617de8SPaolo Abeni 			return ifa;
1926e617de8SPaolo Abeni 
1936e617de8SPaolo Abeni 	return NULL;
1946e617de8SPaolo Abeni }
1956e617de8SPaolo Abeni 
196d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1971da177e4SLinus Torvalds 
198e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1993ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
2002638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
2012638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
2021da177e4SLinus Torvalds 			 int destroy);
2031da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20420e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20551602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20651602b2aSPavel Emelyanov #else
20720e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20851602b2aSPavel Emelyanov {
20920e61da7SWANG Cong 	return 0;
21051602b2aSPavel Emelyanov }
21140384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
21251602b2aSPavel Emelyanov {
21351602b2aSPavel Emelyanov }
2141da177e4SLinus Torvalds #endif
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds /* Locks all the inet devices. */
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2191da177e4SLinus Torvalds {
2206126891cSVasily Averin 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT);
2211da177e4SLinus Torvalds }
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2241da177e4SLinus Torvalds {
2251da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2261da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2271da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2281da177e4SLinus Torvalds 	kfree(ifa);
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
23140384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2321da177e4SLinus Torvalds {
2331da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2371da177e4SLinus Torvalds {
2381da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2391da177e4SLinus Torvalds 
240547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
241547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
242e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2431da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
24491df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2451da177e4SLinus Torvalds #endif
246*c04438f5SEric Dumazet 	dev_put_track(dev, &idev->dev_tracker);
2471da177e4SLinus Torvalds 	if (!idev->dead)
2489f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2499f9354b9SEric Dumazet 	else
2501da177e4SLinus Torvalds 		kfree(idev);
2511da177e4SLinus Torvalds }
2529f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2531da177e4SLinus Torvalds 
25471e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2551da177e4SLinus Torvalds {
2561da177e4SLinus Torvalds 	struct in_device *in_dev;
25720e61da7SWANG Cong 	int err = -ENOMEM;
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	ASSERT_RTNL();
2601da177e4SLinus Torvalds 
2610da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2621da177e4SLinus Torvalds 	if (!in_dev)
2631da177e4SLinus Torvalds 		goto out;
264c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2659355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2661da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2671da177e4SLinus Torvalds 	in_dev->dev = dev;
2689f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2699f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2701da177e4SLinus Torvalds 		goto out_kfree;
2710187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2720187bdfbSBen Hutchings 		dev_disable_lro(dev);
2731da177e4SLinus Torvalds 	/* Reference in_dev->dev */
274*c04438f5SEric Dumazet 	dev_hold_track(dev, &in_dev->dev_tracker, GFP_KERNEL);
27530c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2767658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2771da177e4SLinus Torvalds 
27820e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
27920e61da7SWANG Cong 	if (err) {
28020e61da7SWANG Cong 		in_dev->dead = 1;
2811b49cd71SYang Yingliang 		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
28220e61da7SWANG Cong 		in_dev_put(in_dev);
28320e61da7SWANG Cong 		in_dev = NULL;
28420e61da7SWANG Cong 		goto out;
28520e61da7SWANG Cong 	}
2861da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2871da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2881da177e4SLinus Torvalds 		ip_mc_up(in_dev);
289483479ecSJarek Poplawski 
29030c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
291cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
292483479ecSJarek Poplawski out:
29320e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2941da177e4SLinus Torvalds out_kfree:
2951da177e4SLinus Torvalds 	kfree(in_dev);
2961da177e4SLinus Torvalds 	in_dev = NULL;
2971da177e4SLinus Torvalds 	goto out;
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
3031da177e4SLinus Torvalds 	in_dev_put(idev);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3071da177e4SLinus Torvalds {
3081da177e4SLinus Torvalds 	struct net_device *dev;
3092638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	ASSERT_RTNL();
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	dev = in_dev->dev;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	in_dev->dead = 1;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3181da177e4SLinus Torvalds 
3192638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3201da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3211da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3221da177e4SLinus Torvalds 	}
3231da177e4SLinus Torvalds 
324a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3251da177e4SLinus Torvalds 
32651602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3271da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3281da177e4SLinus Torvalds 	arp_ifdown(dev);
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
333ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3341da177e4SLinus Torvalds {
335d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
336d519e870SFlorian Westphal 
3371da177e4SLinus Torvalds 	rcu_read_lock();
338d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3391da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3401da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3411da177e4SLinus Torvalds 				rcu_read_unlock();
3421da177e4SLinus Torvalds 				return 1;
3431da177e4SLinus Torvalds 			}
3441da177e4SLinus Torvalds 		}
345d519e870SFlorian Westphal 	}
3461da177e4SLinus Torvalds 	rcu_read_unlock();
3471da177e4SLinus Torvalds 	return 0;
3481da177e4SLinus Torvalds }
3491da177e4SLinus Torvalds 
3502638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3512638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
35215e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3531da177e4SLinus Torvalds {
3548f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3552638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
3562638eb8bSFlorian Westphal 	struct in_ifaddr *last_prim;
3570ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3580ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds 	ASSERT_RTNL();
3611da177e4SLinus Torvalds 
3622638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
3632638eb8bSFlorian Westphal 	last_prim = rtnl_dereference(in_dev->ifa_list);
364fbd40ea0SDavid S. Miller 	if (in_dev->dead)
365fbd40ea0SDavid S. Miller 		goto no_promotions;
366fbd40ea0SDavid S. Miller 
3678f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3688f937c60SHarald Welte 	 * unless alias promotion is set
3698f937c60SHarald Welte 	 **/
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3722638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3731da177e4SLinus Torvalds 
3742638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3750ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3760ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3770ff60a45SJamal Hadi Salim 				last_prim = ifa;
3780ff60a45SJamal Hadi Salim 
3791da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3801da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3811da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3821da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3830ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3841da177e4SLinus Torvalds 				continue;
3851da177e4SLinus Torvalds 			}
3861da177e4SLinus Torvalds 
3870ff60a45SJamal Hadi Salim 			if (!do_promote) {
388fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3891da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3901da177e4SLinus Torvalds 
39115e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
392e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
393e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3941da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3958f937c60SHarald Welte 			} else {
3968f937c60SHarald Welte 				promote = ifa;
3978f937c60SHarald Welte 				break;
3988f937c60SHarald Welte 			}
3991da177e4SLinus Torvalds 		}
4001da177e4SLinus Torvalds 	}
4011da177e4SLinus Torvalds 
4022d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4032d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4042d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4052d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4062d230e2bSJulian Anastasov 	 */
4072638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4082d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4092d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4102d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4112d230e2bSJulian Anastasov 	}
4122d230e2bSJulian Anastasov 
413fbd40ea0SDavid S. Miller no_promotions:
4141da177e4SLinus Torvalds 	/* 2. Unlink it */
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
417fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4221da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4231da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4241da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4251da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4261da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4271da177e4SLinus Torvalds 	   So that, this order is correct.
4281da177e4SLinus Torvalds 	 */
42915e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
430e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4310ff60a45SJamal Hadi Salim 
4320ff60a45SJamal Hadi Salim 	if (promote) {
4332638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4340ff60a45SJamal Hadi Salim 
4352638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4360ff60a45SJamal Hadi Salim 		if (prev_prom) {
4372638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4382638eb8bSFlorian Westphal 
4392638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4406a9e9ceaSFlorian Westphal 
4416a9e9ceaSFlorian Westphal 			last_sec = rtnl_dereference(last_prim->ifa_next);
4422638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
4432638eb8bSFlorian Westphal 			rcu_assign_pointer(last_prim->ifa_next, promote);
4440ff60a45SJamal Hadi Salim 		}
4450ff60a45SJamal Hadi Salim 
4460ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
44715e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
448e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
449e041c683SAlan Stern 				NETDEV_UP, promote);
4502638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4512638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4520ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4530ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4540ff60a45SJamal Hadi Salim 					continue;
4550ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4560ff60a45SJamal Hadi Salim 		}
4570ff60a45SJamal Hadi Salim 
4580ff60a45SJamal Hadi Salim 	}
4596363097cSHerbert Xu 	if (destroy)
4601da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds 
4632638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4642638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
465d6062cbbSThomas Graf 			 int destroy)
466d6062cbbSThomas Graf {
467d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
468d6062cbbSThomas Graf }
469d6062cbbSThomas Graf 
4705c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4715c766d64SJiri Pirko 
4725c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4735c766d64SJiri Pirko 
474d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
475de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4761da177e4SLinus Torvalds {
4772638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4781da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4793ad7d246SKrister Johansen 	struct in_validator_info ivi;
4802638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4813ad7d246SKrister Johansen 	int ret;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	ASSERT_RTNL();
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4861da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4871da177e4SLinus Torvalds 		return 0;
4881da177e4SLinus Torvalds 	}
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4911da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4921da177e4SLinus Torvalds 
4932e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
4942e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
4952e605463SMatteo Croce 
4962638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
4972638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
4982638eb8bSFlorian Westphal 
4992638eb8bSFlorian Westphal 	while (ifa1) {
5001da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
5011da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5021da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5031da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5041da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5051da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5061da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5071da177e4SLinus Torvalds 				return -EEXIST;
5081da177e4SLinus Torvalds 			}
5091da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
5101da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5111da177e4SLinus Torvalds 				return -EINVAL;
5121da177e4SLinus Torvalds 			}
5131da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5141da177e4SLinus Torvalds 		}
5152638eb8bSFlorian Westphal 
5162638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5172638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5181da177e4SLinus Torvalds 	}
5191da177e4SLinus Torvalds 
5203ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5213ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5223ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5233ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5243ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5253ad7d246SKrister Johansen 	 * not the flags.
5263ad7d246SKrister Johansen 	 */
5273ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5283ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
529de95e047SDavid Ahern 	ivi.extack = extack;
5303ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5313ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5323ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5333ad7d246SKrister Johansen 	if (ret) {
5343ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5353ad7d246SKrister Johansen 		return ret;
5363ad7d246SKrister Johansen 	}
5373ad7d246SKrister Johansen 
5381da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
53963862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
5401da177e4SLinus Torvalds 		ifap = last_primary;
5411da177e4SLinus Torvalds 	}
5421da177e4SLinus Torvalds 
5432638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5442638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5451da177e4SLinus Torvalds 
546fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
547fd23c3b3SDavid S. Miller 
5485c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
549906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5505c766d64SJiri Pirko 
5511da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5521da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5531da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
55415e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
555e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 	return 0;
5581da177e4SLinus Torvalds }
5591da177e4SLinus Torvalds 
560d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
561d6062cbbSThomas Graf {
562de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
563d6062cbbSThomas Graf }
564d6062cbbSThomas Graf 
5651da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5661da177e4SLinus Torvalds {
567e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	ASSERT_RTNL();
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	if (!in_dev) {
5721da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5731da177e4SLinus Torvalds 		return -ENOBUFS;
5741da177e4SLinus Torvalds 	}
57571e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5761d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5771da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
578547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5791da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5801da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5811da177e4SLinus Torvalds 	}
582f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5831da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5841da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5851da177e4SLinus Torvalds }
5861da177e4SLinus Torvalds 
5878723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5888723e1b4SEric Dumazet  * We dont take a reference on found in_device
5898723e1b4SEric Dumazet  */
5907fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5911da177e4SLinus Torvalds {
5921da177e4SLinus Torvalds 	struct net_device *dev;
5931da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
594c148fc2eSEric Dumazet 
595c148fc2eSEric Dumazet 	rcu_read_lock();
596c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5971da177e4SLinus Torvalds 	if (dev)
5988723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
599c148fc2eSEric Dumazet 	rcu_read_unlock();
6001da177e4SLinus Torvalds 	return in_dev;
6011da177e4SLinus Torvalds }
6029f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6051da177e4SLinus Torvalds 
60660cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
60760cad5daSAl Viro 				    __be32 mask)
6081da177e4SLinus Torvalds {
609d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
610d519e870SFlorian Westphal 
6111da177e4SLinus Torvalds 	ASSERT_RTNL();
6121da177e4SLinus Torvalds 
613d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6141da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6151da177e4SLinus Torvalds 			return ifa;
616d519e870SFlorian Westphal 	}
6171da177e4SLinus Torvalds 	return NULL;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
620690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
621690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
62293a714d6SMadhu Challa {
623690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
62493a714d6SMadhu Challa 	struct ip_mreqn mreq = {
62593a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
62693a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
62793a714d6SMadhu Challa 	};
628690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
62993a714d6SMadhu Challa 	int ret;
63093a714d6SMadhu Challa 
63193a714d6SMadhu Challa 	ASSERT_RTNL();
63293a714d6SMadhu Challa 
63393a714d6SMadhu Challa 	lock_sock(sk);
63493a714d6SMadhu Challa 	if (join)
63554ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
63693a714d6SMadhu Challa 	else
63754ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
63893a714d6SMadhu Challa 	release_sock(sk);
63993a714d6SMadhu Challa 
64093a714d6SMadhu Challa 	return ret;
641690cc863STaras Chornyi #else
642690cc863STaras Chornyi 	return -EOPNOTSUPP;
643690cc863STaras Chornyi #endif
64493a714d6SMadhu Challa }
64593a714d6SMadhu Challa 
646c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
647c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6481da177e4SLinus Torvalds {
6493b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6502638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
651dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6521da177e4SLinus Torvalds 	struct in_device *in_dev;
653dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6542638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
65530e2379eSMenglong Dong 	int err;
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	ASSERT_RTNL();
6581da177e4SLinus Torvalds 
6598cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6608cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
661dfdd5fd4SThomas Graf 	if (err < 0)
662dfdd5fd4SThomas Graf 		goto errout;
663dfdd5fd4SThomas Graf 
664dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6657fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
66651456b29SIan Morris 	if (!in_dev) {
667dfdd5fd4SThomas Graf 		err = -ENODEV;
668dfdd5fd4SThomas Graf 		goto errout;
669dfdd5fd4SThomas Graf 	}
670dfdd5fd4SThomas Graf 
6712638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6721da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
673dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
67467b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6751da177e4SLinus Torvalds 			continue;
676dfdd5fd4SThomas Graf 
677dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
678dfdd5fd4SThomas Graf 			continue;
679dfdd5fd4SThomas Graf 
680dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
681dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
68267b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
683dfdd5fd4SThomas Graf 			continue;
684dfdd5fd4SThomas Graf 
68593a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
686690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
68715e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6881da177e4SLinus Torvalds 		return 0;
6891da177e4SLinus Torvalds 	}
690dfdd5fd4SThomas Graf 
691dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
692dfdd5fd4SThomas Graf errout:
693dfdd5fd4SThomas Graf 	return err;
6941da177e4SLinus Torvalds }
6951da177e4SLinus Torvalds 
6965c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6975c766d64SJiri Pirko 
6985c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6995c766d64SJiri Pirko {
7005c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
7015c766d64SJiri Pirko 	struct in_ifaddr *ifa;
702c988d1e8SJiri Pirko 	struct hlist_node *n;
7035c766d64SJiri Pirko 	int i;
7045c766d64SJiri Pirko 
7055c766d64SJiri Pirko 	now = jiffies;
7065c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
7075c766d64SJiri Pirko 
7085c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
709c988d1e8SJiri Pirko 		bool change_needed = false;
710c988d1e8SJiri Pirko 
711c988d1e8SJiri Pirko 		rcu_read_lock();
712b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
7135c766d64SJiri Pirko 			unsigned long age;
7145c766d64SJiri Pirko 
7155c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
7165c766d64SJiri Pirko 				continue;
7175c766d64SJiri Pirko 
7185c766d64SJiri Pirko 			/* We try to batch several events at once. */
7195c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
7205c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7215c766d64SJiri Pirko 
7225c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
7235c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
724c988d1e8SJiri Pirko 				change_needed = true;
725c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
726c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
727c988d1e8SJiri Pirko 				continue;
728c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
729c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
730c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
731c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
732c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
733c988d1e8SJiri Pirko 
734c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
735c988d1e8SJiri Pirko 					change_needed = true;
736c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
737c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
738c988d1e8SJiri Pirko 					       next)) {
739c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
740c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
741c988d1e8SJiri Pirko 			}
742c988d1e8SJiri Pirko 		}
743c988d1e8SJiri Pirko 		rcu_read_unlock();
744c988d1e8SJiri Pirko 		if (!change_needed)
745c988d1e8SJiri Pirko 			continue;
746c988d1e8SJiri Pirko 		rtnl_lock();
747c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
748c988d1e8SJiri Pirko 			unsigned long age;
749c988d1e8SJiri Pirko 
750c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
751c988d1e8SJiri Pirko 				continue;
752c988d1e8SJiri Pirko 
753c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
754c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
755c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
756c988d1e8SJiri Pirko 
757c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
758c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7592638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7602638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7615c766d64SJiri Pirko 
7622638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7632638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7642638eb8bSFlorian Westphal 				while (tmp) {
76540008e92SFlorian Westphal 					if (tmp == ifa) {
7665c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7675c766d64SJiri Pirko 							     ifap, 1);
768c988d1e8SJiri Pirko 						break;
7695c766d64SJiri Pirko 					}
7702638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7712638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
772c988d1e8SJiri Pirko 				}
773c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
774c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
775c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
776c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7775c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7785c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7795c766d64SJiri Pirko 			}
7805c766d64SJiri Pirko 		}
781c988d1e8SJiri Pirko 		rtnl_unlock();
7825c766d64SJiri Pirko 	}
7835c766d64SJiri Pirko 
7845c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7855c766d64SJiri Pirko 	next_sched = next;
7865c766d64SJiri Pirko 
7875c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7885c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7895c766d64SJiri Pirko 		next_sched = next_sec;
7905c766d64SJiri Pirko 
7915c766d64SJiri Pirko 	now = jiffies;
7925c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7935c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7945c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7955c766d64SJiri Pirko 
796906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
797906e073fSviresh kumar 			next_sched - now);
7985c766d64SJiri Pirko }
7995c766d64SJiri Pirko 
8005c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
8015c766d64SJiri Pirko 			     __u32 prefered_lft)
8025c766d64SJiri Pirko {
8035c766d64SJiri Pirko 	unsigned long timeout;
8045c766d64SJiri Pirko 
8055c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
8065c766d64SJiri Pirko 
8075c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
8085c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
8095c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
8105c766d64SJiri Pirko 	else
8115c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
8125c766d64SJiri Pirko 
8135c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8145c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8155c766d64SJiri Pirko 		if (timeout == 0)
8165c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
8175c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
8185c766d64SJiri Pirko 	}
8195c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
8205c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
8215c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
8225c766d64SJiri Pirko }
8235c766d64SJiri Pirko 
8245c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
825dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
826dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8271da177e4SLinus Torvalds {
8285c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8295c753978SThomas Graf 	struct in_ifaddr *ifa;
8305c753978SThomas Graf 	struct ifaddrmsg *ifm;
8311da177e4SLinus Torvalds 	struct net_device *dev;
8321da177e4SLinus Torvalds 	struct in_device *in_dev;
8337b218574SDenis V. Lunev 	int err;
8341da177e4SLinus Torvalds 
8358cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8368cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8375c753978SThomas Graf 	if (err < 0)
8385c753978SThomas Graf 		goto errout;
8391da177e4SLinus Torvalds 
8405c753978SThomas Graf 	ifm = nlmsg_data(nlh);
841c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
84251456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8435c753978SThomas Graf 		goto errout;
8441da177e4SLinus Torvalds 
8454b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8465c753978SThomas Graf 	err = -ENODEV;
84751456b29SIan Morris 	if (!dev)
8485c753978SThomas Graf 		goto errout;
8491da177e4SLinus Torvalds 
8505c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8515c753978SThomas Graf 	err = -ENOBUFS;
85251456b29SIan Morris 	if (!in_dev)
8535c753978SThomas Graf 		goto errout;
85471e27da9SHerbert Xu 
8555c753978SThomas Graf 	ifa = inet_alloc_ifa();
85651456b29SIan Morris 	if (!ifa)
8575c753978SThomas Graf 		/*
8585c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8595c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8605c753978SThomas Graf 		 */
8615c753978SThomas Graf 		goto errout;
8625c753978SThomas Graf 
863a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8641d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8655c753978SThomas Graf 	in_dev_hold(in_dev);
8665c753978SThomas Graf 
86751456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8685c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8695c753978SThomas Graf 
870fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8711da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8721da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
873ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
874ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8751da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8761da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8775c753978SThomas Graf 
87867b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
87967b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8805c753978SThomas Graf 
8815c753978SThomas Graf 	if (tb[IFA_BROADCAST])
88267b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8835c753978SThomas Graf 
8845c753978SThomas Graf 	if (tb[IFA_LABEL])
885872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8861da177e4SLinus Torvalds 	else
8871da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8881da177e4SLinus Torvalds 
889af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
890af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
891af4d768aSDavid Ahern 
8925c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8935c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8945c766d64SJiri Pirko 
8955c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8965c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8975c766d64SJiri Pirko 			err = -EINVAL;
898446266b0SDaniel Borkmann 			goto errout_free;
8995c766d64SJiri Pirko 		}
9005c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
9015c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9025c766d64SJiri Pirko 	}
9035c766d64SJiri Pirko 
9045c753978SThomas Graf 	return ifa;
9055c753978SThomas Graf 
906446266b0SDaniel Borkmann errout_free:
907446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9085c753978SThomas Graf errout:
9095c753978SThomas Graf 	return ERR_PTR(err);
9105c753978SThomas Graf }
9115c753978SThomas Graf 
9125c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9135c766d64SJiri Pirko {
9145c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
915ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9165c766d64SJiri Pirko 
9175c766d64SJiri Pirko 	if (!ifa->ifa_local)
9185c766d64SJiri Pirko 		return NULL;
9195c766d64SJiri Pirko 
920ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9215c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9225c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9235c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9245c766d64SJiri Pirko 			return ifa1;
9255c766d64SJiri Pirko 	}
9265c766d64SJiri Pirko 	return NULL;
9275c766d64SJiri Pirko }
9285c766d64SJiri Pirko 
929c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
930c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9315c753978SThomas Graf {
9323b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9335c753978SThomas Graf 	struct in_ifaddr *ifa;
9345c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9355c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9365c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9375c753978SThomas Graf 
9385c753978SThomas Graf 	ASSERT_RTNL();
9395c753978SThomas Graf 
940dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9415c753978SThomas Graf 	if (IS_ERR(ifa))
9425c753978SThomas Graf 		return PTR_ERR(ifa);
9435c753978SThomas Graf 
9445c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9455c766d64SJiri Pirko 	if (!ifa_existing) {
9465c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
947614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9485c766d64SJiri Pirko 		 */
9495c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
95093a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
951690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
95293a714d6SMadhu Challa 
95393a714d6SMadhu Challa 			if (ret < 0) {
95493a714d6SMadhu Challa 				inet_free_ifa(ifa);
95593a714d6SMadhu Challa 				return ret;
95693a714d6SMadhu Challa 			}
95793a714d6SMadhu Challa 		}
958de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
959de95e047SDavid Ahern 					 extack);
9605c766d64SJiri Pirko 	} else {
961af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
962af4d768aSDavid Ahern 
9635c766d64SJiri Pirko 		inet_free_ifa(ifa);
9645c766d64SJiri Pirko 
9655c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9665c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9675c766d64SJiri Pirko 			return -EEXIST;
96834e2ed34SJiri Pirko 		ifa = ifa_existing;
969af4d768aSDavid Ahern 
970af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
971af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
972af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
973af4d768aSDavid Ahern 		}
974af4d768aSDavid Ahern 
97534e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
97605a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
977906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
978906e073fSviresh kumar 				&check_lifetime_work, 0);
97934e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9805c766d64SJiri Pirko 	}
9815c766d64SJiri Pirko 	return 0;
9821da177e4SLinus Torvalds }
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds /*
9851da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9861da177e4SLinus Torvalds  */
9871da177e4SLinus Torvalds 
98840384999SEric Dumazet static int inet_abc_len(__be32 addr)
9891da177e4SLinus Torvalds {
9901da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9911da177e4SLinus Torvalds 
99265cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
9931da177e4SLinus Torvalds 		rc = 0;
9941da177e4SLinus Torvalds 	else {
995714e85beSAl Viro 		__u32 haddr = ntohl(addr);
996714e85beSAl Viro 		if (IN_CLASSA(haddr))
9971da177e4SLinus Torvalds 			rc = 8;
998714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9991da177e4SLinus Torvalds 			rc = 16;
1000714e85beSAl Viro 		else if (IN_CLASSC(haddr))
10011da177e4SLinus Torvalds 			rc = 24;
100265cab850SDave Taht 		else if (IN_CLASSE(haddr))
100365cab850SDave Taht 			rc = 32;
10041da177e4SLinus Torvalds 	}
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds 	return rc;
10071da177e4SLinus Torvalds }
10081da177e4SLinus Torvalds 
10091da177e4SLinus Torvalds 
101003aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10111da177e4SLinus Torvalds {
10121da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
101303aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10142638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10151da177e4SLinus Torvalds 	struct in_device *in_dev;
10161da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10171da177e4SLinus Torvalds 	struct net_device *dev;
10181da177e4SLinus Torvalds 	char *colon;
10191da177e4SLinus Torvalds 	int ret = -EFAULT;
10201da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10211da177e4SLinus Torvalds 
102203aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10231da177e4SLinus Torvalds 
10241da177e4SLinus Torvalds 	/* save original address for comparison */
10251da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10261da177e4SLinus Torvalds 
102703aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10281da177e4SLinus Torvalds 	if (colon)
10291da177e4SLinus Torvalds 		*colon = 0;
10301da177e4SLinus Torvalds 
103103aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 	switch (cmd) {
10341da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10351da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10361da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10371da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10381da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10391da177e4SLinus Torvalds 		   so that we do not impose a lock.
10401da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10411da177e4SLinus Torvalds 		 */
10421da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10431da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10441da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10451da177e4SLinus Torvalds 		break;
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1048bf5b30b8SZhao Hongjiang 		ret = -EPERM;
104952e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10501da177e4SLinus Torvalds 			goto out;
10511da177e4SLinus Torvalds 		break;
10521da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10531da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10541da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10551da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1056bf5b30b8SZhao Hongjiang 		ret = -EPERM;
105752e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10581da177e4SLinus Torvalds 			goto out;
10591da177e4SLinus Torvalds 		ret = -EINVAL;
10601da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10611da177e4SLinus Torvalds 			goto out;
10621da177e4SLinus Torvalds 		break;
10631da177e4SLinus Torvalds 	default:
10641da177e4SLinus Torvalds 		ret = -EINVAL;
10651da177e4SLinus Torvalds 		goto out;
10661da177e4SLinus Torvalds 	}
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds 	rtnl_lock();
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	ret = -ENODEV;
107103aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10729f9354b9SEric Dumazet 	if (!dev)
10731da177e4SLinus Torvalds 		goto done;
10741da177e4SLinus Torvalds 
10751da177e4SLinus Torvalds 	if (colon)
10761da177e4SLinus Torvalds 		*colon = ':';
10771da177e4SLinus Torvalds 
10789f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10799f9354b9SEric Dumazet 	if (in_dev) {
10801da177e4SLinus Torvalds 		if (tryaddrmatch) {
10811da177e4SLinus Torvalds 			/* Matthias Andree */
10821da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10831da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10841da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10851da177e4SLinus Torvalds 			   This is checked above. */
10862638eb8bSFlorian Westphal 
10872638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
10882638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
10891da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
109003aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10911da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10926c91afe1SDavid S. Miller 							ifa->ifa_local) {
10931da177e4SLinus Torvalds 					break; /* found */
10941da177e4SLinus Torvalds 				}
10951da177e4SLinus Torvalds 			}
10961da177e4SLinus Torvalds 		}
10971da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10981da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10991da177e4SLinus Torvalds 		   comparing just the label */
11001da177e4SLinus Torvalds 		if (!ifa) {
11012638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11022638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11031da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
110403aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11051da177e4SLinus Torvalds 					break;
11061da177e4SLinus Torvalds 		}
11071da177e4SLinus Torvalds 	}
11081da177e4SLinus Torvalds 
11091da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11101da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11111da177e4SLinus Torvalds 		goto done;
11121da177e4SLinus Torvalds 
11131da177e4SLinus Torvalds 	switch (cmd) {
11141da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
111530e948a3STonghao Zhang 		ret = 0;
11161da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
111703aef17bSAl Viro 		break;
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
112030e948a3STonghao Zhang 		ret = 0;
11211da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
112203aef17bSAl Viro 		break;
11231da177e4SLinus Torvalds 
11241da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
112530e948a3STonghao Zhang 		ret = 0;
11261da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
112703aef17bSAl Viro 		break;
11281da177e4SLinus Torvalds 
11291da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
113030e948a3STonghao Zhang 		ret = 0;
11311da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
113203aef17bSAl Viro 		break;
11331da177e4SLinus Torvalds 
11341da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11351da177e4SLinus Torvalds 		if (colon) {
11361da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11371da177e4SLinus Torvalds 			if (!ifa)
11381da177e4SLinus Torvalds 				break;
11391da177e4SLinus Torvalds 			ret = 0;
114003aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11411da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11421da177e4SLinus Torvalds 			break;
11431da177e4SLinus Torvalds 		}
1144567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11451da177e4SLinus Torvalds 		break;
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11481da177e4SLinus Torvalds 		ret = -EINVAL;
11491da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11501da177e4SLinus Torvalds 			break;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 		if (!ifa) {
11531da177e4SLinus Torvalds 			ret = -ENOBUFS;
11549f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11559f9354b9SEric Dumazet 			if (!ifa)
11561da177e4SLinus Torvalds 				break;
1157c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11581da177e4SLinus Torvalds 			if (colon)
115903aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11601da177e4SLinus Torvalds 			else
11611da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11621da177e4SLinus Torvalds 		} else {
11631da177e4SLinus Torvalds 			ret = 0;
11641da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11651da177e4SLinus Torvalds 				break;
11661da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11671da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1168148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11691da177e4SLinus Torvalds 		}
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11741da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11751da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11761da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11771da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11781da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11791da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11801da177e4SLinus Torvalds 		} else {
11811da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11821da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11831da177e4SLinus Torvalds 		}
11845c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11851da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11861da177e4SLinus Torvalds 		break;
11871da177e4SLinus Torvalds 
11881da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11891da177e4SLinus Torvalds 		ret = 0;
11901da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11911da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11921da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11931da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11941da177e4SLinus Torvalds 		}
11951da177e4SLinus Torvalds 		break;
11961da177e4SLinus Torvalds 
11971da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11981da177e4SLinus Torvalds 		ret = 0;
11991da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
12001da177e4SLinus Torvalds 			break;
12011da177e4SLinus Torvalds 		ret = -EINVAL;
12021da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12031da177e4SLinus Torvalds 			break;
12041da177e4SLinus Torvalds 		ret = 0;
12051da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12061da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12071da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12081da177e4SLinus Torvalds 		break;
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12111da177e4SLinus Torvalds 
12121da177e4SLinus Torvalds 		/*
12131da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12141da177e4SLinus Torvalds 		 */
12151da177e4SLinus Torvalds 		ret = -EINVAL;
12161da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12171da177e4SLinus Torvalds 			break;
12181da177e4SLinus Torvalds 		ret = 0;
12191da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1220a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12211da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12221da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12231da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12241da177e4SLinus Torvalds 
12251da177e4SLinus Torvalds 			/* See if current broadcast address matches
12261da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12271da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12281da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12291da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12301da177e4SLinus Torvalds 			 */
12311da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12321da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12331da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1234dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12351da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12361da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12371da177e4SLinus Torvalds 			}
12381da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12391da177e4SLinus Torvalds 		}
12401da177e4SLinus Torvalds 		break;
12411da177e4SLinus Torvalds 	}
12421da177e4SLinus Torvalds done:
12431da177e4SLinus Torvalds 	rtnl_unlock();
12441da177e4SLinus Torvalds out:
12451da177e4SLinus Torvalds 	return ret;
12461da177e4SLinus Torvalds }
12471da177e4SLinus Torvalds 
1248b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12491da177e4SLinus Torvalds {
1250e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1251ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12521da177e4SLinus Torvalds 	struct ifreq ifr;
12531da177e4SLinus Torvalds 	int done = 0;
12541da177e4SLinus Torvalds 
125536fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
125636fd633eSAl Viro 		goto out;
125736fd633eSAl Viro 
12589f9354b9SEric Dumazet 	if (!in_dev)
12591da177e4SLinus Torvalds 		goto out;
12601da177e4SLinus Torvalds 
1261ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12621da177e4SLinus Torvalds 		if (!buf) {
126336fd633eSAl Viro 			done += size;
12641da177e4SLinus Torvalds 			continue;
12651da177e4SLinus Torvalds 		}
126636fd633eSAl Viro 		if (len < size)
12671da177e4SLinus Torvalds 			break;
12681da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12691da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12701da177e4SLinus Torvalds 
12711da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12721da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12731da177e4SLinus Torvalds 								ifa->ifa_local;
12741da177e4SLinus Torvalds 
127536fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12761da177e4SLinus Torvalds 			done = -EFAULT;
12771da177e4SLinus Torvalds 			break;
12781da177e4SLinus Torvalds 		}
127936fd633eSAl Viro 		len  -= size;
128036fd633eSAl Viro 		done += size;
12811da177e4SLinus Torvalds 	}
12821da177e4SLinus Torvalds out:
12831da177e4SLinus Torvalds 	return done;
12841da177e4SLinus Torvalds }
12851da177e4SLinus Torvalds 
12868b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12878b57fd1eSGao Feng 				 int scope)
12888b57fd1eSGao Feng {
1289d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1290d519e870SFlorian Westphal 
1291d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1292d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1293d519e870SFlorian Westphal 			continue;
12948b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12958b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12968b57fd1eSGao Feng 			return ifa->ifa_local;
1297d519e870SFlorian Westphal 	}
12988b57fd1eSGao Feng 
12998b57fd1eSGao Feng 	return 0;
13008b57fd1eSGao Feng }
13018b57fd1eSGao Feng 
1302a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13031da177e4SLinus Torvalds {
1304d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1305a61ced5dSAl Viro 	__be32 addr = 0;
1306d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13071da177e4SLinus Torvalds 	struct in_device *in_dev;
1308c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13093f2fb9a8SDavid Ahern 	int master_idx;
13101da177e4SLinus Torvalds 
13111da177e4SLinus Torvalds 	rcu_read_lock();
1312e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13131da177e4SLinus Torvalds 	if (!in_dev)
13141da177e4SLinus Torvalds 		goto no_in_dev;
13151da177e4SLinus Torvalds 
1316d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1317d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1318d8c444d5SShijie Luo 
1319d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1320d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1321d519e870SFlorian Westphal 			continue;
1322d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13231da177e4SLinus Torvalds 			continue;
13241da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13251da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13261da177e4SLinus Torvalds 			break;
13271da177e4SLinus Torvalds 		}
13281da177e4SLinus Torvalds 		if (!addr)
13291da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1330d519e870SFlorian Westphal 	}
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 	if (addr)
1333c6d14c84SEric Dumazet 		goto out_unlock;
13349f9354b9SEric Dumazet no_in_dev:
13353f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13361da177e4SLinus Torvalds 
133717b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
133817b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
133917b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
134017b693cdSDavid Lamparter 	 * equality check in the loop below.
134117b693cdSDavid Lamparter 	 */
134217b693cdSDavid Lamparter 	if (master_idx &&
134317b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
134417b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13458b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13468b57fd1eSGao Feng 		if (addr)
134717b693cdSDavid Lamparter 			goto out_unlock;
134817b693cdSDavid Lamparter 	}
134917b693cdSDavid Lamparter 
13501da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1351ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13521da177e4SLinus Torvalds 	   in dev_base list.
13531da177e4SLinus Torvalds 	 */
1354c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13553f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13563f2fb9a8SDavid Ahern 			continue;
13573f2fb9a8SDavid Ahern 
13589f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13599f9354b9SEric Dumazet 		if (!in_dev)
13601da177e4SLinus Torvalds 			continue;
13611da177e4SLinus Torvalds 
13628b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13638b57fd1eSGao Feng 		if (addr)
1364c6d14c84SEric Dumazet 			goto out_unlock;
13651da177e4SLinus Torvalds 	}
1366c6d14c84SEric Dumazet out_unlock:
13671da177e4SLinus Torvalds 	rcu_read_unlock();
13681da177e4SLinus Torvalds 	return addr;
13691da177e4SLinus Torvalds }
13709f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13711da177e4SLinus Torvalds 
137260cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
137360cad5daSAl Viro 			      __be32 local, int scope)
13741da177e4SLinus Torvalds {
1375650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1376ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1377a144ea4bSAl Viro 	__be32 addr = 0;
1378ef11db33SFlorian Westphal 	int same = 0;
13791da177e4SLinus Torvalds 
1380650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1381650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1382650638a7SShijie Luo 
1383ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1384650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1385650638a7SShijie Luo 
13861da177e4SLinus Torvalds 		if (!addr &&
13871da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1388650638a7SShijie Luo 		    min_scope <= scope) {
13891da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13901da177e4SLinus Torvalds 			if (same)
13911da177e4SLinus Torvalds 				break;
13921da177e4SLinus Torvalds 		}
13931da177e4SLinus Torvalds 		if (!same) {
13941da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13951da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13961da177e4SLinus Torvalds 			if (same && addr) {
13971da177e4SLinus Torvalds 				if (local || !dst)
13981da177e4SLinus Torvalds 					break;
13991da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
14001da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
14011da177e4SLinus Torvalds 					break;
14021da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1403650638a7SShijie Luo 				if (min_scope <= scope) {
14041da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14051da177e4SLinus Torvalds 					break;
14061da177e4SLinus Torvalds 				}
14071da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14081da177e4SLinus Torvalds 				same = 0;
14091da177e4SLinus Torvalds 			}
14101da177e4SLinus Torvalds 		}
1411ef11db33SFlorian Westphal 	}
14121da177e4SLinus Torvalds 
14131da177e4SLinus Torvalds 	return same ? addr : 0;
14141da177e4SLinus Torvalds }
14151da177e4SLinus Torvalds 
14161da177e4SLinus Torvalds /*
14171da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1418b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1419b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14201da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14211da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14221da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14231da177e4SLinus Torvalds  */
1424b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14259bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14261da177e4SLinus Torvalds {
142760cad5daSAl Viro 	__be32 addr = 0;
14289bd85e32SDenis V. Lunev 	struct net_device *dev;
14291da177e4SLinus Torvalds 
143000db4124SIan Morris 	if (in_dev)
14319bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14321da177e4SLinus Torvalds 
14331da177e4SLinus Torvalds 	rcu_read_lock();
1434c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14359f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14369f9354b9SEric Dumazet 		if (in_dev) {
14371da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14381da177e4SLinus Torvalds 			if (addr)
14391da177e4SLinus Torvalds 				break;
14401da177e4SLinus Torvalds 		}
14411da177e4SLinus Torvalds 	}
14421da177e4SLinus Torvalds 	rcu_read_unlock();
14431da177e4SLinus Torvalds 
14441da177e4SLinus Torvalds 	return addr;
14451da177e4SLinus Torvalds }
1446eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14471da177e4SLinus Torvalds 
14481da177e4SLinus Torvalds /*
14491da177e4SLinus Torvalds  *	Device notifier
14501da177e4SLinus Torvalds  */
14511da177e4SLinus Torvalds 
14521da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14531da177e4SLinus Torvalds {
1454e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14551da177e4SLinus Torvalds }
14569f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14571da177e4SLinus Torvalds 
14581da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14591da177e4SLinus Torvalds {
1460e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14611da177e4SLinus Torvalds }
14629f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14631da177e4SLinus Torvalds 
14643ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14653ad7d246SKrister Johansen {
14663ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14673ad7d246SKrister Johansen }
14683ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14693ad7d246SKrister Johansen 
14703ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14713ad7d246SKrister Johansen {
14723ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14733ad7d246SKrister Johansen 	    nb);
14743ad7d246SKrister Johansen }
14753ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14763ad7d246SKrister Johansen 
14779f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14789f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14791da177e4SLinus Torvalds */
14801da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14811da177e4SLinus Torvalds {
14821da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14831da177e4SLinus Torvalds 	int named = 0;
14841da177e4SLinus Torvalds 
1485ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
14861da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14871da177e4SLinus Torvalds 
14881da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14891da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14901da177e4SLinus Torvalds 		if (named++ == 0)
1491573bf470SThomas Graf 			goto skip;
149244344b2aSMark McLoughlin 		dot = strchr(old, ':');
149351456b29SIan Morris 		if (!dot) {
14941da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14951da177e4SLinus Torvalds 			dot = old;
14961da177e4SLinus Torvalds 		}
14979f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14981da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14999f9354b9SEric Dumazet 		else
15001da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1501573bf470SThomas Graf skip:
1502573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15031da177e4SLinus Torvalds 	}
15041da177e4SLinus Torvalds }
15051da177e4SLinus Torvalds 
1506d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1507d11327adSIan Campbell 					struct in_device *in_dev)
1508d11327adSIan Campbell 
1509d11327adSIan Campbell {
1510ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1511d11327adSIan Campbell 
1512ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1513d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15146c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15156c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1516d11327adSIan Campbell 			 dev->dev_addr, NULL);
1517d11327adSIan Campbell 	}
1518b76d0789SZoltan Kiss }
1519d11327adSIan Campbell 
15201da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15211da177e4SLinus Torvalds 
15221da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15231da177e4SLinus Torvalds 			 void *ptr)
15241da177e4SLinus Torvalds {
1525351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1526748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15271da177e4SLinus Torvalds 
15281da177e4SLinus Torvalds 	ASSERT_RTNL();
15291da177e4SLinus Torvalds 
15301da177e4SLinus Torvalds 	if (!in_dev) {
15318030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15321da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
153320e61da7SWANG Cong 			if (IS_ERR(in_dev))
153420e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15350cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
153642f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
153742f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15381da177e4SLinus Torvalds 			}
153906770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
154006770843SBreno Leitao 			/* Re-enabling IP */
154106770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
154206770843SBreno Leitao 				in_dev = inetdev_init(dev);
15438030f544SHerbert Xu 		}
15441da177e4SLinus Torvalds 		goto out;
15451da177e4SLinus Torvalds 	}
15461da177e4SLinus Torvalds 
15471da177e4SLinus Torvalds 	switch (event) {
15481da177e4SLinus Torvalds 	case NETDEV_REGISTER:
154991df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1550a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15511da177e4SLinus Torvalds 		break;
15521da177e4SLinus Torvalds 	case NETDEV_UP:
155306770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15541da177e4SLinus Torvalds 			break;
15550cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15569f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15579f9354b9SEric Dumazet 
15589f9354b9SEric Dumazet 			if (ifa) {
1559fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15601da177e4SLinus Torvalds 				ifa->ifa_local =
15611da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15621da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15631da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15641da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15651da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15661da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15671da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15685c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15695c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1570dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1571dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15721da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15731da177e4SLinus Torvalds 			}
15741da177e4SLinus Torvalds 		}
15751da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1576a8eceea8SJoe Perches 		fallthrough;
1577eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1578d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1579d11327adSIan Campbell 			break;
1580a8eceea8SJoe Perches 		fallthrough;
1581d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1582a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1583d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15841da177e4SLinus Torvalds 		break;
15851da177e4SLinus Torvalds 	case NETDEV_DOWN:
15861da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15871da177e4SLinus Torvalds 		break;
158893d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
158975c78500SMoni Shoua 		ip_mc_unmap(in_dev);
159075c78500SMoni Shoua 		break;
159193d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
159275c78500SMoni Shoua 		ip_mc_remap(in_dev);
159375c78500SMoni Shoua 		break;
15941da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
159506770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15961da177e4SLinus Torvalds 			break;
159706770843SBreno Leitao 		/* disable IP when MTU is not enough */
1598a8eceea8SJoe Perches 		fallthrough;
15991da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
16001da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
16011da177e4SLinus Torvalds 		break;
16021da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16031da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16041da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16051da177e4SLinus Torvalds 		 */
16061da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16071da177e4SLinus Torvalds 
160851602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
160966f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16101da177e4SLinus Torvalds 		break;
16111da177e4SLinus Torvalds 	}
16121da177e4SLinus Torvalds out:
16131da177e4SLinus Torvalds 	return NOTIFY_DONE;
16141da177e4SLinus Torvalds }
16151da177e4SLinus Torvalds 
16161da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16171da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16181da177e4SLinus Torvalds };
16191da177e4SLinus Torvalds 
162040384999SEric Dumazet static size_t inet_nlmsg_size(void)
1621339bf98fSThomas Graf {
1622339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1623339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1624339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1625339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1626ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
162763b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1628af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
162963b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1630339bf98fSThomas Graf }
1631339bf98fSThomas Graf 
16325c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16335c766d64SJiri Pirko {
16345c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16355c766d64SJiri Pirko }
16365c766d64SJiri Pirko 
16375c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16385c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16395c766d64SJiri Pirko {
16405c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16415c766d64SJiri Pirko 
16425c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16435c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16445c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16455c766d64SJiri Pirko 	ci.ifa_valid = valid;
16465c766d64SJiri Pirko 
16475c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16485c766d64SJiri Pirko }
16495c766d64SJiri Pirko 
16501da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1651978a46faSChristian Brauner 			    struct inet_fill_args *args)
16521da177e4SLinus Torvalds {
16531da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16541da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16555c766d64SJiri Pirko 	u32 preferred, valid;
16561da177e4SLinus Torvalds 
1657978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1658978a46faSChristian Brauner 			args->flags);
165951456b29SIan Morris 	if (!nlh)
166026932566SPatrick McHardy 		return -EMSGSIZE;
166147f68512SThomas Graf 
166247f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16631da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16641da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16655c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16661da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16671da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16681da177e4SLinus Torvalds 
1669978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1670978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1671d3807145SChristian Brauner 		goto nla_put_failure;
1672d3807145SChristian Brauner 
16735c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16745c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16755c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16765c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16775c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16785c766d64SJiri Pirko 
16795c766d64SJiri Pirko 			if (preferred > tval)
16805c766d64SJiri Pirko 				preferred -= tval;
16815c766d64SJiri Pirko 			else
16825c766d64SJiri Pirko 				preferred = 0;
16835c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16845c766d64SJiri Pirko 				if (valid > tval)
16855c766d64SJiri Pirko 					valid -= tval;
16865c766d64SJiri Pirko 				else
16875c766d64SJiri Pirko 					valid = 0;
16885c766d64SJiri Pirko 			}
16895c766d64SJiri Pirko 		}
16905c766d64SJiri Pirko 	} else {
16915c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16925c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16935c766d64SJiri Pirko 	}
1694f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1695930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1696f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1697930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1698f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1699930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1700f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
17015c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1702ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1703af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1704af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
17055c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
17065c766d64SJiri Pirko 			  preferred, valid))
1707f3756b79SDavid S. Miller 		goto nla_put_failure;
170847f68512SThomas Graf 
1709053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1710053c095aSJohannes Berg 	return 0;
171147f68512SThomas Graf 
171247f68512SThomas Graf nla_put_failure:
171326932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
171426932566SPatrick McHardy 	return -EMSGSIZE;
17151da177e4SLinus Torvalds }
17161da177e4SLinus Torvalds 
1717c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1718c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1719c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17205fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1721c33078e3SDavid Ahern {
17225fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1723c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1724c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1725c33078e3SDavid Ahern 	int err, i;
1726c33078e3SDavid Ahern 
1727c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1728c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1729c33078e3SDavid Ahern 		return -EINVAL;
1730c33078e3SDavid Ahern 	}
1731c33078e3SDavid Ahern 
1732c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1733c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1734c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1735c33078e3SDavid Ahern 		return -EINVAL;
1736c33078e3SDavid Ahern 	}
17375fcd266aSDavid Ahern 
17385fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17395fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17405fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17415fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1742c33078e3SDavid Ahern 	}
1743c33078e3SDavid Ahern 
17448cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1745c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1746c33078e3SDavid Ahern 	if (err < 0)
1747c33078e3SDavid Ahern 		return err;
1748c33078e3SDavid Ahern 
1749c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1750c33078e3SDavid Ahern 		if (!tb[i])
1751c33078e3SDavid Ahern 			continue;
1752c33078e3SDavid Ahern 
1753c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1754c33078e3SDavid Ahern 			struct net *net;
1755c33078e3SDavid Ahern 
1756c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1757c33078e3SDavid Ahern 
1758c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1759c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1760bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1761c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1762c33078e3SDavid Ahern 				return PTR_ERR(net);
1763c33078e3SDavid Ahern 			}
1764c33078e3SDavid Ahern 			*tgt_net = net;
1765c33078e3SDavid Ahern 		} else {
1766c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1767c33078e3SDavid Ahern 			return -EINVAL;
1768c33078e3SDavid Ahern 		}
1769c33078e3SDavid Ahern 	}
1770c33078e3SDavid Ahern 
1771c33078e3SDavid Ahern 	return 0;
1772c33078e3SDavid Ahern }
1773c33078e3SDavid Ahern 
17741c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
17751c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
17761c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
17771c98eca4SDavid Ahern {
17781c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
17791c98eca4SDavid Ahern 	int ip_idx = 0;
17801c98eca4SDavid Ahern 	int err;
17811c98eca4SDavid Ahern 
1782d3e6e285SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1783ef11db33SFlorian Westphal 		if (ip_idx < s_ip_idx) {
1784ef11db33SFlorian Westphal 			ip_idx++;
17851c98eca4SDavid Ahern 			continue;
1786ef11db33SFlorian Westphal 		}
17871c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
17881c98eca4SDavid Ahern 		if (err < 0)
17891c98eca4SDavid Ahern 			goto done;
17901c98eca4SDavid Ahern 
17911c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1792ef11db33SFlorian Westphal 		ip_idx++;
17931c98eca4SDavid Ahern 	}
17941c98eca4SDavid Ahern 	err = 0;
17951c98eca4SDavid Ahern 
17961c98eca4SDavid Ahern done:
17971c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
17981c98eca4SDavid Ahern 
17991c98eca4SDavid Ahern 	return err;
18001c98eca4SDavid Ahern }
18011c98eca4SDavid Ahern 
18021da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18031da177e4SLinus Torvalds {
1804c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1805978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1806978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1807c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1808978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1809978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1810978a46faSChristian Brauner 		.netnsid = -1,
1811978a46faSChristian Brauner 	};
18123b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1813d3807145SChristian Brauner 	struct net *tgt_net = net;
1814eec4df98SEric Dumazet 	int h, s_h;
1815eec4df98SEric Dumazet 	int idx, s_idx;
18161c98eca4SDavid Ahern 	int s_ip_idx;
18171da177e4SLinus Torvalds 	struct net_device *dev;
18181da177e4SLinus Torvalds 	struct in_device *in_dev;
1819eec4df98SEric Dumazet 	struct hlist_head *head;
1820d7e38611SDavid Ahern 	int err = 0;
18211da177e4SLinus Torvalds 
1822eec4df98SEric Dumazet 	s_h = cb->args[0];
1823eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
18241c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1825eec4df98SEric Dumazet 
1826c33078e3SDavid Ahern 	if (cb->strict_check) {
1827c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18285fcd266aSDavid Ahern 						 skb->sk, cb);
1829c33078e3SDavid Ahern 		if (err < 0)
1830d7e38611SDavid Ahern 			goto put_tgt_net;
18315fcd266aSDavid Ahern 
1832d7e38611SDavid Ahern 		err = 0;
18335fcd266aSDavid Ahern 		if (fillargs.ifindex) {
18345fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1835d7e38611SDavid Ahern 			if (!dev) {
1836d7e38611SDavid Ahern 				err = -ENODEV;
1837d7e38611SDavid Ahern 				goto put_tgt_net;
1838d7e38611SDavid Ahern 			}
18395fcd266aSDavid Ahern 
18405fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
18415fcd266aSDavid Ahern 			if (in_dev) {
18425fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18435fcd266aSDavid Ahern 						       &fillargs);
18445fcd266aSDavid Ahern 			}
18455fcd266aSDavid Ahern 			goto put_tgt_net;
18465fcd266aSDavid Ahern 		}
1847d3807145SChristian Brauner 	}
1848d3807145SChristian Brauner 
1849eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18507562f876SPavel Emelianov 		idx = 0;
1851d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1852eec4df98SEric Dumazet 		rcu_read_lock();
1853d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1854d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1855b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18561da177e4SLinus Torvalds 			if (idx < s_idx)
18577562f876SPavel Emelianov 				goto cont;
18584b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
18591da177e4SLinus Torvalds 				s_ip_idx = 0;
1860eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18619f9354b9SEric Dumazet 			if (!in_dev)
18627562f876SPavel Emelianov 				goto cont;
18631da177e4SLinus Torvalds 
18641c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18651c98eca4SDavid Ahern 					       &fillargs);
18661c98eca4SDavid Ahern 			if (err < 0) {
1867eec4df98SEric Dumazet 				rcu_read_unlock();
18681da177e4SLinus Torvalds 				goto done;
18691da177e4SLinus Torvalds 			}
18707562f876SPavel Emelianov cont:
18717562f876SPavel Emelianov 			idx++;
18721da177e4SLinus Torvalds 		}
1873eec4df98SEric Dumazet 		rcu_read_unlock();
1874eec4df98SEric Dumazet 	}
18751da177e4SLinus Torvalds 
18761da177e4SLinus Torvalds done:
1877eec4df98SEric Dumazet 	cb->args[0] = h;
1878eec4df98SEric Dumazet 	cb->args[1] = idx;
18795fcd266aSDavid Ahern put_tgt_net:
1880978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1881d3807145SChristian Brauner 		put_net(tgt_net);
18821da177e4SLinus Torvalds 
18837c1e8a38SArthur Gautier 	return skb->len ? : err;
18841da177e4SLinus Torvalds }
18851da177e4SLinus Torvalds 
1886d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
188715e47304SEric W. Biederman 		      u32 portid)
18881da177e4SLinus Torvalds {
1889978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1890978a46faSChristian Brauner 		.portid = portid,
1891978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1892978a46faSChristian Brauner 		.event = event,
1893978a46faSChristian Brauner 		.flags = 0,
1894978a46faSChristian Brauner 		.netnsid = -1,
1895978a46faSChristian Brauner 	};
189647f68512SThomas Graf 	struct sk_buff *skb;
1897d6062cbbSThomas Graf 	int err = -ENOBUFS;
18984b8aa9abSDenis V. Lunev 	struct net *net;
18991da177e4SLinus Torvalds 
1900c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1901339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
190251456b29SIan Morris 	if (!skb)
1903d6062cbbSThomas Graf 		goto errout;
1904d6062cbbSThomas Graf 
1905978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
190626932566SPatrick McHardy 	if (err < 0) {
190726932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
190826932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
190926932566SPatrick McHardy 		kfree_skb(skb);
191026932566SPatrick McHardy 		goto errout;
191126932566SPatrick McHardy 	}
191215e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19131ce85fe4SPablo Neira Ayuso 	return;
1914d6062cbbSThomas Graf errout:
1915d6062cbbSThomas Graf 	if (err < 0)
19164b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19171da177e4SLinus Torvalds }
19181da177e4SLinus Torvalds 
1919b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1920b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19219f0f7272SThomas Graf {
19221fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19239f0f7272SThomas Graf 
19249f0f7272SThomas Graf 	if (!in_dev)
19259f0f7272SThomas Graf 		return 0;
19269f0f7272SThomas Graf 
19279f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19289f0f7272SThomas Graf }
19299f0f7272SThomas Graf 
1930d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1931d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19329f0f7272SThomas Graf {
19331fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19349f0f7272SThomas Graf 	struct nlattr *nla;
19359f0f7272SThomas Graf 	int i;
19369f0f7272SThomas Graf 
19379f0f7272SThomas Graf 	if (!in_dev)
19389f0f7272SThomas Graf 		return -ENODATA;
19399f0f7272SThomas Graf 
19409f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
194151456b29SIan Morris 	if (!nla)
19429f0f7272SThomas Graf 		return -EMSGSIZE;
19439f0f7272SThomas Graf 
19449f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19459f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
19469f0f7272SThomas Graf 
19479f0f7272SThomas Graf 	return 0;
19489f0f7272SThomas Graf }
19499f0f7272SThomas Graf 
19509f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19519f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19529f0f7272SThomas Graf };
19539f0f7272SThomas Graf 
1954cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
19558679c31eSRocco Yue 				 const struct nlattr *nla,
19568679c31eSRocco Yue 				 struct netlink_ext_ack *extack)
19579f0f7272SThomas Graf {
19589f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19599f0f7272SThomas Graf 	int err, rem;
19609f0f7272SThomas Graf 
1961a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
1962cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19639f0f7272SThomas Graf 
19648cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
19658679c31eSRocco Yue 					  inet_af_policy, extack);
19669f0f7272SThomas Graf 	if (err < 0)
19679f0f7272SThomas Graf 		return err;
19689f0f7272SThomas Graf 
19699f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19709f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
19719f0f7272SThomas Graf 			int cfgid = nla_type(a);
19729f0f7272SThomas Graf 
19739f0f7272SThomas Graf 			if (nla_len(a) < 4)
19749f0f7272SThomas Graf 				return -EINVAL;
19759f0f7272SThomas Graf 
19769f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
19779f0f7272SThomas Graf 				return -EINVAL;
19789f0f7272SThomas Graf 		}
19799f0f7272SThomas Graf 	}
19809f0f7272SThomas Graf 
1981cf7afbfeSThomas Graf 	return 0;
1982cf7afbfeSThomas Graf }
1983cf7afbfeSThomas Graf 
19843583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
19853583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
1986cf7afbfeSThomas Graf {
1987a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1988cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1989cf7afbfeSThomas Graf 	int rem;
1990cf7afbfeSThomas Graf 
1991cf7afbfeSThomas Graf 	if (!in_dev)
1992cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1993cf7afbfeSThomas Graf 
19948cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
19955ac6b198SZheng Yongjun 		return -EINVAL;
1996cf7afbfeSThomas Graf 
19979f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19989f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
19999f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
20009f0f7272SThomas Graf 	}
20019f0f7272SThomas Graf 
20029f0f7272SThomas Graf 	return 0;
20039f0f7272SThomas Graf }
20049f0f7272SThomas Graf 
2005edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2006edc9e748SNicolas Dichtel {
2007edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2008edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2009136ba622SZhang Shengju 	bool all = false;
2010edc9e748SNicolas Dichtel 
2011136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2012136ba622SZhang Shengju 		all = true;
2013136ba622SZhang Shengju 
2014136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2015edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2016136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2017cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2018136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2019d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20205cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20215cbf777cSXin Long 		size += nla_total_size(4);
2022136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2023f085ff1cSstephen hemminger 		size += nla_total_size(4);
2024136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2025974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2026edc9e748SNicolas Dichtel 
2027edc9e748SNicolas Dichtel 	return size;
2028edc9e748SNicolas Dichtel }
2029edc9e748SNicolas Dichtel 
2030edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
2031edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
2032edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
2033edc9e748SNicolas Dichtel 				     int type)
2034edc9e748SNicolas Dichtel {
2035edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2036edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2037136ba622SZhang Shengju 	bool all = false;
2038edc9e748SNicolas Dichtel 
2039edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2040edc9e748SNicolas Dichtel 			flags);
204151456b29SIan Morris 	if (!nlh)
2042edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2043edc9e748SNicolas Dichtel 
2044136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2045136ba622SZhang Shengju 		all = true;
2046136ba622SZhang Shengju 
2047edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2048edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2049edc9e748SNicolas Dichtel 
2050edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2051edc9e748SNicolas Dichtel 		goto nla_put_failure;
2052edc9e748SNicolas Dichtel 
2053b5c9641dSDavid Ahern 	if (!devconf)
2054b5c9641dSDavid Ahern 		goto out;
2055b5c9641dSDavid Ahern 
2056136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2057edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2058edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2059edc9e748SNicolas Dichtel 		goto nla_put_failure;
2060136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2061cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2062cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2063cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2064136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2065d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2066d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2067d67b8c61SNicolas Dichtel 		goto nla_put_failure;
20685cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
20695cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
20705cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
20715cbf777cSXin Long 		goto nla_put_failure;
2072136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
207309aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2074f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2075f085ff1cSstephen hemminger 		goto nla_put_failure;
2076136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2077974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2078974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2079974d7af5SAndy Gospodarek 		goto nla_put_failure;
2080edc9e748SNicolas Dichtel 
2081b5c9641dSDavid Ahern out:
2082053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2083053c095aSJohannes Berg 	return 0;
2084edc9e748SNicolas Dichtel 
2085edc9e748SNicolas Dichtel nla_put_failure:
2086edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2087edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2088edc9e748SNicolas Dichtel }
2089edc9e748SNicolas Dichtel 
20903b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
20913b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2092edc9e748SNicolas Dichtel {
2093edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2094edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2095edc9e748SNicolas Dichtel 
2096fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
209751456b29SIan Morris 	if (!skb)
2098edc9e748SNicolas Dichtel 		goto errout;
2099edc9e748SNicolas Dichtel 
2100edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
21013b022865SDavid Ahern 					event, 0, type);
2102edc9e748SNicolas Dichtel 	if (err < 0) {
2103edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2104edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2105edc9e748SNicolas Dichtel 		kfree_skb(skb);
2106edc9e748SNicolas Dichtel 		goto errout;
2107edc9e748SNicolas Dichtel 	}
2108fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2109edc9e748SNicolas Dichtel 	return;
2110edc9e748SNicolas Dichtel errout:
2111edc9e748SNicolas Dichtel 	if (err < 0)
2112edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2113edc9e748SNicolas Dichtel }
2114edc9e748SNicolas Dichtel 
21159e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21169e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21179e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2118cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
211909aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2120974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21219e551110SNicolas Dichtel };
21229e551110SNicolas Dichtel 
2123eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2124eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2125eede370dSJakub Kicinski 				      struct nlattr **tb,
2126eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2127eede370dSJakub Kicinski {
2128eede370dSJakub Kicinski 	int i, err;
2129eede370dSJakub Kicinski 
2130eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2131eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2132eede370dSJakub Kicinski 		return -EINVAL;
2133eede370dSJakub Kicinski 	}
2134eede370dSJakub Kicinski 
2135eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21368cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21378cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21388cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2139eede370dSJakub Kicinski 
21408cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21418cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21428cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2143eede370dSJakub Kicinski 	if (err)
2144eede370dSJakub Kicinski 		return err;
2145eede370dSJakub Kicinski 
2146eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2147eede370dSJakub Kicinski 		if (!tb[i])
2148eede370dSJakub Kicinski 			continue;
2149eede370dSJakub Kicinski 
2150eede370dSJakub Kicinski 		switch (i) {
2151eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2152eede370dSJakub Kicinski 			break;
2153eede370dSJakub Kicinski 		default:
2154eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2155eede370dSJakub Kicinski 			return -EINVAL;
2156eede370dSJakub Kicinski 		}
2157eede370dSJakub Kicinski 	}
2158eede370dSJakub Kicinski 
2159eede370dSJakub Kicinski 	return 0;
2160eede370dSJakub Kicinski }
2161eede370dSJakub Kicinski 
21629e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2163c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2164c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
21659e551110SNicolas Dichtel {
21669e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
21679e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
21689e551110SNicolas Dichtel 	struct sk_buff *skb;
21699e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
21709e551110SNicolas Dichtel 	struct in_device *in_dev;
21719e551110SNicolas Dichtel 	struct net_device *dev;
21729e551110SNicolas Dichtel 	int ifindex;
21739e551110SNicolas Dichtel 	int err;
21749e551110SNicolas Dichtel 
2175eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2176eede370dSJakub Kicinski 	if (err)
21779e551110SNicolas Dichtel 		goto errout;
21789e551110SNicolas Dichtel 
2179a97eb33fSAnton Protopopov 	err = -EINVAL;
21809e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
21819e551110SNicolas Dichtel 		goto errout;
21829e551110SNicolas Dichtel 
21839e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
21849e551110SNicolas Dichtel 	switch (ifindex) {
21859e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
21869e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
21879e551110SNicolas Dichtel 		break;
21889e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
21899e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
21909e551110SNicolas Dichtel 		break;
21919e551110SNicolas Dichtel 	default:
21929e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
219351456b29SIan Morris 		if (!dev)
21949e551110SNicolas Dichtel 			goto errout;
21959e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
219651456b29SIan Morris 		if (!in_dev)
21979e551110SNicolas Dichtel 			goto errout;
21989e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
21999e551110SNicolas Dichtel 		break;
22009e551110SNicolas Dichtel 	}
22019e551110SNicolas Dichtel 
22029e551110SNicolas Dichtel 	err = -ENOBUFS;
2203fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
220451456b29SIan Morris 	if (!skb)
22059e551110SNicolas Dichtel 		goto errout;
22069e551110SNicolas Dichtel 
22079e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22089e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22099e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2210136ba622SZhang Shengju 					NETCONFA_ALL);
22119e551110SNicolas Dichtel 	if (err < 0) {
22129e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22139e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22149e551110SNicolas Dichtel 		kfree_skb(skb);
22159e551110SNicolas Dichtel 		goto errout;
22169e551110SNicolas Dichtel 	}
22179e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22189e551110SNicolas Dichtel errout:
22199e551110SNicolas Dichtel 	return err;
22209e551110SNicolas Dichtel }
22219e551110SNicolas Dichtel 
22227a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22237a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22247a674200SNicolas Dichtel {
2225addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22267a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
22277a674200SNicolas Dichtel 	int h, s_h;
22287a674200SNicolas Dichtel 	int idx, s_idx;
22297a674200SNicolas Dichtel 	struct net_device *dev;
22307a674200SNicolas Dichtel 	struct in_device *in_dev;
22317a674200SNicolas Dichtel 	struct hlist_head *head;
22327a674200SNicolas Dichtel 
2233addd383fSDavid Ahern 	if (cb->strict_check) {
2234addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2235addd383fSDavid Ahern 		struct netconfmsg *ncm;
2236addd383fSDavid Ahern 
2237addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2238addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2239addd383fSDavid Ahern 			return -EINVAL;
2240addd383fSDavid Ahern 		}
2241addd383fSDavid Ahern 
2242addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2243addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2244addd383fSDavid Ahern 			return -EINVAL;
2245addd383fSDavid Ahern 		}
2246addd383fSDavid Ahern 	}
2247addd383fSDavid Ahern 
22487a674200SNicolas Dichtel 	s_h = cb->args[0];
22497a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
22507a674200SNicolas Dichtel 
22517a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
22527a674200SNicolas Dichtel 		idx = 0;
22537a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
22547a674200SNicolas Dichtel 		rcu_read_lock();
22550465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
22560465277fSNicolas Dichtel 			  net->dev_base_seq;
22577a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
22587a674200SNicolas Dichtel 			if (idx < s_idx)
22597a674200SNicolas Dichtel 				goto cont;
22607a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
22617a674200SNicolas Dichtel 			if (!in_dev)
22627a674200SNicolas Dichtel 				goto cont;
22637a674200SNicolas Dichtel 
22647a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
22657a674200SNicolas Dichtel 						      &in_dev->cnf,
22667a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2267addd383fSDavid Ahern 						      nlh->nlmsg_seq,
22687a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
22697a674200SNicolas Dichtel 						      NLM_F_MULTI,
2270136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
22717a674200SNicolas Dichtel 				rcu_read_unlock();
22727a674200SNicolas Dichtel 				goto done;
22737a674200SNicolas Dichtel 			}
22740465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
22757a674200SNicolas Dichtel cont:
22767a674200SNicolas Dichtel 			idx++;
22777a674200SNicolas Dichtel 		}
22787a674200SNicolas Dichtel 		rcu_read_unlock();
22797a674200SNicolas Dichtel 	}
22807a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
22817a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
22827a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
22837a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2284addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22857a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2286136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22877a674200SNicolas Dichtel 			goto done;
22887a674200SNicolas Dichtel 		else
22897a674200SNicolas Dichtel 			h++;
22907a674200SNicolas Dichtel 	}
22917a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
22927a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
22937a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
22947a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2295addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22967a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2297136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22987a674200SNicolas Dichtel 			goto done;
22997a674200SNicolas Dichtel 		else
23007a674200SNicolas Dichtel 			h++;
23017a674200SNicolas Dichtel 	}
23027a674200SNicolas Dichtel done:
23037a674200SNicolas Dichtel 	cb->args[0] = h;
23047a674200SNicolas Dichtel 	cb->args[1] = idx;
23057a674200SNicolas Dichtel 
23067a674200SNicolas Dichtel 	return skb->len;
23077a674200SNicolas Dichtel }
23087a674200SNicolas Dichtel 
23091da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23101da177e4SLinus Torvalds 
2311c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
231231be3085SHerbert Xu {
231331be3085SHerbert Xu 	struct net_device *dev;
231431be3085SHerbert Xu 
231531be3085SHerbert Xu 	rcu_read_lock();
2316c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2317c6d14c84SEric Dumazet 		struct in_device *in_dev;
2318c6d14c84SEric Dumazet 
231931be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
232031be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23219355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2322c6d14c84SEric Dumazet 	}
232331be3085SHerbert Xu 	rcu_read_unlock();
232431be3085SHerbert Xu }
232531be3085SHerbert Xu 
2326c6d14c84SEric Dumazet /* called with RTNL locked */
2327c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
232868dd299bSPavel Emelyanov {
232968dd299bSPavel Emelyanov 	struct net_device *dev;
2330586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
233168dd299bSPavel Emelyanov 
2332586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23339355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23343b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23353b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2336edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2337edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23383b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23393b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2340edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2341edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
234268dd299bSPavel Emelyanov 
2343c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
234468dd299bSPavel Emelyanov 		struct in_device *in_dev;
2345fa17806cSEric Dumazet 
23460187bdfbSBen Hutchings 		if (on)
23470187bdfbSBen Hutchings 			dev_disable_lro(dev);
2348fa17806cSEric Dumazet 
2349fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2350edc9e748SNicolas Dichtel 		if (in_dev) {
235168dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23523b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23533b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2354edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2355edc9e748SNicolas Dichtel 		}
235668dd299bSPavel Emelyanov 	}
235768dd299bSPavel Emelyanov }
235868dd299bSPavel Emelyanov 
2359f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2360f085ff1cSstephen hemminger {
2361f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2362f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2363f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2364f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2365f085ff1cSstephen hemminger 	else {
2366f085ff1cSstephen hemminger 		struct in_device *idev
2367f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2368f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2369f085ff1cSstephen hemminger 	}
2370f085ff1cSstephen hemminger }
2371f085ff1cSstephen hemminger 
2372fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
237332927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
237431be3085SHerbert Xu {
2375d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23768d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2377d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
237831be3085SHerbert Xu 
237931be3085SHerbert Xu 	if (write) {
238031be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2381c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
238231be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2383f085ff1cSstephen hemminger 		int ifindex;
238431be3085SHerbert Xu 
238531be3085SHerbert Xu 		set_bit(i, cnf->state);
238631be3085SHerbert Xu 
23879355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2388c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2389d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2390d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2391d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
23924ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2393f085ff1cSstephen hemminger 
23945cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
23955cbf777cSXin Long 		    new_value != old_value)
23965cbf777cSXin Long 			rt_cache_flush(net);
23975cbf777cSXin Long 
2398cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2399cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2400f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24013b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24023b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2403cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2404cc535dfbSNicolas Dichtel 		}
2405f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2406f085ff1cSstephen hemminger 		    new_value != old_value) {
2407f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24083b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24093b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2410f085ff1cSstephen hemminger 						    ifindex, cnf);
2411f085ff1cSstephen hemminger 		}
2412974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2413974d7af5SAndy Gospodarek 		    new_value != old_value) {
2414974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24153b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24163b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2417974d7af5SAndy Gospodarek 						    ifindex, cnf);
2418974d7af5SAndy Gospodarek 		}
241931be3085SHerbert Xu 	}
242031be3085SHerbert Xu 
242131be3085SHerbert Xu 	return ret;
242231be3085SHerbert Xu }
242331be3085SHerbert Xu 
2424fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
242532927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24261da177e4SLinus Torvalds {
24271da177e4SLinus Torvalds 	int *valp = ctl->data;
24281da177e4SLinus Torvalds 	int val = *valp;
242988af182eSEric W. Biederman 	loff_t pos = *ppos;
24308292d7f6SYang Yang 	struct net *net = ctl->extra2;
24318292d7f6SYang Yang 	int ret;
24328292d7f6SYang Yang 
24338292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24348292d7f6SYang Yang 		return -EPERM;
24358292d7f6SYang Yang 
24368292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24371da177e4SLinus Torvalds 
24381da177e4SLinus Torvalds 	if (write && *valp != val) {
24390187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
244088af182eSEric W. Biederman 			if (!rtnl_trylock()) {
244188af182eSEric W. Biederman 				/* Restore the original values before restarting */
244288af182eSEric W. Biederman 				*valp = val;
244388af182eSEric W. Biederman 				*ppos = pos;
24449b8adb5eSEric W. Biederman 				return restart_syscall();
244588af182eSEric W. Biederman 			}
24460187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2447c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2448edc9e748SNicolas Dichtel 			} else {
24490187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24500187bdfbSBen Hutchings 				struct in_device *idev =
24510187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2452edc9e748SNicolas Dichtel 				if (*valp)
24530187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24543b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2455edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2456edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2457edc9e748SNicolas Dichtel 							    cnf);
24580187bdfbSBen Hutchings 			}
24590187bdfbSBen Hutchings 			rtnl_unlock();
24604ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2461edc9e748SNicolas Dichtel 		} else
24623b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24633b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2464edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2465edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24660187bdfbSBen Hutchings 	}
24671da177e4SLinus Torvalds 
24681da177e4SLinus Torvalds 	return ret;
24691da177e4SLinus Torvalds }
24701da177e4SLinus Torvalds 
2471fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
247232927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
24731da177e4SLinus Torvalds {
24741da177e4SLinus Torvalds 	int *valp = ctl->data;
24751da177e4SLinus Torvalds 	int val = *valp;
24768d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
247776e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24781da177e4SLinus Torvalds 
24791da177e4SLinus Torvalds 	if (write && *valp != val)
24804ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
24811da177e4SLinus Torvalds 
24821da177e4SLinus Torvalds 	return ret;
24831da177e4SLinus Torvalds }
24841da177e4SLinus Torvalds 
2485f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
248642f811b8SHerbert Xu 	{ \
248742f811b8SHerbert Xu 		.procname	= name, \
248842f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
248902291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
249042f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
249142f811b8SHerbert Xu 		.mode		= mval, \
249242f811b8SHerbert Xu 		.proc_handler	= proc, \
249331be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
249442f811b8SHerbert Xu 	}
249542f811b8SHerbert Xu 
249642f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2497f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
249842f811b8SHerbert Xu 
249942f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2500f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
250142f811b8SHerbert Xu 
2502f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2503f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
250442f811b8SHerbert Xu 
250542f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2506f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
250742f811b8SHerbert Xu 
25081da177e4SLinus Torvalds static struct devinet_sysctl_table {
25091da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
251002291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
25111da177e4SLinus Torvalds } devinet_sysctl = {
25121da177e4SLinus Torvalds 	.devinet_vars = {
251342f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2514f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
251542f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25165cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
251742f811b8SHerbert Xu 
251842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
251942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
252042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
252142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
252242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
252342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
252442f811b8SHerbert Xu 					"accept_source_route"),
25258153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
252628f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
252742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
252842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
252942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
253042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
253142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
253242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
253342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
253442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
253542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2536eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
2537fcdb44d0SJames Prestwood 		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
2538fcdb44d0SJames Prestwood 					"arp_evict_nocarrier"),
253965324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25405c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25415c6fe01cSWilliam Manley 					"force_igmp_version"),
25422690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25432690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25442690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25452690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25460eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25470eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
254897daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
254997daf331SJohannes Berg 					"drop_gratuitous_arp"),
255042f811b8SHerbert Xu 
255142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
255242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
255342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
255442f811b8SHerbert Xu 					      "promote_secondaries"),
2555d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2556d0daebc3SThomas Graf 					      "route_localnet"),
255712b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
255812b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25591da177e4SLinus Torvalds 	},
25601da177e4SLinus Torvalds };
25611da177e4SLinus Torvalds 
2562ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
256329c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25641da177e4SLinus Torvalds {
25651da177e4SLinus Torvalds 	int i;
25669fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25678607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2568bfada697SPavel Emelyanov 
25699fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
25701da177e4SLinus Torvalds 	if (!t)
25719fa89642SPavel Emelyanov 		goto out;
25729fa89642SPavel Emelyanov 
25731da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
25741da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
257531be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2576c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25771da177e4SLinus Torvalds 	}
25781da177e4SLinus Torvalds 
25798607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
25801da177e4SLinus Torvalds 
25818607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
25821da177e4SLinus Torvalds 	if (!t->sysctl_header)
25838607ddb8SEric W. Biederman 		goto free;
25841da177e4SLinus Torvalds 
25851da177e4SLinus Torvalds 	p->sysctl = t;
258629c994e3SNicolas Dichtel 
25873b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
25883b022865SDavid Ahern 				    ifindex, p);
2589ea40b324SPavel Emelyanov 	return 0;
25901da177e4SLinus Torvalds 
25911da177e4SLinus Torvalds free:
25921da177e4SLinus Torvalds 	kfree(t);
25939fa89642SPavel Emelyanov out:
25946def4801Sliuguoqiang 	return -ENOMEM;
25951da177e4SLinus Torvalds }
25961da177e4SLinus Torvalds 
2597b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2598b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
259966f27a52SPavel Emelyanov {
260051602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
260166f27a52SPavel Emelyanov 
2602b5c9641dSDavid Ahern 	if (t) {
260351602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2604ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26051da177e4SLinus Torvalds 		kfree(t);
26061da177e4SLinus Torvalds 	}
260751602b2aSPavel Emelyanov 
2608b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2609b5c9641dSDavid Ahern }
2610b5c9641dSDavid Ahern 
261120e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
261251602b2aSPavel Emelyanov {
261320e61da7SWANG Cong 	int err;
261420e61da7SWANG Cong 
261520e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
261620e61da7SWANG Cong 		return -EINVAL;
261720e61da7SWANG Cong 
261820e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
261920e61da7SWANG Cong 	if (err)
262020e61da7SWANG Cong 		return err;
262120e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
262229c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
262320e61da7SWANG Cong 	if (err)
262420e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
262520e61da7SWANG Cong 	return err;
262651602b2aSPavel Emelyanov }
262751602b2aSPavel Emelyanov 
262851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
262951602b2aSPavel Emelyanov {
2630b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2631b5c9641dSDavid Ahern 
2632b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
263351602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26341da177e4SLinus Torvalds }
26351da177e4SLinus Torvalds 
263668dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
263768dd299bSPavel Emelyanov 	{
263868dd299bSPavel Emelyanov 		.procname	= "ip_forward",
263968dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
264002291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
264168dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
264268dd299bSPavel Emelyanov 		.mode		= 0644,
264368dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
264468dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2645c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
264668dd299bSPavel Emelyanov 	},
264768dd299bSPavel Emelyanov 	{ },
264868dd299bSPavel Emelyanov };
26492a75de0cSEric Dumazet #endif
265068dd299bSPavel Emelyanov 
2651752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2652752d14dcSPavel Emelyanov {
2653752d14dcSPavel Emelyanov 	int err;
2654752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26552a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2656856c395cSCong Wang 	struct ctl_table *tbl;
2657752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26582a75de0cSEric Dumazet #endif
2659752d14dcSPavel Emelyanov 
2660752d14dcSPavel Emelyanov 	err = -ENOMEM;
2661856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
266251456b29SIan Morris 	if (!all)
2663752d14dcSPavel Emelyanov 		goto err_alloc_all;
2664752d14dcSPavel Emelyanov 
2665856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
266651456b29SIan Morris 	if (!dflt)
2667752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2668752d14dcSPavel Emelyanov 
26692a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2670856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
267151456b29SIan Morris 	if (!tbl)
2672752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2673752d14dcSPavel Emelyanov 
267402291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2675752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2676752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26772a75de0cSEric Dumazet #endif
2678856c395cSCong Wang 
26799efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
26809efd6a3cSNicolas Dichtel 		if (IS_ENABLED(CONFIG_SYSCTL) &&
26819efd6a3cSNicolas Dichtel 		    sysctl_devconf_inherit_init_net == 3) {
26829efd6a3cSNicolas Dichtel 			/* copy from the current netns */
26839efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
26849efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
26859efd6a3cSNicolas Dichtel 			memcpy(dflt,
26869efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
26879efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
26889efd6a3cSNicolas Dichtel 		} else if (!IS_ENABLED(CONFIG_SYSCTL) ||
26899efd6a3cSNicolas Dichtel 			   sysctl_devconf_inherit_init_net != 2) {
26909efd6a3cSNicolas Dichtel 			/* inherit == 0 or 1: copy from init_net */
26919efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
26929efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
26939efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
26949efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
26959efd6a3cSNicolas Dichtel 		}
26969efd6a3cSNicolas Dichtel 		/* else inherit == 2: use compiled values */
2697752d14dcSPavel Emelyanov 	}
2698752d14dcSPavel Emelyanov 
2699752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
270029c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2701752d14dcSPavel Emelyanov 	if (err < 0)
2702752d14dcSPavel Emelyanov 		goto err_reg_all;
2703752d14dcSPavel Emelyanov 
270429c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
270529c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2706752d14dcSPavel Emelyanov 	if (err < 0)
2707752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2708752d14dcSPavel Emelyanov 
2709752d14dcSPavel Emelyanov 	err = -ENOMEM;
27108607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
271151456b29SIan Morris 	if (!forw_hdr)
2712752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27132a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2714752d14dcSPavel Emelyanov #endif
2715752d14dcSPavel Emelyanov 
2716752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2717752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2718752d14dcSPavel Emelyanov 	return 0;
2719752d14dcSPavel Emelyanov 
2720752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2721752d14dcSPavel Emelyanov err_reg_ctl:
2722b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2723752d14dcSPavel Emelyanov err_reg_dflt:
2724b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2725752d14dcSPavel Emelyanov err_reg_all:
2726752d14dcSPavel Emelyanov 	kfree(tbl);
2727752d14dcSPavel Emelyanov err_alloc_ctl:
27282a75de0cSEric Dumazet #endif
2729752d14dcSPavel Emelyanov 	kfree(dflt);
2730752d14dcSPavel Emelyanov err_alloc_dflt:
2731752d14dcSPavel Emelyanov 	kfree(all);
2732752d14dcSPavel Emelyanov err_alloc_all:
2733752d14dcSPavel Emelyanov 	return err;
2734752d14dcSPavel Emelyanov }
2735752d14dcSPavel Emelyanov 
2736752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2737752d14dcSPavel Emelyanov {
27382a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2739752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2740752d14dcSPavel Emelyanov 
2741752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2742752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2743b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2744b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2745b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2746b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2747752d14dcSPavel Emelyanov 	kfree(tbl);
27482a75de0cSEric Dumazet #endif
2749752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2750752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2751752d14dcSPavel Emelyanov }
2752752d14dcSPavel Emelyanov 
2753752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2754752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2755752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2756752d14dcSPavel Emelyanov };
2757752d14dcSPavel Emelyanov 
2758207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
27599f0f7272SThomas Graf 	.family		  = AF_INET,
27609f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
27619f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2762cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2763cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
27649f0f7272SThomas Graf };
27659f0f7272SThomas Graf 
27661da177e4SLinus Torvalds void __init devinet_init(void)
27671da177e4SLinus Torvalds {
2768fd23c3b3SDavid S. Miller 	int i;
2769fd23c3b3SDavid S. Miller 
2770fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2771fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2772fd23c3b3SDavid S. Miller 
2773752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
27741da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
277563f3444fSThomas Graf 
2776906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
27775c766d64SJiri Pirko 
27789f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
27799f0f7272SThomas Graf 
2780b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2781b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2782b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
27839e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2784b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
27851da177e4SLinus Torvalds }
2786