xref: /linux/net/ipv4/devinet.c (revision 2e60546368165c2449564d71f6005dda9205b5fb)
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 
65*2e605463SMatteo Croce #define IPV6ONLY_FLAGS	\
66*2e605463SMatteo Croce 		(IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \
67*2e605463SMatteo Croce 		 IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \
68*2e605463SMatteo Croce 		 IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY)
69*2e605463SMatteo 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*/,
7842f811b8SHerbert Xu 	},
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8242f811b8SHerbert Xu 	.data = {
8302291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
882690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
9042f811b8SHerbert Xu 	},
911da177e4SLinus Torvalds };
921da177e4SLinus Torvalds 
939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
949355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9542f811b8SHerbert Xu 
96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
975c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
985c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
995c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1005176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1015c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
102ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
103af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
104d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
1055c753978SThomas Graf };
1065c753978SThomas Graf 
107978a46faSChristian Brauner struct inet_fill_args {
108978a46faSChristian Brauner 	u32 portid;
109978a46faSChristian Brauner 	u32 seq;
110978a46faSChristian Brauner 	int event;
111978a46faSChristian Brauner 	unsigned int flags;
112978a46faSChristian Brauner 	int netnsid;
1135fcd266aSDavid Ahern 	int ifindex;
114978a46faSChristian Brauner };
115978a46faSChristian Brauner 
11640384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
11740384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
11840384999SEric Dumazet 
119fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
120fd23c3b3SDavid S. Miller 
1216eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
122fd23c3b3SDavid S. Miller {
12340384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
124fd23c3b3SDavid S. Miller 
12540384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
126fd23c3b3SDavid S. Miller }
127fd23c3b3SDavid S. Miller 
128fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
129fd23c3b3SDavid S. Miller {
13040384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
131fd23c3b3SDavid S. Miller 
13232a4be48SWANG Cong 	ASSERT_RTNL();
133fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
134fd23c3b3SDavid S. Miller }
135fd23c3b3SDavid S. Miller 
136fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
137fd23c3b3SDavid S. Miller {
13832a4be48SWANG Cong 	ASSERT_RTNL();
139fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
140fd23c3b3SDavid S. Miller }
141fd23c3b3SDavid S. Miller 
1429435eb1cSDavid S. Miller /**
1439435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1449435eb1cSDavid S. Miller  * @net: the net namespace
1459435eb1cSDavid S. Miller  * @addr: the source address
1469435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1479435eb1cSDavid S. Miller  *
1489435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1499435eb1cSDavid S. Miller  */
1509435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1519435eb1cSDavid S. Miller {
1529435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1539435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1549435eb1cSDavid S. Miller 
1559435eb1cSDavid S. Miller 	rcu_read_lock();
1566e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1576e617de8SPaolo Abeni 	if (!ifa) {
158406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
159406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
160406b6f97SDavid S. Miller 		struct fib_table *local;
161406b6f97SDavid S. Miller 
162406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
163406b6f97SDavid S. Miller 		 * over loopback subnets work.
164406b6f97SDavid S. Miller 		 */
165406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
166406b6f97SDavid S. Miller 		if (local &&
167406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
168406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
169406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1706e617de8SPaolo Abeni 	} else {
1716e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
172406b6f97SDavid S. Miller 	}
1739435eb1cSDavid S. Miller 	if (result && devref)
1749435eb1cSDavid S. Miller 		dev_hold(result);
1759435eb1cSDavid S. Miller 	rcu_read_unlock();
1769435eb1cSDavid S. Miller 	return result;
1779435eb1cSDavid S. Miller }
1789435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1799435eb1cSDavid S. Miller 
1806e617de8SPaolo Abeni /* called under RCU lock */
1816e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1826e617de8SPaolo Abeni {
1836e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1846e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1856e617de8SPaolo Abeni 
1866e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1876e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1886e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1896e617de8SPaolo Abeni 			return ifa;
1906e617de8SPaolo Abeni 
1916e617de8SPaolo Abeni 	return NULL;
1926e617de8SPaolo Abeni }
1936e617de8SPaolo Abeni 
194d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1951da177e4SLinus Torvalds 
196e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1973ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
1981da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1991da177e4SLinus Torvalds 			 int destroy);
2001da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20120e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20351602b2aSPavel Emelyanov #else
20420e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20551602b2aSPavel Emelyanov {
20620e61da7SWANG Cong 	return 0;
20751602b2aSPavel Emelyanov }
20840384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
20951602b2aSPavel Emelyanov {
21051602b2aSPavel Emelyanov }
2111da177e4SLinus Torvalds #endif
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds /* Locks all the inet devices. */
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2161da177e4SLinus Torvalds {
21793adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2211da177e4SLinus Torvalds {
2221da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2231da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2241da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2251da177e4SLinus Torvalds 	kfree(ifa);
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
22840384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2341da177e4SLinus Torvalds {
2351da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2361da177e4SLinus Torvalds 
237547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
238547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
239e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2401da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
24191df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2421da177e4SLinus Torvalds #endif
2431da177e4SLinus Torvalds 	dev_put(dev);
2441da177e4SLinus Torvalds 	if (!idev->dead)
2459f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2469f9354b9SEric Dumazet 	else
2471da177e4SLinus Torvalds 		kfree(idev);
2481da177e4SLinus Torvalds }
2499f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2501da177e4SLinus Torvalds 
25171e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2521da177e4SLinus Torvalds {
2531da177e4SLinus Torvalds 	struct in_device *in_dev;
25420e61da7SWANG Cong 	int err = -ENOMEM;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	ASSERT_RTNL();
2571da177e4SLinus Torvalds 
2580da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2591da177e4SLinus Torvalds 	if (!in_dev)
2601da177e4SLinus Torvalds 		goto out;
261c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2629355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2631da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2641da177e4SLinus Torvalds 	in_dev->dev = dev;
2659f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2669f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2671da177e4SLinus Torvalds 		goto out_kfree;
2680187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2690187bdfbSBen Hutchings 		dev_disable_lro(dev);
2701da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2711da177e4SLinus Torvalds 	dev_hold(dev);
27230c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2737658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2741da177e4SLinus Torvalds 
27520e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
27620e61da7SWANG Cong 	if (err) {
27720e61da7SWANG Cong 		in_dev->dead = 1;
27820e61da7SWANG Cong 		in_dev_put(in_dev);
27920e61da7SWANG Cong 		in_dev = NULL;
28020e61da7SWANG Cong 		goto out;
28120e61da7SWANG Cong 	}
2821da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2831da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2841da177e4SLinus Torvalds 		ip_mc_up(in_dev);
285483479ecSJarek Poplawski 
28630c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
287cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
288483479ecSJarek Poplawski out:
28920e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2901da177e4SLinus Torvalds out_kfree:
2911da177e4SLinus Torvalds 	kfree(in_dev);
2921da177e4SLinus Torvalds 	in_dev = NULL;
2931da177e4SLinus Torvalds 	goto out;
2941da177e4SLinus Torvalds }
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2991da177e4SLinus Torvalds 	in_dev_put(idev);
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
3051da177e4SLinus Torvalds 	struct net_device *dev;
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	ASSERT_RTNL();
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	dev = in_dev->dev;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	in_dev->dead = 1;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
3161da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3171da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3181da177e4SLinus Torvalds 	}
3191da177e4SLinus Torvalds 
320a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3211da177e4SLinus Torvalds 
32251602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3231da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3241da177e4SLinus Torvalds 	arp_ifdown(dev);
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds 
329ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3301da177e4SLinus Torvalds {
3311da177e4SLinus Torvalds 	rcu_read_lock();
3321da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3331da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3341da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3351da177e4SLinus Torvalds 				rcu_read_unlock();
3361da177e4SLinus Torvalds 				return 1;
3371da177e4SLinus Torvalds 			}
3381da177e4SLinus Torvalds 		}
3391da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3401da177e4SLinus Torvalds 	rcu_read_unlock();
3411da177e4SLinus Torvalds 	return 0;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds 
344d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
34515e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3461da177e4SLinus Torvalds {
3478f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3480ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3490ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3500ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3510ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	ASSERT_RTNL();
3541da177e4SLinus Torvalds 
355fbd40ea0SDavid S. Miller 	if (in_dev->dead)
356fbd40ea0SDavid S. Miller 		goto no_promotions;
357fbd40ea0SDavid S. Miller 
3588f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3598f937c60SHarald Welte 	 * unless alias promotion is set
3608f937c60SHarald Welte 	 **/
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3631da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3660ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3670ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3680ff60a45SJamal Hadi Salim 				last_prim = ifa;
3690ff60a45SJamal Hadi Salim 
3701da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3711da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3721da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3731da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3740ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3751da177e4SLinus Torvalds 				continue;
3761da177e4SLinus Torvalds 			}
3771da177e4SLinus Torvalds 
3780ff60a45SJamal Hadi Salim 			if (!do_promote) {
379fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3801da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3811da177e4SLinus Torvalds 
38215e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
383e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
384e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3851da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3868f937c60SHarald Welte 			} else {
3878f937c60SHarald Welte 				promote = ifa;
3888f937c60SHarald Welte 				break;
3898f937c60SHarald Welte 			}
3901da177e4SLinus Torvalds 		}
3911da177e4SLinus Torvalds 	}
3921da177e4SLinus Torvalds 
3932d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3942d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3952d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3962d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3972d230e2bSJulian Anastasov 	 */
3982d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3992d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4002d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4012d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4022d230e2bSJulian Anastasov 	}
4032d230e2bSJulian Anastasov 
404fbd40ea0SDavid S. Miller no_promotions:
4051da177e4SLinus Torvalds 	/* 2. Unlink it */
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
408fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4131da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4141da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4151da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4161da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4171da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4181da177e4SLinus Torvalds 	   So that, this order is correct.
4191da177e4SLinus Torvalds 	 */
42015e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
421e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4220ff60a45SJamal Hadi Salim 
4230ff60a45SJamal Hadi Salim 	if (promote) {
42404024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
4250ff60a45SJamal Hadi Salim 
4260ff60a45SJamal Hadi Salim 		if (prev_prom) {
4270ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
4280ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
4290ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4300ff60a45SJamal Hadi Salim 		}
4310ff60a45SJamal Hadi Salim 
4320ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
43315e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
434e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
435e041c683SAlan Stern 				NETDEV_UP, promote);
43604024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4370ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4380ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4390ff60a45SJamal Hadi Salim 					continue;
4400ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4410ff60a45SJamal Hadi Salim 		}
4420ff60a45SJamal Hadi Salim 
4430ff60a45SJamal Hadi Salim 	}
4446363097cSHerbert Xu 	if (destroy)
4451da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4461da177e4SLinus Torvalds }
4471da177e4SLinus Torvalds 
448d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
449d6062cbbSThomas Graf 			 int destroy)
450d6062cbbSThomas Graf {
451d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
452d6062cbbSThomas Graf }
453d6062cbbSThomas Graf 
4545c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4555c766d64SJiri Pirko 
4565c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4575c766d64SJiri Pirko 
458d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
459de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4601da177e4SLinus Torvalds {
4611da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4621da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4633ad7d246SKrister Johansen 	struct in_validator_info ivi;
4643ad7d246SKrister Johansen 	int ret;
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	ASSERT_RTNL();
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4691da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4701da177e4SLinus Torvalds 		return 0;
4711da177e4SLinus Torvalds 	}
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4741da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4751da177e4SLinus Torvalds 
476*2e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
477*2e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
478*2e605463SMatteo Croce 
4791da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4801da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4811da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4821da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4831da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4841da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4851da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4861da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4871da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4881da177e4SLinus Torvalds 				return -EEXIST;
4891da177e4SLinus Torvalds 			}
4901da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4911da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4921da177e4SLinus Torvalds 				return -EINVAL;
4931da177e4SLinus Torvalds 			}
4941da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4951da177e4SLinus Torvalds 		}
4961da177e4SLinus Torvalds 	}
4971da177e4SLinus Torvalds 
4983ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
4993ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5003ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5013ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5023ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5033ad7d246SKrister Johansen 	 * not the flags.
5043ad7d246SKrister Johansen 	 */
5053ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5063ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
507de95e047SDavid Ahern 	ivi.extack = extack;
5083ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5093ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5103ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5113ad7d246SKrister Johansen 	if (ret) {
5123ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5133ad7d246SKrister Johansen 		return ret;
5143ad7d246SKrister Johansen 	}
5153ad7d246SKrister Johansen 
5161da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
51763862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
5181da177e4SLinus Torvalds 		ifap = last_primary;
5191da177e4SLinus Torvalds 	}
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
5221da177e4SLinus Torvalds 	*ifap = ifa;
5231da177e4SLinus Torvalds 
524fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
525fd23c3b3SDavid S. Miller 
5265c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
527906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5285c766d64SJiri Pirko 
5291da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5301da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5311da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
53215e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
533e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds 	return 0;
5361da177e4SLinus Torvalds }
5371da177e4SLinus Torvalds 
538d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
539d6062cbbSThomas Graf {
540de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
541d6062cbbSThomas Graf }
542d6062cbbSThomas Graf 
5431da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5441da177e4SLinus Torvalds {
545e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 	ASSERT_RTNL();
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds 	if (!in_dev) {
5501da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5511da177e4SLinus Torvalds 		return -ENOBUFS;
5521da177e4SLinus Torvalds 	}
55371e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5541d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5551da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
556547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5571da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5581da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5591da177e4SLinus Torvalds 	}
560f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5611da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5621da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds 
5658723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5668723e1b4SEric Dumazet  * We dont take a reference on found in_device
5678723e1b4SEric Dumazet  */
5687fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5691da177e4SLinus Torvalds {
5701da177e4SLinus Torvalds 	struct net_device *dev;
5711da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
572c148fc2eSEric Dumazet 
573c148fc2eSEric Dumazet 	rcu_read_lock();
574c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5751da177e4SLinus Torvalds 	if (dev)
5768723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
577c148fc2eSEric Dumazet 	rcu_read_unlock();
5781da177e4SLinus Torvalds 	return in_dev;
5791da177e4SLinus Torvalds }
5809f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5831da177e4SLinus Torvalds 
58460cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
58560cad5daSAl Viro 				    __be32 mask)
5861da177e4SLinus Torvalds {
5871da177e4SLinus Torvalds 	ASSERT_RTNL();
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5901da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5911da177e4SLinus Torvalds 			return ifa;
5921da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5931da177e4SLinus Torvalds 	return NULL;
5941da177e4SLinus Torvalds }
5951da177e4SLinus Torvalds 
59693a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
59793a714d6SMadhu Challa {
59893a714d6SMadhu Challa 	struct ip_mreqn mreq = {
59993a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
60093a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
60193a714d6SMadhu Challa 	};
60293a714d6SMadhu Challa 	int ret;
60393a714d6SMadhu Challa 
60493a714d6SMadhu Challa 	ASSERT_RTNL();
60593a714d6SMadhu Challa 
60693a714d6SMadhu Challa 	lock_sock(sk);
60793a714d6SMadhu Challa 	if (join)
60854ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
60993a714d6SMadhu Challa 	else
61054ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
61193a714d6SMadhu Challa 	release_sock(sk);
61293a714d6SMadhu Challa 
61393a714d6SMadhu Challa 	return ret;
61493a714d6SMadhu Challa }
61593a714d6SMadhu Challa 
616c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
617c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6181da177e4SLinus Torvalds {
6193b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
620dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6211da177e4SLinus Torvalds 	struct in_device *in_dev;
622dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6231da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
624dfdd5fd4SThomas Graf 	int err = -EINVAL;
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 	ASSERT_RTNL();
6271da177e4SLinus Torvalds 
6288cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6298cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
630dfdd5fd4SThomas Graf 	if (err < 0)
631dfdd5fd4SThomas Graf 		goto errout;
632dfdd5fd4SThomas Graf 
633dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6347fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
63551456b29SIan Morris 	if (!in_dev) {
636dfdd5fd4SThomas Graf 		err = -ENODEV;
637dfdd5fd4SThomas Graf 		goto errout;
638dfdd5fd4SThomas Graf 	}
639dfdd5fd4SThomas Graf 
6401da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
6411da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
642dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
64367b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6441da177e4SLinus Torvalds 			continue;
645dfdd5fd4SThomas Graf 
646dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
647dfdd5fd4SThomas Graf 			continue;
648dfdd5fd4SThomas Graf 
649dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
650dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
65167b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
652dfdd5fd4SThomas Graf 			continue;
653dfdd5fd4SThomas Graf 
65493a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
65593a714d6SMadhu Challa 			ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
65615e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6571da177e4SLinus Torvalds 		return 0;
6581da177e4SLinus Torvalds 	}
659dfdd5fd4SThomas Graf 
660dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
661dfdd5fd4SThomas Graf errout:
662dfdd5fd4SThomas Graf 	return err;
6631da177e4SLinus Torvalds }
6641da177e4SLinus Torvalds 
6655c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6665c766d64SJiri Pirko 
6675c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6685c766d64SJiri Pirko {
6695c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6705c766d64SJiri Pirko 	struct in_ifaddr *ifa;
671c988d1e8SJiri Pirko 	struct hlist_node *n;
6725c766d64SJiri Pirko 	int i;
6735c766d64SJiri Pirko 
6745c766d64SJiri Pirko 	now = jiffies;
6755c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6765c766d64SJiri Pirko 
6775c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
678c988d1e8SJiri Pirko 		bool change_needed = false;
679c988d1e8SJiri Pirko 
680c988d1e8SJiri Pirko 		rcu_read_lock();
681b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6825c766d64SJiri Pirko 			unsigned long age;
6835c766d64SJiri Pirko 
6845c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6855c766d64SJiri Pirko 				continue;
6865c766d64SJiri Pirko 
6875c766d64SJiri Pirko 			/* We try to batch several events at once. */
6885c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6895c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6905c766d64SJiri Pirko 
6915c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6925c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
693c988d1e8SJiri Pirko 				change_needed = true;
694c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
695c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
696c988d1e8SJiri Pirko 				continue;
697c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
698c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
699c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
700c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
701c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
702c988d1e8SJiri Pirko 
703c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
704c988d1e8SJiri Pirko 					change_needed = true;
705c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
706c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
707c988d1e8SJiri Pirko 					       next)) {
708c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
709c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
710c988d1e8SJiri Pirko 			}
711c988d1e8SJiri Pirko 		}
712c988d1e8SJiri Pirko 		rcu_read_unlock();
713c988d1e8SJiri Pirko 		if (!change_needed)
714c988d1e8SJiri Pirko 			continue;
715c988d1e8SJiri Pirko 		rtnl_lock();
716c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
717c988d1e8SJiri Pirko 			unsigned long age;
718c988d1e8SJiri Pirko 
719c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
720c988d1e8SJiri Pirko 				continue;
721c988d1e8SJiri Pirko 
722c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
723c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
724c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
725c988d1e8SJiri Pirko 
726c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
727c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7285c766d64SJiri Pirko 				struct in_ifaddr **ifap;
7295c766d64SJiri Pirko 
7305c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
731c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
732c988d1e8SJiri Pirko 					if (*ifap == ifa) {
7335c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7345c766d64SJiri Pirko 							     ifap, 1);
735c988d1e8SJiri Pirko 						break;
7365c766d64SJiri Pirko 					}
737c988d1e8SJiri Pirko 				}
738c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
739c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
740c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
741c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7425c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7435c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7445c766d64SJiri Pirko 			}
7455c766d64SJiri Pirko 		}
746c988d1e8SJiri Pirko 		rtnl_unlock();
7475c766d64SJiri Pirko 	}
7485c766d64SJiri Pirko 
7495c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7505c766d64SJiri Pirko 	next_sched = next;
7515c766d64SJiri Pirko 
7525c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7535c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7545c766d64SJiri Pirko 		next_sched = next_sec;
7555c766d64SJiri Pirko 
7565c766d64SJiri Pirko 	now = jiffies;
7575c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7585c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7595c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7605c766d64SJiri Pirko 
761906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
762906e073fSviresh kumar 			next_sched - now);
7635c766d64SJiri Pirko }
7645c766d64SJiri Pirko 
7655c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7665c766d64SJiri Pirko 			     __u32 prefered_lft)
7675c766d64SJiri Pirko {
7685c766d64SJiri Pirko 	unsigned long timeout;
7695c766d64SJiri Pirko 
7705c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
7715c766d64SJiri Pirko 
7725c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
7735c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
7745c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7755c766d64SJiri Pirko 	else
7765c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
7775c766d64SJiri Pirko 
7785c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
7795c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
7805c766d64SJiri Pirko 		if (timeout == 0)
7815c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7825c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7835c766d64SJiri Pirko 	}
7845c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7855c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7865c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7875c766d64SJiri Pirko }
7885c766d64SJiri Pirko 
7895c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
790dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
791dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
7921da177e4SLinus Torvalds {
7935c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7945c753978SThomas Graf 	struct in_ifaddr *ifa;
7955c753978SThomas Graf 	struct ifaddrmsg *ifm;
7961da177e4SLinus Torvalds 	struct net_device *dev;
7971da177e4SLinus Torvalds 	struct in_device *in_dev;
7987b218574SDenis V. Lunev 	int err;
7991da177e4SLinus Torvalds 
8008cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8018cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8025c753978SThomas Graf 	if (err < 0)
8035c753978SThomas Graf 		goto errout;
8041da177e4SLinus Torvalds 
8055c753978SThomas Graf 	ifm = nlmsg_data(nlh);
806c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
80751456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8085c753978SThomas Graf 		goto errout;
8091da177e4SLinus Torvalds 
8104b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8115c753978SThomas Graf 	err = -ENODEV;
81251456b29SIan Morris 	if (!dev)
8135c753978SThomas Graf 		goto errout;
8141da177e4SLinus Torvalds 
8155c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8165c753978SThomas Graf 	err = -ENOBUFS;
81751456b29SIan Morris 	if (!in_dev)
8185c753978SThomas Graf 		goto errout;
81971e27da9SHerbert Xu 
8205c753978SThomas Graf 	ifa = inet_alloc_ifa();
82151456b29SIan Morris 	if (!ifa)
8225c753978SThomas Graf 		/*
8235c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8245c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8255c753978SThomas Graf 		 */
8265c753978SThomas Graf 		goto errout;
8275c753978SThomas Graf 
828a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8291d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8305c753978SThomas Graf 	in_dev_hold(in_dev);
8315c753978SThomas Graf 
83251456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8335c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8345c753978SThomas Graf 
835fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8361da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8371da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
838ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
839ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8401da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8411da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8425c753978SThomas Graf 
84367b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
84467b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8455c753978SThomas Graf 
8465c753978SThomas Graf 	if (tb[IFA_BROADCAST])
84767b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8485c753978SThomas Graf 
8495c753978SThomas Graf 	if (tb[IFA_LABEL])
8505c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8511da177e4SLinus Torvalds 	else
8521da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8531da177e4SLinus Torvalds 
854af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
855af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
856af4d768aSDavid Ahern 
8575c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8585c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8595c766d64SJiri Pirko 
8605c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8615c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8625c766d64SJiri Pirko 			err = -EINVAL;
863446266b0SDaniel Borkmann 			goto errout_free;
8645c766d64SJiri Pirko 		}
8655c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8665c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8675c766d64SJiri Pirko 	}
8685c766d64SJiri Pirko 
8695c753978SThomas Graf 	return ifa;
8705c753978SThomas Graf 
871446266b0SDaniel Borkmann errout_free:
872446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8735c753978SThomas Graf errout:
8745c753978SThomas Graf 	return ERR_PTR(err);
8755c753978SThomas Graf }
8765c753978SThomas Graf 
8775c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
8785c766d64SJiri Pirko {
8795c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8805c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8815c766d64SJiri Pirko 
8825c766d64SJiri Pirko 	if (!ifa->ifa_local)
8835c766d64SJiri Pirko 		return NULL;
8845c766d64SJiri Pirko 
8855c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8865c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8875c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8885c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8895c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8905c766d64SJiri Pirko 			return ifa1;
8915c766d64SJiri Pirko 	}
8925c766d64SJiri Pirko 	return NULL;
8935c766d64SJiri Pirko }
8945c766d64SJiri Pirko 
895c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
896c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
8975c753978SThomas Graf {
8983b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8995c753978SThomas Graf 	struct in_ifaddr *ifa;
9005c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9015c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9025c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9035c753978SThomas Graf 
9045c753978SThomas Graf 	ASSERT_RTNL();
9055c753978SThomas Graf 
906dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9075c753978SThomas Graf 	if (IS_ERR(ifa))
9085c753978SThomas Graf 		return PTR_ERR(ifa);
9095c753978SThomas Graf 
9105c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9115c766d64SJiri Pirko 	if (!ifa_existing) {
9125c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
913614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9145c766d64SJiri Pirko 		 */
9155c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
91693a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
91793a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
91893a714d6SMadhu Challa 					       true, ifa);
91993a714d6SMadhu Challa 
92093a714d6SMadhu Challa 			if (ret < 0) {
92193a714d6SMadhu Challa 				inet_free_ifa(ifa);
92293a714d6SMadhu Challa 				return ret;
92393a714d6SMadhu Challa 			}
92493a714d6SMadhu Challa 		}
925de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
926de95e047SDavid Ahern 					 extack);
9275c766d64SJiri Pirko 	} else {
928af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
929af4d768aSDavid Ahern 
9305c766d64SJiri Pirko 		inet_free_ifa(ifa);
9315c766d64SJiri Pirko 
9325c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9335c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9345c766d64SJiri Pirko 			return -EEXIST;
93534e2ed34SJiri Pirko 		ifa = ifa_existing;
936af4d768aSDavid Ahern 
937af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
938af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
939af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
940af4d768aSDavid Ahern 		}
941af4d768aSDavid Ahern 
94234e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
94305a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
944906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
945906e073fSviresh kumar 				&check_lifetime_work, 0);
94634e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9475c766d64SJiri Pirko 	}
9485c766d64SJiri Pirko 	return 0;
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds 
9511da177e4SLinus Torvalds /*
9521da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9531da177e4SLinus Torvalds  */
9541da177e4SLinus Torvalds 
95540384999SEric Dumazet static int inet_abc_len(__be32 addr)
9561da177e4SLinus Torvalds {
9571da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9581da177e4SLinus Torvalds 
95965cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
9601da177e4SLinus Torvalds 		rc = 0;
9611da177e4SLinus Torvalds 	else {
962714e85beSAl Viro 		__u32 haddr = ntohl(addr);
963714e85beSAl Viro 		if (IN_CLASSA(haddr))
9641da177e4SLinus Torvalds 			rc = 8;
965714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9661da177e4SLinus Torvalds 			rc = 16;
967714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9681da177e4SLinus Torvalds 			rc = 24;
96965cab850SDave Taht 		else if (IN_CLASSE(haddr))
97065cab850SDave Taht 			rc = 32;
9711da177e4SLinus Torvalds 	}
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds 	return rc;
9741da177e4SLinus Torvalds }
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds 
97703aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
9781da177e4SLinus Torvalds {
9791da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
98003aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
9811da177e4SLinus Torvalds 	struct in_device *in_dev;
9821da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
9831da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
9841da177e4SLinus Torvalds 	struct net_device *dev;
9851da177e4SLinus Torvalds 	char *colon;
9861da177e4SLinus Torvalds 	int ret = -EFAULT;
9871da177e4SLinus Torvalds 	int tryaddrmatch = 0;
9881da177e4SLinus Torvalds 
98903aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
9901da177e4SLinus Torvalds 
9911da177e4SLinus Torvalds 	/* save original address for comparison */
9921da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9931da177e4SLinus Torvalds 
99403aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
9951da177e4SLinus Torvalds 	if (colon)
9961da177e4SLinus Torvalds 		*colon = 0;
9971da177e4SLinus Torvalds 
99803aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
9991da177e4SLinus Torvalds 
10001da177e4SLinus Torvalds 	switch (cmd) {
10011da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10021da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10031da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10041da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10051da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10061da177e4SLinus Torvalds 		   so that we do not impose a lock.
10071da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10081da177e4SLinus Torvalds 		 */
10091da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10101da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10111da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10121da177e4SLinus Torvalds 		break;
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1015bf5b30b8SZhao Hongjiang 		ret = -EPERM;
101652e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10171da177e4SLinus Torvalds 			goto out;
10181da177e4SLinus Torvalds 		break;
10191da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10201da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10211da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10221da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1023bf5b30b8SZhao Hongjiang 		ret = -EPERM;
102452e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10251da177e4SLinus Torvalds 			goto out;
10261da177e4SLinus Torvalds 		ret = -EINVAL;
10271da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10281da177e4SLinus Torvalds 			goto out;
10291da177e4SLinus Torvalds 		break;
10301da177e4SLinus Torvalds 	default:
10311da177e4SLinus Torvalds 		ret = -EINVAL;
10321da177e4SLinus Torvalds 		goto out;
10331da177e4SLinus Torvalds 	}
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	rtnl_lock();
10361da177e4SLinus Torvalds 
10371da177e4SLinus Torvalds 	ret = -ENODEV;
103803aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10399f9354b9SEric Dumazet 	if (!dev)
10401da177e4SLinus Torvalds 		goto done;
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds 	if (colon)
10431da177e4SLinus Torvalds 		*colon = ':';
10441da177e4SLinus Torvalds 
10459f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10469f9354b9SEric Dumazet 	if (in_dev) {
10471da177e4SLinus Torvalds 		if (tryaddrmatch) {
10481da177e4SLinus Torvalds 			/* Matthias Andree */
10491da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10501da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10511da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10521da177e4SLinus Torvalds 			   This is checked above. */
10531da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10541da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
105503aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10561da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10576c91afe1SDavid S. Miller 							ifa->ifa_local) {
10581da177e4SLinus Torvalds 					break; /* found */
10591da177e4SLinus Torvalds 				}
10601da177e4SLinus Torvalds 			}
10611da177e4SLinus Torvalds 		}
10621da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10631da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10641da177e4SLinus Torvalds 		   comparing just the label */
10651da177e4SLinus Torvalds 		if (!ifa) {
10661da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10671da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
106803aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
10691da177e4SLinus Torvalds 					break;
10701da177e4SLinus Torvalds 		}
10711da177e4SLinus Torvalds 	}
10721da177e4SLinus Torvalds 
10731da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10741da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
10751da177e4SLinus Torvalds 		goto done;
10761da177e4SLinus Torvalds 
10771da177e4SLinus Torvalds 	switch (cmd) {
10781da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
107930e948a3STonghao Zhang 		ret = 0;
10801da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
108103aef17bSAl Viro 		break;
10821da177e4SLinus Torvalds 
10831da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
108430e948a3STonghao Zhang 		ret = 0;
10851da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
108603aef17bSAl Viro 		break;
10871da177e4SLinus Torvalds 
10881da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
108930e948a3STonghao Zhang 		ret = 0;
10901da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
109103aef17bSAl Viro 		break;
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
109430e948a3STonghao Zhang 		ret = 0;
10951da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
109603aef17bSAl Viro 		break;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10991da177e4SLinus Torvalds 		if (colon) {
11001da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11011da177e4SLinus Torvalds 			if (!ifa)
11021da177e4SLinus Torvalds 				break;
11031da177e4SLinus Torvalds 			ret = 0;
110403aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11051da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11061da177e4SLinus Torvalds 			break;
11071da177e4SLinus Torvalds 		}
1108567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11091da177e4SLinus Torvalds 		break;
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11121da177e4SLinus Torvalds 		ret = -EINVAL;
11131da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11141da177e4SLinus Torvalds 			break;
11151da177e4SLinus Torvalds 
11161da177e4SLinus Torvalds 		if (!ifa) {
11171da177e4SLinus Torvalds 			ret = -ENOBUFS;
11189f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11199f9354b9SEric Dumazet 			if (!ifa)
11201da177e4SLinus Torvalds 				break;
1121c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11221da177e4SLinus Torvalds 			if (colon)
112303aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11241da177e4SLinus Torvalds 			else
11251da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11261da177e4SLinus Torvalds 		} else {
11271da177e4SLinus Torvalds 			ret = 0;
11281da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11291da177e4SLinus Torvalds 				break;
11301da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11311da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1132148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11331da177e4SLinus Torvalds 		}
11341da177e4SLinus Torvalds 
11351da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11381da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11391da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11401da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11411da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11421da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11431da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11441da177e4SLinus Torvalds 		} else {
11451da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11461da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11471da177e4SLinus Torvalds 		}
11485c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11491da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11501da177e4SLinus Torvalds 		break;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11531da177e4SLinus Torvalds 		ret = 0;
11541da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11551da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11561da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11571da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11581da177e4SLinus Torvalds 		}
11591da177e4SLinus Torvalds 		break;
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11621da177e4SLinus Torvalds 		ret = 0;
11631da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11641da177e4SLinus Torvalds 			break;
11651da177e4SLinus Torvalds 		ret = -EINVAL;
11661da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11671da177e4SLinus Torvalds 			break;
11681da177e4SLinus Torvalds 		ret = 0;
11691da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11701da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11711da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11721da177e4SLinus Torvalds 		break;
11731da177e4SLinus Torvalds 
11741da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 		/*
11771da177e4SLinus Torvalds 		 *	The mask we set must be legal.
11781da177e4SLinus Torvalds 		 */
11791da177e4SLinus Torvalds 		ret = -EINVAL;
11801da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
11811da177e4SLinus Torvalds 			break;
11821da177e4SLinus Torvalds 		ret = 0;
11831da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1184a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
11851da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11861da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
11871da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
11881da177e4SLinus Torvalds 
11891da177e4SLinus Torvalds 			/* See if current broadcast address matches
11901da177e4SLinus Torvalds 			 * with current netmask, then recalculate
11911da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
11921da177e4SLinus Torvalds 			 * funny address, so don't touch it since
11931da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
11941da177e4SLinus Torvalds 			 */
11951da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11961da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11971da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1198dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11991da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12001da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12011da177e4SLinus Torvalds 			}
12021da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12031da177e4SLinus Torvalds 		}
12041da177e4SLinus Torvalds 		break;
12051da177e4SLinus Torvalds 	}
12061da177e4SLinus Torvalds done:
12071da177e4SLinus Torvalds 	rtnl_unlock();
12081da177e4SLinus Torvalds out:
12091da177e4SLinus Torvalds 	return ret;
12101da177e4SLinus Torvalds }
12111da177e4SLinus Torvalds 
121236fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12131da177e4SLinus Torvalds {
1214e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
12151da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12161da177e4SLinus Torvalds 	struct ifreq ifr;
12171da177e4SLinus Torvalds 	int done = 0;
12181da177e4SLinus Torvalds 
121936fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
122036fd633eSAl Viro 		goto out;
122136fd633eSAl Viro 
12229f9354b9SEric Dumazet 	if (!in_dev)
12231da177e4SLinus Torvalds 		goto out;
12241da177e4SLinus Torvalds 
12259f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12261da177e4SLinus Torvalds 		if (!buf) {
122736fd633eSAl Viro 			done += size;
12281da177e4SLinus Torvalds 			continue;
12291da177e4SLinus Torvalds 		}
123036fd633eSAl Viro 		if (len < size)
12311da177e4SLinus Torvalds 			break;
12321da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12331da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12341da177e4SLinus Torvalds 
12351da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12361da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12371da177e4SLinus Torvalds 								ifa->ifa_local;
12381da177e4SLinus Torvalds 
123936fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12401da177e4SLinus Torvalds 			done = -EFAULT;
12411da177e4SLinus Torvalds 			break;
12421da177e4SLinus Torvalds 		}
124336fd633eSAl Viro 		len  -= size;
124436fd633eSAl Viro 		done += size;
12451da177e4SLinus Torvalds 	}
12461da177e4SLinus Torvalds out:
12471da177e4SLinus Torvalds 	return done;
12481da177e4SLinus Torvalds }
12491da177e4SLinus Torvalds 
12508b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12518b57fd1eSGao Feng 				 int scope)
12528b57fd1eSGao Feng {
12538b57fd1eSGao Feng 	for_primary_ifa(in_dev) {
12548b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12558b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12568b57fd1eSGao Feng 			return ifa->ifa_local;
12578b57fd1eSGao Feng 	} endfor_ifa(in_dev);
12588b57fd1eSGao Feng 
12598b57fd1eSGao Feng 	return 0;
12608b57fd1eSGao Feng }
12618b57fd1eSGao Feng 
1262a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
12631da177e4SLinus Torvalds {
1264a61ced5dSAl Viro 	__be32 addr = 0;
12651da177e4SLinus Torvalds 	struct in_device *in_dev;
1266c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
12673f2fb9a8SDavid Ahern 	int master_idx;
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 	rcu_read_lock();
1270e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
12711da177e4SLinus Torvalds 	if (!in_dev)
12721da177e4SLinus Torvalds 		goto no_in_dev;
12731da177e4SLinus Torvalds 
12741da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
12751da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
12761da177e4SLinus Torvalds 			continue;
12771da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
12781da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12791da177e4SLinus Torvalds 			break;
12801da177e4SLinus Torvalds 		}
12811da177e4SLinus Torvalds 		if (!addr)
12821da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12831da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12841da177e4SLinus Torvalds 
12851da177e4SLinus Torvalds 	if (addr)
1286c6d14c84SEric Dumazet 		goto out_unlock;
12879f9354b9SEric Dumazet no_in_dev:
12883f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
12891da177e4SLinus Torvalds 
129017b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
129117b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
129217b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
129317b693cdSDavid Lamparter 	 * equality check in the loop below.
129417b693cdSDavid Lamparter 	 */
129517b693cdSDavid Lamparter 	if (master_idx &&
129617b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
129717b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
12988b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
12998b57fd1eSGao Feng 		if (addr)
130017b693cdSDavid Lamparter 			goto out_unlock;
130117b693cdSDavid Lamparter 	}
130217b693cdSDavid Lamparter 
13031da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1304ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13051da177e4SLinus Torvalds 	   in dev_base list.
13061da177e4SLinus Torvalds 	 */
1307c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13083f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13093f2fb9a8SDavid Ahern 			continue;
13103f2fb9a8SDavid Ahern 
13119f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13129f9354b9SEric Dumazet 		if (!in_dev)
13131da177e4SLinus Torvalds 			continue;
13141da177e4SLinus Torvalds 
13158b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13168b57fd1eSGao Feng 		if (addr)
1317c6d14c84SEric Dumazet 			goto out_unlock;
13181da177e4SLinus Torvalds 	}
1319c6d14c84SEric Dumazet out_unlock:
13201da177e4SLinus Torvalds 	rcu_read_unlock();
13211da177e4SLinus Torvalds 	return addr;
13221da177e4SLinus Torvalds }
13239f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13241da177e4SLinus Torvalds 
132560cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
132660cad5daSAl Viro 			      __be32 local, int scope)
13271da177e4SLinus Torvalds {
13281da177e4SLinus Torvalds 	int same = 0;
1329a144ea4bSAl Viro 	__be32 addr = 0;
13301da177e4SLinus Torvalds 
13311da177e4SLinus Torvalds 	for_ifa(in_dev) {
13321da177e4SLinus Torvalds 		if (!addr &&
13331da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
13341da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
13351da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13361da177e4SLinus Torvalds 			if (same)
13371da177e4SLinus Torvalds 				break;
13381da177e4SLinus Torvalds 		}
13391da177e4SLinus Torvalds 		if (!same) {
13401da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13411da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13421da177e4SLinus Torvalds 			if (same && addr) {
13431da177e4SLinus Torvalds 				if (local || !dst)
13441da177e4SLinus Torvalds 					break;
13451da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
13461da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
13471da177e4SLinus Torvalds 					break;
13481da177e4SLinus Torvalds 				/* No, then can we use new local src? */
13491da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
13501da177e4SLinus Torvalds 					addr = ifa->ifa_local;
13511da177e4SLinus Torvalds 					break;
13521da177e4SLinus Torvalds 				}
13531da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
13541da177e4SLinus Torvalds 				same = 0;
13551da177e4SLinus Torvalds 			}
13561da177e4SLinus Torvalds 		}
13571da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
13581da177e4SLinus Torvalds 
13591da177e4SLinus Torvalds 	return same ? addr : 0;
13601da177e4SLinus Torvalds }
13611da177e4SLinus Torvalds 
13621da177e4SLinus Torvalds /*
13631da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1364b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1365b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
13661da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
13671da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
13681da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
13691da177e4SLinus Torvalds  */
1370b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
13719bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
13721da177e4SLinus Torvalds {
137360cad5daSAl Viro 	__be32 addr = 0;
13749bd85e32SDenis V. Lunev 	struct net_device *dev;
13751da177e4SLinus Torvalds 
137600db4124SIan Morris 	if (in_dev)
13779bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
13781da177e4SLinus Torvalds 
13791da177e4SLinus Torvalds 	rcu_read_lock();
1380c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13819f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13829f9354b9SEric Dumazet 		if (in_dev) {
13831da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
13841da177e4SLinus Torvalds 			if (addr)
13851da177e4SLinus Torvalds 				break;
13861da177e4SLinus Torvalds 		}
13871da177e4SLinus Torvalds 	}
13881da177e4SLinus Torvalds 	rcu_read_unlock();
13891da177e4SLinus Torvalds 
13901da177e4SLinus Torvalds 	return addr;
13911da177e4SLinus Torvalds }
1392eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
13931da177e4SLinus Torvalds 
13941da177e4SLinus Torvalds /*
13951da177e4SLinus Torvalds  *	Device notifier
13961da177e4SLinus Torvalds  */
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
13991da177e4SLinus Torvalds {
1400e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14011da177e4SLinus Torvalds }
14029f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14031da177e4SLinus Torvalds 
14041da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14051da177e4SLinus Torvalds {
1406e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14071da177e4SLinus Torvalds }
14089f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14091da177e4SLinus Torvalds 
14103ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14113ad7d246SKrister Johansen {
14123ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14133ad7d246SKrister Johansen }
14143ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14153ad7d246SKrister Johansen 
14163ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14173ad7d246SKrister Johansen {
14183ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14193ad7d246SKrister Johansen 	    nb);
14203ad7d246SKrister Johansen }
14213ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14223ad7d246SKrister Johansen 
14239f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14249f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14251da177e4SLinus Torvalds */
14261da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14271da177e4SLinus Torvalds {
14281da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14291da177e4SLinus Torvalds 	int named = 0;
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
14321da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14331da177e4SLinus Torvalds 
14341da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14351da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14361da177e4SLinus Torvalds 		if (named++ == 0)
1437573bf470SThomas Graf 			goto skip;
143844344b2aSMark McLoughlin 		dot = strchr(old, ':');
143951456b29SIan Morris 		if (!dot) {
14401da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14411da177e4SLinus Torvalds 			dot = old;
14421da177e4SLinus Torvalds 		}
14439f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14441da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14459f9354b9SEric Dumazet 		else
14461da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1447573bf470SThomas Graf skip:
1448573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
14491da177e4SLinus Torvalds 	}
14501da177e4SLinus Torvalds }
14511da177e4SLinus Torvalds 
145240384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
145306770843SBreno Leitao {
1454b5476022SEric Dumazet 	return mtu >= IPV4_MIN_MTU;
145506770843SBreno Leitao }
145606770843SBreno Leitao 
1457d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1458d11327adSIan Campbell 					struct in_device *in_dev)
1459d11327adSIan Campbell 
1460d11327adSIan Campbell {
1461b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1462d11327adSIan Campbell 
1463b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1464b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1465d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
14666c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
14676c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1468d11327adSIan Campbell 			 dev->dev_addr, NULL);
1469d11327adSIan Campbell 	}
1470b76d0789SZoltan Kiss }
1471d11327adSIan Campbell 
14721da177e4SLinus Torvalds /* Called only under RTNL semaphore */
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
14751da177e4SLinus Torvalds 			 void *ptr)
14761da177e4SLinus Torvalds {
1477351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1478748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
14791da177e4SLinus Torvalds 
14801da177e4SLinus Torvalds 	ASSERT_RTNL();
14811da177e4SLinus Torvalds 
14821da177e4SLinus Torvalds 	if (!in_dev) {
14838030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
14841da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
148520e61da7SWANG Cong 			if (IS_ERR(in_dev))
148620e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
14870cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
148842f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
148942f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
14901da177e4SLinus Torvalds 			}
149106770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
149206770843SBreno Leitao 			/* Re-enabling IP */
149306770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
149406770843SBreno Leitao 				in_dev = inetdev_init(dev);
14958030f544SHerbert Xu 		}
14961da177e4SLinus Torvalds 		goto out;
14971da177e4SLinus Torvalds 	}
14981da177e4SLinus Torvalds 
14991da177e4SLinus Torvalds 	switch (event) {
15001da177e4SLinus Torvalds 	case NETDEV_REGISTER:
150191df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1502a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15031da177e4SLinus Torvalds 		break;
15041da177e4SLinus Torvalds 	case NETDEV_UP:
150506770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15061da177e4SLinus Torvalds 			break;
15070cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15089f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15099f9354b9SEric Dumazet 
15109f9354b9SEric Dumazet 			if (ifa) {
1511fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15121da177e4SLinus Torvalds 				ifa->ifa_local =
15131da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15141da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15151da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15161da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15171da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15181da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15191da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15205c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15215c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1522dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1523dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15241da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15251da177e4SLinus Torvalds 			}
15261da177e4SLinus Torvalds 		}
15271da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1528eefef1cfSStephen Hemminger 		/* fall through */
1529eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1530d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1531d11327adSIan Campbell 			break;
1532d11327adSIan Campbell 		/* fall through */
1533d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1534a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1535d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15361da177e4SLinus Torvalds 		break;
15371da177e4SLinus Torvalds 	case NETDEV_DOWN:
15381da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15391da177e4SLinus Torvalds 		break;
154093d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
154175c78500SMoni Shoua 		ip_mc_unmap(in_dev);
154275c78500SMoni Shoua 		break;
154393d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
154475c78500SMoni Shoua 		ip_mc_remap(in_dev);
154575c78500SMoni Shoua 		break;
15461da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
154706770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15481da177e4SLinus Torvalds 			break;
154906770843SBreno Leitao 		/* disable IP when MTU is not enough */
1550fcfd6dfaSGustavo A. R. Silva 		/* fall through */
15511da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
15521da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
15531da177e4SLinus Torvalds 		break;
15541da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
15551da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
15561da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
15571da177e4SLinus Torvalds 		 */
15581da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
15591da177e4SLinus Torvalds 
156051602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
156166f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
15621da177e4SLinus Torvalds 		break;
15631da177e4SLinus Torvalds 	}
15641da177e4SLinus Torvalds out:
15651da177e4SLinus Torvalds 	return NOTIFY_DONE;
15661da177e4SLinus Torvalds }
15671da177e4SLinus Torvalds 
15681da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
15691da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
15701da177e4SLinus Torvalds };
15711da177e4SLinus Torvalds 
157240384999SEric Dumazet static size_t inet_nlmsg_size(void)
1573339bf98fSThomas Graf {
1574339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1575339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1576339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1577339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1578ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
157963b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1580af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
158163b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1582339bf98fSThomas Graf }
1583339bf98fSThomas Graf 
15845c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
15855c766d64SJiri Pirko {
15865c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
15875c766d64SJiri Pirko }
15885c766d64SJiri Pirko 
15895c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
15905c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
15915c766d64SJiri Pirko {
15925c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
15935c766d64SJiri Pirko 
15945c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
15955c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
15965c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
15975c766d64SJiri Pirko 	ci.ifa_valid = valid;
15985c766d64SJiri Pirko 
15995c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16005c766d64SJiri Pirko }
16015c766d64SJiri Pirko 
16021da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1603978a46faSChristian Brauner 			    struct inet_fill_args *args)
16041da177e4SLinus Torvalds {
16051da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16061da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16075c766d64SJiri Pirko 	u32 preferred, valid;
16081da177e4SLinus Torvalds 
1609978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1610978a46faSChristian Brauner 			args->flags);
161151456b29SIan Morris 	if (!nlh)
161226932566SPatrick McHardy 		return -EMSGSIZE;
161347f68512SThomas Graf 
161447f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16151da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16161da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16175c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16181da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16191da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16201da177e4SLinus Torvalds 
1621978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1622978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1623d3807145SChristian Brauner 		goto nla_put_failure;
1624d3807145SChristian Brauner 
16255c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16265c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16275c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16285c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16295c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16305c766d64SJiri Pirko 
16315c766d64SJiri Pirko 			if (preferred > tval)
16325c766d64SJiri Pirko 				preferred -= tval;
16335c766d64SJiri Pirko 			else
16345c766d64SJiri Pirko 				preferred = 0;
16355c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16365c766d64SJiri Pirko 				if (valid > tval)
16375c766d64SJiri Pirko 					valid -= tval;
16385c766d64SJiri Pirko 				else
16395c766d64SJiri Pirko 					valid = 0;
16405c766d64SJiri Pirko 			}
16415c766d64SJiri Pirko 		}
16425c766d64SJiri Pirko 	} else {
16435c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16445c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16455c766d64SJiri Pirko 	}
1646f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1647930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1648f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1649930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1650f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1651930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1652f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
16535c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1654ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1655af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1656af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
16575c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
16585c766d64SJiri Pirko 			  preferred, valid))
1659f3756b79SDavid S. Miller 		goto nla_put_failure;
166047f68512SThomas Graf 
1661053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1662053c095aSJohannes Berg 	return 0;
166347f68512SThomas Graf 
166447f68512SThomas Graf nla_put_failure:
166526932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
166626932566SPatrick McHardy 	return -EMSGSIZE;
16671da177e4SLinus Torvalds }
16681da177e4SLinus Torvalds 
1669c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1670c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1671c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
16725fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1673c33078e3SDavid Ahern {
16745fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1675c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1676c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1677c33078e3SDavid Ahern 	int err, i;
1678c33078e3SDavid Ahern 
1679c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1680c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1681c33078e3SDavid Ahern 		return -EINVAL;
1682c33078e3SDavid Ahern 	}
1683c33078e3SDavid Ahern 
1684c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1685c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1686c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1687c33078e3SDavid Ahern 		return -EINVAL;
1688c33078e3SDavid Ahern 	}
16895fcd266aSDavid Ahern 
16905fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
16915fcd266aSDavid Ahern 	if (fillargs->ifindex) {
16925fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
16935fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1694c33078e3SDavid Ahern 	}
1695c33078e3SDavid Ahern 
16968cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1697c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1698c33078e3SDavid Ahern 	if (err < 0)
1699c33078e3SDavid Ahern 		return err;
1700c33078e3SDavid Ahern 
1701c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1702c33078e3SDavid Ahern 		if (!tb[i])
1703c33078e3SDavid Ahern 			continue;
1704c33078e3SDavid Ahern 
1705c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1706c33078e3SDavid Ahern 			struct net *net;
1707c33078e3SDavid Ahern 
1708c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1709c33078e3SDavid Ahern 
1710c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1711c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1712bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1713c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1714c33078e3SDavid Ahern 				return PTR_ERR(net);
1715c33078e3SDavid Ahern 			}
1716c33078e3SDavid Ahern 			*tgt_net = net;
1717c33078e3SDavid Ahern 		} else {
1718c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1719c33078e3SDavid Ahern 			return -EINVAL;
1720c33078e3SDavid Ahern 		}
1721c33078e3SDavid Ahern 	}
1722c33078e3SDavid Ahern 
1723c33078e3SDavid Ahern 	return 0;
1724c33078e3SDavid Ahern }
1725c33078e3SDavid Ahern 
17261c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
17271c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
17281c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
17291c98eca4SDavid Ahern {
17301c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
17311c98eca4SDavid Ahern 	int ip_idx = 0;
17321c98eca4SDavid Ahern 	int err;
17331c98eca4SDavid Ahern 
17341c98eca4SDavid Ahern 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next, ip_idx++) {
17351c98eca4SDavid Ahern 		if (ip_idx < s_ip_idx)
17361c98eca4SDavid Ahern 			continue;
17371c98eca4SDavid Ahern 
17381c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
17391c98eca4SDavid Ahern 		if (err < 0)
17401c98eca4SDavid Ahern 			goto done;
17411c98eca4SDavid Ahern 
17421c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
17431c98eca4SDavid Ahern 	}
17441c98eca4SDavid Ahern 	err = 0;
17451c98eca4SDavid Ahern 
17461c98eca4SDavid Ahern done:
17471c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
17481c98eca4SDavid Ahern 
17491c98eca4SDavid Ahern 	return err;
17501c98eca4SDavid Ahern }
17511c98eca4SDavid Ahern 
17521da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
17531da177e4SLinus Torvalds {
1754c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1755978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1756978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1757c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1758978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1759978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1760978a46faSChristian Brauner 		.netnsid = -1,
1761978a46faSChristian Brauner 	};
17623b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1763d3807145SChristian Brauner 	struct net *tgt_net = net;
1764eec4df98SEric Dumazet 	int h, s_h;
1765eec4df98SEric Dumazet 	int idx, s_idx;
17661c98eca4SDavid Ahern 	int s_ip_idx;
17671da177e4SLinus Torvalds 	struct net_device *dev;
17681da177e4SLinus Torvalds 	struct in_device *in_dev;
1769eec4df98SEric Dumazet 	struct hlist_head *head;
1770d7e38611SDavid Ahern 	int err = 0;
17711da177e4SLinus Torvalds 
1772eec4df98SEric Dumazet 	s_h = cb->args[0];
1773eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
17741c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1775eec4df98SEric Dumazet 
1776c33078e3SDavid Ahern 	if (cb->strict_check) {
1777c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
17785fcd266aSDavid Ahern 						 skb->sk, cb);
1779c33078e3SDavid Ahern 		if (err < 0)
1780d7e38611SDavid Ahern 			goto put_tgt_net;
17815fcd266aSDavid Ahern 
1782d7e38611SDavid Ahern 		err = 0;
17835fcd266aSDavid Ahern 		if (fillargs.ifindex) {
17845fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1785d7e38611SDavid Ahern 			if (!dev) {
1786d7e38611SDavid Ahern 				err = -ENODEV;
1787d7e38611SDavid Ahern 				goto put_tgt_net;
1788d7e38611SDavid Ahern 			}
17895fcd266aSDavid Ahern 
17905fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
17915fcd266aSDavid Ahern 			if (in_dev) {
17925fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
17935fcd266aSDavid Ahern 						       &fillargs);
17945fcd266aSDavid Ahern 			}
17955fcd266aSDavid Ahern 			goto put_tgt_net;
17965fcd266aSDavid Ahern 		}
1797d3807145SChristian Brauner 	}
1798d3807145SChristian Brauner 
1799eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18007562f876SPavel Emelianov 		idx = 0;
1801d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1802eec4df98SEric Dumazet 		rcu_read_lock();
1803d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1804d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1805b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18061da177e4SLinus Torvalds 			if (idx < s_idx)
18077562f876SPavel Emelianov 				goto cont;
18084b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
18091da177e4SLinus Torvalds 				s_ip_idx = 0;
1810eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18119f9354b9SEric Dumazet 			if (!in_dev)
18127562f876SPavel Emelianov 				goto cont;
18131da177e4SLinus Torvalds 
18141c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18151c98eca4SDavid Ahern 					       &fillargs);
18161c98eca4SDavid Ahern 			if (err < 0) {
1817eec4df98SEric Dumazet 				rcu_read_unlock();
18181da177e4SLinus Torvalds 				goto done;
18191da177e4SLinus Torvalds 			}
18207562f876SPavel Emelianov cont:
18217562f876SPavel Emelianov 			idx++;
18221da177e4SLinus Torvalds 		}
1823eec4df98SEric Dumazet 		rcu_read_unlock();
1824eec4df98SEric Dumazet 	}
18251da177e4SLinus Torvalds 
18261da177e4SLinus Torvalds done:
1827eec4df98SEric Dumazet 	cb->args[0] = h;
1828eec4df98SEric Dumazet 	cb->args[1] = idx;
18295fcd266aSDavid Ahern put_tgt_net:
1830978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1831d3807145SChristian Brauner 		put_net(tgt_net);
18321da177e4SLinus Torvalds 
18337c1e8a38SArthur Gautier 	return skb->len ? : err;
18341da177e4SLinus Torvalds }
18351da177e4SLinus Torvalds 
1836d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
183715e47304SEric W. Biederman 		      u32 portid)
18381da177e4SLinus Torvalds {
1839978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1840978a46faSChristian Brauner 		.portid = portid,
1841978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1842978a46faSChristian Brauner 		.event = event,
1843978a46faSChristian Brauner 		.flags = 0,
1844978a46faSChristian Brauner 		.netnsid = -1,
1845978a46faSChristian Brauner 	};
184647f68512SThomas Graf 	struct sk_buff *skb;
1847d6062cbbSThomas Graf 	int err = -ENOBUFS;
18484b8aa9abSDenis V. Lunev 	struct net *net;
18491da177e4SLinus Torvalds 
1850c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1851339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
185251456b29SIan Morris 	if (!skb)
1853d6062cbbSThomas Graf 		goto errout;
1854d6062cbbSThomas Graf 
1855978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
185626932566SPatrick McHardy 	if (err < 0) {
185726932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
185826932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
185926932566SPatrick McHardy 		kfree_skb(skb);
186026932566SPatrick McHardy 		goto errout;
186126932566SPatrick McHardy 	}
186215e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
18631ce85fe4SPablo Neira Ayuso 	return;
1864d6062cbbSThomas Graf errout:
1865d6062cbbSThomas Graf 	if (err < 0)
18664b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
18671da177e4SLinus Torvalds }
18681da177e4SLinus Torvalds 
1869b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1870b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
18719f0f7272SThomas Graf {
18721fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
18739f0f7272SThomas Graf 
18749f0f7272SThomas Graf 	if (!in_dev)
18759f0f7272SThomas Graf 		return 0;
18769f0f7272SThomas Graf 
18779f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
18789f0f7272SThomas Graf }
18799f0f7272SThomas Graf 
1880d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1881d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
18829f0f7272SThomas Graf {
18831fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
18849f0f7272SThomas Graf 	struct nlattr *nla;
18859f0f7272SThomas Graf 	int i;
18869f0f7272SThomas Graf 
18879f0f7272SThomas Graf 	if (!in_dev)
18889f0f7272SThomas Graf 		return -ENODATA;
18899f0f7272SThomas Graf 
18909f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
189151456b29SIan Morris 	if (!nla)
18929f0f7272SThomas Graf 		return -EMSGSIZE;
18939f0f7272SThomas Graf 
18949f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
18959f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
18969f0f7272SThomas Graf 
18979f0f7272SThomas Graf 	return 0;
18989f0f7272SThomas Graf }
18999f0f7272SThomas Graf 
19009f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19019f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19029f0f7272SThomas Graf };
19039f0f7272SThomas Graf 
1904cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1905cf7afbfeSThomas Graf 				 const struct nlattr *nla)
19069f0f7272SThomas Graf {
19079f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19089f0f7272SThomas Graf 	int err, rem;
19099f0f7272SThomas Graf 
19105fa85a09SFlorian Westphal 	if (dev && !__in_dev_get_rcu(dev))
1911cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19129f0f7272SThomas Graf 
19138cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
19148cb08174SJohannes Berg 					  inet_af_policy, NULL);
19159f0f7272SThomas Graf 	if (err < 0)
19169f0f7272SThomas Graf 		return err;
19179f0f7272SThomas Graf 
19189f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19199f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
19209f0f7272SThomas Graf 			int cfgid = nla_type(a);
19219f0f7272SThomas Graf 
19229f0f7272SThomas Graf 			if (nla_len(a) < 4)
19239f0f7272SThomas Graf 				return -EINVAL;
19249f0f7272SThomas Graf 
19259f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
19269f0f7272SThomas Graf 				return -EINVAL;
19279f0f7272SThomas Graf 		}
19289f0f7272SThomas Graf 	}
19299f0f7272SThomas Graf 
1930cf7afbfeSThomas Graf 	return 0;
1931cf7afbfeSThomas Graf }
1932cf7afbfeSThomas Graf 
1933cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1934cf7afbfeSThomas Graf {
19355fa85a09SFlorian Westphal 	struct in_device *in_dev = __in_dev_get_rcu(dev);
1936cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1937cf7afbfeSThomas Graf 	int rem;
1938cf7afbfeSThomas Graf 
1939cf7afbfeSThomas Graf 	if (!in_dev)
1940cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1941cf7afbfeSThomas Graf 
19428cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
1943cf7afbfeSThomas Graf 		BUG();
1944cf7afbfeSThomas Graf 
19459f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19469f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
19479f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
19489f0f7272SThomas Graf 	}
19499f0f7272SThomas Graf 
19509f0f7272SThomas Graf 	return 0;
19519f0f7272SThomas Graf }
19529f0f7272SThomas Graf 
1953edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1954edc9e748SNicolas Dichtel {
1955edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1956edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1957136ba622SZhang Shengju 	bool all = false;
1958edc9e748SNicolas Dichtel 
1959136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1960136ba622SZhang Shengju 		all = true;
1961136ba622SZhang Shengju 
1962136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
1963edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1964136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
1965cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1966136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
1967d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
19685cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
19695cbf777cSXin Long 		size += nla_total_size(4);
1970136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
1971f085ff1cSstephen hemminger 		size += nla_total_size(4);
1972136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
1973974d7af5SAndy Gospodarek 		size += nla_total_size(4);
1974edc9e748SNicolas Dichtel 
1975edc9e748SNicolas Dichtel 	return size;
1976edc9e748SNicolas Dichtel }
1977edc9e748SNicolas Dichtel 
1978edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1979edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1980edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1981edc9e748SNicolas Dichtel 				     int type)
1982edc9e748SNicolas Dichtel {
1983edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1984edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1985136ba622SZhang Shengju 	bool all = false;
1986edc9e748SNicolas Dichtel 
1987edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1988edc9e748SNicolas Dichtel 			flags);
198951456b29SIan Morris 	if (!nlh)
1990edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1991edc9e748SNicolas Dichtel 
1992136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
1993136ba622SZhang Shengju 		all = true;
1994136ba622SZhang Shengju 
1995edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1996edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1997edc9e748SNicolas Dichtel 
1998edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1999edc9e748SNicolas Dichtel 		goto nla_put_failure;
2000edc9e748SNicolas Dichtel 
2001b5c9641dSDavid Ahern 	if (!devconf)
2002b5c9641dSDavid Ahern 		goto out;
2003b5c9641dSDavid Ahern 
2004136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2005edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2006edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2007edc9e748SNicolas Dichtel 		goto nla_put_failure;
2008136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2009cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2010cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2011cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2012136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2013d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2014d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2015d67b8c61SNicolas Dichtel 		goto nla_put_failure;
20165cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
20175cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
20185cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
20195cbf777cSXin Long 		goto nla_put_failure;
2020136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
202109aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2022f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2023f085ff1cSstephen hemminger 		goto nla_put_failure;
2024136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2025974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2026974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2027974d7af5SAndy Gospodarek 		goto nla_put_failure;
2028edc9e748SNicolas Dichtel 
2029b5c9641dSDavid Ahern out:
2030053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2031053c095aSJohannes Berg 	return 0;
2032edc9e748SNicolas Dichtel 
2033edc9e748SNicolas Dichtel nla_put_failure:
2034edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2035edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2036edc9e748SNicolas Dichtel }
2037edc9e748SNicolas Dichtel 
20383b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
20393b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2040edc9e748SNicolas Dichtel {
2041edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2042edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2043edc9e748SNicolas Dichtel 
2044fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
204551456b29SIan Morris 	if (!skb)
2046edc9e748SNicolas Dichtel 		goto errout;
2047edc9e748SNicolas Dichtel 
2048edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
20493b022865SDavid Ahern 					event, 0, type);
2050edc9e748SNicolas Dichtel 	if (err < 0) {
2051edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2052edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2053edc9e748SNicolas Dichtel 		kfree_skb(skb);
2054edc9e748SNicolas Dichtel 		goto errout;
2055edc9e748SNicolas Dichtel 	}
2056fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2057edc9e748SNicolas Dichtel 	return;
2058edc9e748SNicolas Dichtel errout:
2059edc9e748SNicolas Dichtel 	if (err < 0)
2060edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2061edc9e748SNicolas Dichtel }
2062edc9e748SNicolas Dichtel 
20639e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
20649e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
20659e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2066cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
206709aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2068974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
20699e551110SNicolas Dichtel };
20709e551110SNicolas Dichtel 
2071eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2072eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2073eede370dSJakub Kicinski 				      struct nlattr **tb,
2074eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2075eede370dSJakub Kicinski {
2076eede370dSJakub Kicinski 	int i, err;
2077eede370dSJakub Kicinski 
2078eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2079eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2080eede370dSJakub Kicinski 		return -EINVAL;
2081eede370dSJakub Kicinski 	}
2082eede370dSJakub Kicinski 
2083eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
20848cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
20858cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
20868cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2087eede370dSJakub Kicinski 
20888cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
20898cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
20908cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2091eede370dSJakub Kicinski 	if (err)
2092eede370dSJakub Kicinski 		return err;
2093eede370dSJakub Kicinski 
2094eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2095eede370dSJakub Kicinski 		if (!tb[i])
2096eede370dSJakub Kicinski 			continue;
2097eede370dSJakub Kicinski 
2098eede370dSJakub Kicinski 		switch (i) {
2099eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2100eede370dSJakub Kicinski 			break;
2101eede370dSJakub Kicinski 		default:
2102eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2103eede370dSJakub Kicinski 			return -EINVAL;
2104eede370dSJakub Kicinski 		}
2105eede370dSJakub Kicinski 	}
2106eede370dSJakub Kicinski 
2107eede370dSJakub Kicinski 	return 0;
2108eede370dSJakub Kicinski }
2109eede370dSJakub Kicinski 
21109e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2111c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2112c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
21139e551110SNicolas Dichtel {
21149e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
21159e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
21169e551110SNicolas Dichtel 	struct sk_buff *skb;
21179e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
21189e551110SNicolas Dichtel 	struct in_device *in_dev;
21199e551110SNicolas Dichtel 	struct net_device *dev;
21209e551110SNicolas Dichtel 	int ifindex;
21219e551110SNicolas Dichtel 	int err;
21229e551110SNicolas Dichtel 
2123eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2124eede370dSJakub Kicinski 	if (err)
21259e551110SNicolas Dichtel 		goto errout;
21269e551110SNicolas Dichtel 
2127a97eb33fSAnton Protopopov 	err = -EINVAL;
21289e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
21299e551110SNicolas Dichtel 		goto errout;
21309e551110SNicolas Dichtel 
21319e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
21329e551110SNicolas Dichtel 	switch (ifindex) {
21339e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
21349e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
21359e551110SNicolas Dichtel 		break;
21369e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
21379e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
21389e551110SNicolas Dichtel 		break;
21399e551110SNicolas Dichtel 	default:
21409e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
214151456b29SIan Morris 		if (!dev)
21429e551110SNicolas Dichtel 			goto errout;
21439e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
214451456b29SIan Morris 		if (!in_dev)
21459e551110SNicolas Dichtel 			goto errout;
21469e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
21479e551110SNicolas Dichtel 		break;
21489e551110SNicolas Dichtel 	}
21499e551110SNicolas Dichtel 
21509e551110SNicolas Dichtel 	err = -ENOBUFS;
2151fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
215251456b29SIan Morris 	if (!skb)
21539e551110SNicolas Dichtel 		goto errout;
21549e551110SNicolas Dichtel 
21559e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
21569e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
21579e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2158136ba622SZhang Shengju 					NETCONFA_ALL);
21599e551110SNicolas Dichtel 	if (err < 0) {
21609e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
21619e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
21629e551110SNicolas Dichtel 		kfree_skb(skb);
21639e551110SNicolas Dichtel 		goto errout;
21649e551110SNicolas Dichtel 	}
21659e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
21669e551110SNicolas Dichtel errout:
21679e551110SNicolas Dichtel 	return err;
21689e551110SNicolas Dichtel }
21699e551110SNicolas Dichtel 
21707a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
21717a674200SNicolas Dichtel 				     struct netlink_callback *cb)
21727a674200SNicolas Dichtel {
2173addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
21747a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
21757a674200SNicolas Dichtel 	int h, s_h;
21767a674200SNicolas Dichtel 	int idx, s_idx;
21777a674200SNicolas Dichtel 	struct net_device *dev;
21787a674200SNicolas Dichtel 	struct in_device *in_dev;
21797a674200SNicolas Dichtel 	struct hlist_head *head;
21807a674200SNicolas Dichtel 
2181addd383fSDavid Ahern 	if (cb->strict_check) {
2182addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2183addd383fSDavid Ahern 		struct netconfmsg *ncm;
2184addd383fSDavid Ahern 
2185addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2186addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2187addd383fSDavid Ahern 			return -EINVAL;
2188addd383fSDavid Ahern 		}
2189addd383fSDavid Ahern 
2190addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2191addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2192addd383fSDavid Ahern 			return -EINVAL;
2193addd383fSDavid Ahern 		}
2194addd383fSDavid Ahern 	}
2195addd383fSDavid Ahern 
21967a674200SNicolas Dichtel 	s_h = cb->args[0];
21977a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
21987a674200SNicolas Dichtel 
21997a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
22007a674200SNicolas Dichtel 		idx = 0;
22017a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
22027a674200SNicolas Dichtel 		rcu_read_lock();
22030465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
22040465277fSNicolas Dichtel 			  net->dev_base_seq;
22057a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
22067a674200SNicolas Dichtel 			if (idx < s_idx)
22077a674200SNicolas Dichtel 				goto cont;
22087a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
22097a674200SNicolas Dichtel 			if (!in_dev)
22107a674200SNicolas Dichtel 				goto cont;
22117a674200SNicolas Dichtel 
22127a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
22137a674200SNicolas Dichtel 						      &in_dev->cnf,
22147a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2215addd383fSDavid Ahern 						      nlh->nlmsg_seq,
22167a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
22177a674200SNicolas Dichtel 						      NLM_F_MULTI,
2218136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
22197a674200SNicolas Dichtel 				rcu_read_unlock();
22207a674200SNicolas Dichtel 				goto done;
22217a674200SNicolas Dichtel 			}
22220465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
22237a674200SNicolas Dichtel cont:
22247a674200SNicolas Dichtel 			idx++;
22257a674200SNicolas Dichtel 		}
22267a674200SNicolas Dichtel 		rcu_read_unlock();
22277a674200SNicolas Dichtel 	}
22287a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
22297a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
22307a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
22317a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2232addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22337a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2234136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22357a674200SNicolas Dichtel 			goto done;
22367a674200SNicolas Dichtel 		else
22377a674200SNicolas Dichtel 			h++;
22387a674200SNicolas Dichtel 	}
22397a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
22407a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
22417a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
22427a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2243addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22447a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2245136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22467a674200SNicolas Dichtel 			goto done;
22477a674200SNicolas Dichtel 		else
22487a674200SNicolas Dichtel 			h++;
22497a674200SNicolas Dichtel 	}
22507a674200SNicolas Dichtel done:
22517a674200SNicolas Dichtel 	cb->args[0] = h;
22527a674200SNicolas Dichtel 	cb->args[1] = idx;
22537a674200SNicolas Dichtel 
22547a674200SNicolas Dichtel 	return skb->len;
22557a674200SNicolas Dichtel }
22567a674200SNicolas Dichtel 
22571da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
22581da177e4SLinus Torvalds 
2259c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
226031be3085SHerbert Xu {
226131be3085SHerbert Xu 	struct net_device *dev;
226231be3085SHerbert Xu 
226331be3085SHerbert Xu 	rcu_read_lock();
2264c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2265c6d14c84SEric Dumazet 		struct in_device *in_dev;
2266c6d14c84SEric Dumazet 
226731be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
226831be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
22699355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2270c6d14c84SEric Dumazet 	}
227131be3085SHerbert Xu 	rcu_read_unlock();
227231be3085SHerbert Xu }
227331be3085SHerbert Xu 
2274c6d14c84SEric Dumazet /* called with RTNL locked */
2275c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
227668dd299bSPavel Emelyanov {
227768dd299bSPavel Emelyanov 	struct net_device *dev;
2278586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
227968dd299bSPavel Emelyanov 
2280586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
22819355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
22823b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22833b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2284edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2285edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
22863b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
22873b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2288edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2289edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
229068dd299bSPavel Emelyanov 
2291c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
229268dd299bSPavel Emelyanov 		struct in_device *in_dev;
2293fa17806cSEric Dumazet 
22940187bdfbSBen Hutchings 		if (on)
22950187bdfbSBen Hutchings 			dev_disable_lro(dev);
2296fa17806cSEric Dumazet 
2297fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2298edc9e748SNicolas Dichtel 		if (in_dev) {
229968dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23003b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23013b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2302edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2303edc9e748SNicolas Dichtel 		}
230468dd299bSPavel Emelyanov 	}
230568dd299bSPavel Emelyanov }
230668dd299bSPavel Emelyanov 
2307f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2308f085ff1cSstephen hemminger {
2309f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2310f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2311f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2312f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2313f085ff1cSstephen hemminger 	else {
2314f085ff1cSstephen hemminger 		struct in_device *idev
2315f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2316f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2317f085ff1cSstephen hemminger 	}
2318f085ff1cSstephen hemminger }
2319f085ff1cSstephen hemminger 
2320fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
23218d65af78SAlexey Dobriyan 			     void __user *buffer,
232231be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
232331be3085SHerbert Xu {
2324d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23258d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2326d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
232731be3085SHerbert Xu 
232831be3085SHerbert Xu 	if (write) {
232931be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2330c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
233131be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2332f085ff1cSstephen hemminger 		int ifindex;
233331be3085SHerbert Xu 
233431be3085SHerbert Xu 		set_bit(i, cnf->state);
233531be3085SHerbert Xu 
23369355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2337c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2338d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2339d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2340d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
23414ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2342f085ff1cSstephen hemminger 
23435cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
23445cbf777cSXin Long 		    new_value != old_value)
23455cbf777cSXin Long 			rt_cache_flush(net);
23465cbf777cSXin Long 
2347cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2348cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2349f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
23503b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23513b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2352cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2353cc535dfbSNicolas Dichtel 		}
2354f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2355f085ff1cSstephen hemminger 		    new_value != old_value) {
2356f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
23573b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23583b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2359f085ff1cSstephen hemminger 						    ifindex, cnf);
2360f085ff1cSstephen hemminger 		}
2361974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2362974d7af5SAndy Gospodarek 		    new_value != old_value) {
2363974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
23643b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23653b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2366974d7af5SAndy Gospodarek 						    ifindex, cnf);
2367974d7af5SAndy Gospodarek 		}
236831be3085SHerbert Xu 	}
236931be3085SHerbert Xu 
237031be3085SHerbert Xu 	return ret;
237131be3085SHerbert Xu }
237231be3085SHerbert Xu 
2373fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
23748d65af78SAlexey Dobriyan 				  void __user *buffer,
23751da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
23761da177e4SLinus Torvalds {
23771da177e4SLinus Torvalds 	int *valp = ctl->data;
23781da177e4SLinus Torvalds 	int val = *valp;
237988af182eSEric W. Biederman 	loff_t pos = *ppos;
23808d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
23811da177e4SLinus Torvalds 
23821da177e4SLinus Torvalds 	if (write && *valp != val) {
2383c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2384c0ce9fb3SPavel Emelyanov 
23850187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
238688af182eSEric W. Biederman 			if (!rtnl_trylock()) {
238788af182eSEric W. Biederman 				/* Restore the original values before restarting */
238888af182eSEric W. Biederman 				*valp = val;
238988af182eSEric W. Biederman 				*ppos = pos;
23909b8adb5eSEric W. Biederman 				return restart_syscall();
239188af182eSEric W. Biederman 			}
23920187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2393c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2394edc9e748SNicolas Dichtel 			} else {
23950187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
23960187bdfbSBen Hutchings 				struct in_device *idev =
23970187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2398edc9e748SNicolas Dichtel 				if (*valp)
23990187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24003b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2401edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2402edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2403edc9e748SNicolas Dichtel 							    cnf);
24040187bdfbSBen Hutchings 			}
24050187bdfbSBen Hutchings 			rtnl_unlock();
24064ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2407edc9e748SNicolas Dichtel 		} else
24083b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24093b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2410edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2411edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24120187bdfbSBen Hutchings 	}
24131da177e4SLinus Torvalds 
24141da177e4SLinus Torvalds 	return ret;
24151da177e4SLinus Torvalds }
24161da177e4SLinus Torvalds 
2417fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
24188d65af78SAlexey Dobriyan 				void __user *buffer,
24191da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
24201da177e4SLinus Torvalds {
24211da177e4SLinus Torvalds 	int *valp = ctl->data;
24221da177e4SLinus Torvalds 	int val = *valp;
24238d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
242476e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24251da177e4SLinus Torvalds 
24261da177e4SLinus Torvalds 	if (write && *valp != val)
24274ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
24281da177e4SLinus Torvalds 
24291da177e4SLinus Torvalds 	return ret;
24301da177e4SLinus Torvalds }
24311da177e4SLinus Torvalds 
2432f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
243342f811b8SHerbert Xu 	{ \
243442f811b8SHerbert Xu 		.procname	= name, \
243542f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
243602291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
243742f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
243842f811b8SHerbert Xu 		.mode		= mval, \
243942f811b8SHerbert Xu 		.proc_handler	= proc, \
244031be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
244142f811b8SHerbert Xu 	}
244242f811b8SHerbert Xu 
244342f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2444f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
244542f811b8SHerbert Xu 
244642f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2447f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
244842f811b8SHerbert Xu 
2449f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2450f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
245142f811b8SHerbert Xu 
245242f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2453f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
245442f811b8SHerbert Xu 
24551da177e4SLinus Torvalds static struct devinet_sysctl_table {
24561da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
245702291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
24581da177e4SLinus Torvalds } devinet_sysctl = {
24591da177e4SLinus Torvalds 	.devinet_vars = {
246042f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2461f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
246242f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
24635cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
246442f811b8SHerbert Xu 
246542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
246642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
246742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
246842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
246942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
247042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
247142f811b8SHerbert Xu 					"accept_source_route"),
24728153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
247328f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
247442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
247542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
247642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
247742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
247842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
247942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
248042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
248142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
248242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2483eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
248465324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
24855c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
24865c6fe01cSWilliam Manley 					"force_igmp_version"),
24872690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
24882690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
24892690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
24902690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
24910eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
24920eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
249397daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
249497daf331SJohannes Berg 					"drop_gratuitous_arp"),
249542f811b8SHerbert Xu 
249642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
249742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
249842f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
249942f811b8SHerbert Xu 					      "promote_secondaries"),
2500d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2501d0daebc3SThomas Graf 					      "route_localnet"),
250212b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
250312b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25041da177e4SLinus Torvalds 	},
25051da177e4SLinus Torvalds };
25061da177e4SLinus Torvalds 
2507ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
250829c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25091da177e4SLinus Torvalds {
25101da177e4SLinus Torvalds 	int i;
25119fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25128607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2513bfada697SPavel Emelyanov 
25149fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
25151da177e4SLinus Torvalds 	if (!t)
25169fa89642SPavel Emelyanov 		goto out;
25179fa89642SPavel Emelyanov 
25181da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
25191da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
252031be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2521c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25221da177e4SLinus Torvalds 	}
25231da177e4SLinus Torvalds 
25248607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
25251da177e4SLinus Torvalds 
25268607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
25271da177e4SLinus Torvalds 	if (!t->sysctl_header)
25288607ddb8SEric W. Biederman 		goto free;
25291da177e4SLinus Torvalds 
25301da177e4SLinus Torvalds 	p->sysctl = t;
253129c994e3SNicolas Dichtel 
25323b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
25333b022865SDavid Ahern 				    ifindex, p);
2534ea40b324SPavel Emelyanov 	return 0;
25351da177e4SLinus Torvalds 
25361da177e4SLinus Torvalds free:
25371da177e4SLinus Torvalds 	kfree(t);
25389fa89642SPavel Emelyanov out:
2539ea40b324SPavel Emelyanov 	return -ENOBUFS;
25401da177e4SLinus Torvalds }
25411da177e4SLinus Torvalds 
2542b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2543b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
254466f27a52SPavel Emelyanov {
254551602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
254666f27a52SPavel Emelyanov 
2547b5c9641dSDavid Ahern 	if (t) {
254851602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2549ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
25501da177e4SLinus Torvalds 		kfree(t);
25511da177e4SLinus Torvalds 	}
255251602b2aSPavel Emelyanov 
2553b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2554b5c9641dSDavid Ahern }
2555b5c9641dSDavid Ahern 
255620e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
255751602b2aSPavel Emelyanov {
255820e61da7SWANG Cong 	int err;
255920e61da7SWANG Cong 
256020e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
256120e61da7SWANG Cong 		return -EINVAL;
256220e61da7SWANG Cong 
256320e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
256420e61da7SWANG Cong 	if (err)
256520e61da7SWANG Cong 		return err;
256620e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
256729c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
256820e61da7SWANG Cong 	if (err)
256920e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
257020e61da7SWANG Cong 	return err;
257151602b2aSPavel Emelyanov }
257251602b2aSPavel Emelyanov 
257351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
257451602b2aSPavel Emelyanov {
2575b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2576b5c9641dSDavid Ahern 
2577b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
257851602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
25791da177e4SLinus Torvalds }
25801da177e4SLinus Torvalds 
258168dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
258268dd299bSPavel Emelyanov 	{
258368dd299bSPavel Emelyanov 		.procname	= "ip_forward",
258468dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
258502291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
258668dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
258768dd299bSPavel Emelyanov 		.mode		= 0644,
258868dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
258968dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2590c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
259168dd299bSPavel Emelyanov 	},
259268dd299bSPavel Emelyanov 	{ },
259368dd299bSPavel Emelyanov };
25942a75de0cSEric Dumazet #endif
259568dd299bSPavel Emelyanov 
2596752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2597752d14dcSPavel Emelyanov {
2598752d14dcSPavel Emelyanov 	int err;
2599752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26002a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2601856c395cSCong Wang 	struct ctl_table *tbl;
2602752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26032a75de0cSEric Dumazet #endif
2604752d14dcSPavel Emelyanov 
2605752d14dcSPavel Emelyanov 	err = -ENOMEM;
2606856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
260751456b29SIan Morris 	if (!all)
2608752d14dcSPavel Emelyanov 		goto err_alloc_all;
2609752d14dcSPavel Emelyanov 
2610856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
261151456b29SIan Morris 	if (!dflt)
2612752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2613752d14dcSPavel Emelyanov 
26142a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2615856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
261651456b29SIan Morris 	if (!tbl)
2617752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2618752d14dcSPavel Emelyanov 
261902291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2620752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2621752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26222a75de0cSEric Dumazet #endif
2623856c395cSCong Wang 
2624a154d5d8SArnd Bergmann 	if ((!IS_ENABLED(CONFIG_SYSCTL) ||
2625a154d5d8SArnd Bergmann 	     sysctl_devconf_inherit_init_net != 2) &&
2626a154d5d8SArnd Bergmann 	    !net_eq(net, &init_net)) {
2627856c395cSCong Wang 		memcpy(all, init_net.ipv4.devconf_all, sizeof(ipv4_devconf));
2628856c395cSCong Wang 		memcpy(dflt, init_net.ipv4.devconf_dflt, sizeof(ipv4_devconf_dflt));
2629752d14dcSPavel Emelyanov 	}
2630752d14dcSPavel Emelyanov 
2631752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
263229c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2633752d14dcSPavel Emelyanov 	if (err < 0)
2634752d14dcSPavel Emelyanov 		goto err_reg_all;
2635752d14dcSPavel Emelyanov 
263629c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
263729c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2638752d14dcSPavel Emelyanov 	if (err < 0)
2639752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2640752d14dcSPavel Emelyanov 
2641752d14dcSPavel Emelyanov 	err = -ENOMEM;
26428607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
264351456b29SIan Morris 	if (!forw_hdr)
2644752d14dcSPavel Emelyanov 		goto err_reg_ctl;
26452a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2646752d14dcSPavel Emelyanov #endif
2647752d14dcSPavel Emelyanov 
2648752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2649752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2650752d14dcSPavel Emelyanov 	return 0;
2651752d14dcSPavel Emelyanov 
2652752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2653752d14dcSPavel Emelyanov err_reg_ctl:
2654b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2655752d14dcSPavel Emelyanov err_reg_dflt:
2656b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2657752d14dcSPavel Emelyanov err_reg_all:
2658752d14dcSPavel Emelyanov 	kfree(tbl);
2659752d14dcSPavel Emelyanov err_alloc_ctl:
26602a75de0cSEric Dumazet #endif
2661752d14dcSPavel Emelyanov 	kfree(dflt);
2662752d14dcSPavel Emelyanov err_alloc_dflt:
2663752d14dcSPavel Emelyanov 	kfree(all);
2664752d14dcSPavel Emelyanov err_alloc_all:
2665752d14dcSPavel Emelyanov 	return err;
2666752d14dcSPavel Emelyanov }
2667752d14dcSPavel Emelyanov 
2668752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2669752d14dcSPavel Emelyanov {
26702a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2671752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2672752d14dcSPavel Emelyanov 
2673752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2674752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2675b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2676b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2677b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2678b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2679752d14dcSPavel Emelyanov 	kfree(tbl);
26802a75de0cSEric Dumazet #endif
2681752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2682752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2683752d14dcSPavel Emelyanov }
2684752d14dcSPavel Emelyanov 
2685752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2686752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2687752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2688752d14dcSPavel Emelyanov };
2689752d14dcSPavel Emelyanov 
2690207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
26919f0f7272SThomas Graf 	.family		  = AF_INET,
26929f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
26939f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2694cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2695cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
26969f0f7272SThomas Graf };
26979f0f7272SThomas Graf 
26981da177e4SLinus Torvalds void __init devinet_init(void)
26991da177e4SLinus Torvalds {
2700fd23c3b3SDavid S. Miller 	int i;
2701fd23c3b3SDavid S. Miller 
2702fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2703fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2704fd23c3b3SDavid S. Miller 
2705752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2706752d14dcSPavel Emelyanov 
27071da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
27081da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
270963f3444fSThomas Graf 
2710906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
27115c766d64SJiri Pirko 
27129f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
27139f0f7272SThomas Graf 
2714b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2715b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2716b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
27179e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2718b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
27191da177e4SLinus Torvalds }
2720