xref: /linux/net/ipv4/devinet.c (revision 9410645520e9b820069761f3450ef6661418e279)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	NET3	IP device support routines.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
602c30a84SJesper Juhl  * 		Authors:	Ross Biro
71da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
81da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *	Additional Authors:
111da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
121da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Changes:
151da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
161da177e4SLinus Torvalds  *					lists.
171da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
181da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
191da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
201da177e4SLinus Torvalds  *					fall back to comparing just the label
211da177e4SLinus Torvalds  *					if no match found.
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
261da177e4SLinus Torvalds #include <linux/bitops.h>
274fc268d2SRandy Dunlap #include <linux/capability.h>
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds #include <linux/types.h>
301da177e4SLinus Torvalds #include <linux/kernel.h>
31174cd4b1SIngo Molnar #include <linux/sched/signal.h>
321da177e4SLinus Torvalds #include <linux/string.h>
331da177e4SLinus Torvalds #include <linux/mm.h>
341da177e4SLinus Torvalds #include <linux/socket.h>
351da177e4SLinus Torvalds #include <linux/sockios.h>
361da177e4SLinus Torvalds #include <linux/in.h>
371da177e4SLinus Torvalds #include <linux/errno.h>
381da177e4SLinus Torvalds #include <linux/interrupt.h>
391823730fSThomas Graf #include <linux/if_addr.h>
401da177e4SLinus Torvalds #include <linux/if_ether.h>
411da177e4SLinus Torvalds #include <linux/inet.h>
421da177e4SLinus Torvalds #include <linux/netdevice.h>
431da177e4SLinus Torvalds #include <linux/etherdevice.h>
441da177e4SLinus Torvalds #include <linux/skbuff.h>
451da177e4SLinus Torvalds #include <linux/init.h>
461da177e4SLinus Torvalds #include <linux/notifier.h>
471da177e4SLinus Torvalds #include <linux/inetdevice.h>
481da177e4SLinus Torvalds #include <linux/igmp.h>
495a0e3ad6STejun Heo #include <linux/slab.h>
50fd23c3b3SDavid S. Miller #include <linux/hash.h>
511da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
521da177e4SLinus Torvalds #include <linux/sysctl.h>
531da177e4SLinus Torvalds #endif
541da177e4SLinus Torvalds #include <linux/kmod.h>
55edc9e748SNicolas Dichtel #include <linux/netconf.h>
561da177e4SLinus Torvalds 
5714c85021SArnaldo Carvalho de Melo #include <net/arp.h>
581da177e4SLinus Torvalds #include <net/ip.h>
591da177e4SLinus Torvalds #include <net/route.h>
601da177e4SLinus Torvalds #include <net/ip_fib.h>
6163f3444fSThomas Graf #include <net/rtnetlink.h>
62752d14dcSPavel Emelyanov #include <net/net_namespace.h>
635c766d64SJiri Pirko #include <net/addrconf.h>
641da177e4SLinus Torvalds 
652e605463SMatteo Croce #define IPV6ONLY_FLAGS	\
662e605463SMatteo Croce 		(IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \
672e605463SMatteo Croce 		 IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \
682e605463SMatteo Croce 		 IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY)
692e605463SMatteo Croce 
700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7142f811b8SHerbert Xu 	.data = {
7202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
772690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
78fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
7942f811b8SHerbert Xu 	},
801da177e4SLinus Torvalds };
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8342f811b8SHerbert Xu 	.data = {
8402291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8802291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
902690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
91fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
9242f811b8SHerbert Xu 	},
931da177e4SLinus Torvalds };
941da177e4SLinus Torvalds 
959355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
969355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9742f811b8SHerbert Xu 
98ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
995c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
1005c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
1015c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1025176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1035c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
104ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
105af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
106d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
10747f0bd50SJacques de Laval 	[IFA_PROTO]		= { .type = NLA_U8 },
1085c753978SThomas Graf };
1095c753978SThomas Graf 
110978a46faSChristian Brauner struct inet_fill_args {
111978a46faSChristian Brauner 	u32 portid;
112978a46faSChristian Brauner 	u32 seq;
113978a46faSChristian Brauner 	int event;
114978a46faSChristian Brauner 	unsigned int flags;
115978a46faSChristian Brauner 	int netnsid;
1165fcd266aSDavid Ahern 	int ifindex;
117978a46faSChristian Brauner };
118978a46faSChristian Brauner 
11940384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
12040384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
12140384999SEric Dumazet 
122fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
123fd23c3b3SDavid S. Miller 
inet_addr_hash(const struct net * net,__be32 addr)1246eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
125fd23c3b3SDavid S. Miller {
12640384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
127fd23c3b3SDavid S. Miller 
12840384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
129fd23c3b3SDavid S. Miller }
130fd23c3b3SDavid S. Miller 
inet_hash_insert(struct net * net,struct in_ifaddr * ifa)131fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
132fd23c3b3SDavid S. Miller {
13340384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
134fd23c3b3SDavid S. Miller 
13532a4be48SWANG Cong 	ASSERT_RTNL();
136fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
137fd23c3b3SDavid S. Miller }
138fd23c3b3SDavid S. Miller 
inet_hash_remove(struct in_ifaddr * ifa)139fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
140fd23c3b3SDavid S. Miller {
14132a4be48SWANG Cong 	ASSERT_RTNL();
142fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
143fd23c3b3SDavid S. Miller }
144fd23c3b3SDavid S. Miller 
1459435eb1cSDavid S. Miller /**
1469435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1479435eb1cSDavid S. Miller  * @net: the net namespace
1489435eb1cSDavid S. Miller  * @addr: the source address
1499435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1509435eb1cSDavid S. Miller  *
1519435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1529435eb1cSDavid S. Miller  */
__ip_dev_find(struct net * net,__be32 addr,bool devref)1539435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1549435eb1cSDavid S. Miller {
1559435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1569435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1579435eb1cSDavid S. Miller 
1589435eb1cSDavid S. Miller 	rcu_read_lock();
1596e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1606e617de8SPaolo Abeni 	if (!ifa) {
161406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
162406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
163406b6f97SDavid S. Miller 		struct fib_table *local;
164406b6f97SDavid S. Miller 
165406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
166406b6f97SDavid S. Miller 		 * over loopback subnets work.
167406b6f97SDavid S. Miller 		 */
168406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
169406b6f97SDavid S. Miller 		if (local &&
170406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
171406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
172406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1736e617de8SPaolo Abeni 	} else {
1746e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
175406b6f97SDavid S. Miller 	}
1769435eb1cSDavid S. Miller 	if (result && devref)
1779435eb1cSDavid S. Miller 		dev_hold(result);
1789435eb1cSDavid S. Miller 	rcu_read_unlock();
1799435eb1cSDavid S. Miller 	return result;
1809435eb1cSDavid S. Miller }
1819435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1829435eb1cSDavid S. Miller 
1836e617de8SPaolo Abeni /* called under RCU lock */
inet_lookup_ifaddr_rcu(struct net * net,__be32 addr)1846e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1856e617de8SPaolo Abeni {
1866e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1876e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1886e617de8SPaolo Abeni 
1896e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1906e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1916e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1926e617de8SPaolo Abeni 			return ifa;
1936e617de8SPaolo Abeni 
1946e617de8SPaolo Abeni 	return NULL;
1956e617de8SPaolo Abeni }
1966e617de8SPaolo Abeni 
197d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1981da177e4SLinus Torvalds 
199e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
2003ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
2012638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
2022638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
2031da177e4SLinus Torvalds 			 int destroy);
2041da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20651602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20751602b2aSPavel Emelyanov #else
devinet_sysctl_register(struct in_device * idev)20820e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20951602b2aSPavel Emelyanov {
21020e61da7SWANG Cong 	return 0;
21151602b2aSPavel Emelyanov }
devinet_sysctl_unregister(struct in_device * idev)21240384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
21351602b2aSPavel Emelyanov {
21451602b2aSPavel Emelyanov }
2151da177e4SLinus Torvalds #endif
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /* Locks all the inet devices. */
2181da177e4SLinus Torvalds 
inet_alloc_ifa(struct in_device * in_dev)2196e701eb9SKuniyuki Iwashima static struct in_ifaddr *inet_alloc_ifa(struct in_device *in_dev)
2201da177e4SLinus Torvalds {
2216e701eb9SKuniyuki Iwashima 	struct in_ifaddr *ifa;
2226e701eb9SKuniyuki Iwashima 
2236e701eb9SKuniyuki Iwashima 	ifa = kzalloc(sizeof(*ifa), GFP_KERNEL_ACCOUNT);
2246e701eb9SKuniyuki Iwashima 	if (!ifa)
2256e701eb9SKuniyuki Iwashima 		return NULL;
2266e701eb9SKuniyuki Iwashima 
2276e701eb9SKuniyuki Iwashima 	in_dev_hold(in_dev);
2286e701eb9SKuniyuki Iwashima 	ifa->ifa_dev = in_dev;
2296e701eb9SKuniyuki Iwashima 
230*100465a9SKuniyuki Iwashima 	INIT_HLIST_NODE(&ifa->hash);
231*100465a9SKuniyuki Iwashima 
2326e701eb9SKuniyuki Iwashima 	return ifa;
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds 
inet_rcu_free_ifa(struct rcu_head * head)2351da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
23861f5338dSEric Dumazet 
2391da177e4SLinus Torvalds 	in_dev_put(ifa->ifa_dev);
2401da177e4SLinus Torvalds 	kfree(ifa);
2411da177e4SLinus Torvalds }
2421da177e4SLinus Torvalds 
inet_free_ifa(struct in_ifaddr * ifa)24340384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2441da177e4SLinus Torvalds {
24561f5338dSEric Dumazet 	/* Our reference to ifa->ifa_dev must be freed ASAP
24661f5338dSEric Dumazet 	 * to release the reference to the netdev the same way.
24761f5338dSEric Dumazet 	 * in_dev_put() -> in_dev_finish_destroy() -> netdev_put()
24861f5338dSEric Dumazet 	 */
24961f5338dSEric Dumazet 	call_rcu_hurry(&ifa->rcu_head, inet_rcu_free_ifa);
2501da177e4SLinus Torvalds }
2511da177e4SLinus Torvalds 
in_dev_free_rcu(struct rcu_head * head)2529d40c84cSEric Dumazet static void in_dev_free_rcu(struct rcu_head *head)
2539d40c84cSEric Dumazet {
2549d40c84cSEric Dumazet 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2559d40c84cSEric Dumazet 
2569d40c84cSEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2579d40c84cSEric Dumazet 	kfree(idev);
2589d40c84cSEric Dumazet }
2599d40c84cSEric Dumazet 
in_dev_finish_destroy(struct in_device * idev)2601da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2611da177e4SLinus Torvalds {
2621da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2631da177e4SLinus Torvalds 
264547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
265547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2661da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
26791df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2681da177e4SLinus Torvalds #endif
269d62607c3SJakub Kicinski 	netdev_put(dev, &idev->dev_tracker);
2701da177e4SLinus Torvalds 	if (!idev->dead)
2719f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2729f9354b9SEric Dumazet 	else
2739d40c84cSEric Dumazet 		call_rcu(&idev->rcu_head, in_dev_free_rcu);
2741da177e4SLinus Torvalds }
2759f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2761da177e4SLinus Torvalds 
inetdev_init(struct net_device * dev)27771e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2781da177e4SLinus Torvalds {
2791da177e4SLinus Torvalds 	struct in_device *in_dev;
28020e61da7SWANG Cong 	int err = -ENOMEM;
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	ASSERT_RTNL();
2831da177e4SLinus Torvalds 
2840da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2851da177e4SLinus Torvalds 	if (!in_dev)
2861da177e4SLinus Torvalds 		goto out;
287c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2889355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2891da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2901da177e4SLinus Torvalds 	in_dev->dev = dev;
2919f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2929f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2931da177e4SLinus Torvalds 		goto out_kfree;
2940187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2950187bdfbSBen Hutchings 		dev_disable_lro(dev);
2961da177e4SLinus Torvalds 	/* Reference in_dev->dev */
297d62607c3SJakub Kicinski 	netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL);
29830c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2997658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
3001da177e4SLinus Torvalds 
30120e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
30220e61da7SWANG Cong 	if (err) {
30320e61da7SWANG Cong 		in_dev->dead = 1;
3041b49cd71SYang Yingliang 		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
30520e61da7SWANG Cong 		in_dev_put(in_dev);
30620e61da7SWANG Cong 		in_dev = NULL;
30720e61da7SWANG Cong 		goto out;
30820e61da7SWANG Cong 	}
3091da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
3101da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
3111da177e4SLinus Torvalds 		ip_mc_up(in_dev);
312483479ecSJarek Poplawski 
31330c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
314cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
315483479ecSJarek Poplawski out:
31620e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
3171da177e4SLinus Torvalds out_kfree:
3181da177e4SLinus Torvalds 	kfree(in_dev);
3191da177e4SLinus Torvalds 	in_dev = NULL;
3201da177e4SLinus Torvalds 	goto out;
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds 
inetdev_destroy(struct in_device * in_dev)3231da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3241da177e4SLinus Torvalds {
3251da177e4SLinus Torvalds 	struct net_device *dev;
3262638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	ASSERT_RTNL();
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	dev = in_dev->dev;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	in_dev->dead = 1;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3351da177e4SLinus Torvalds 
3362638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3371da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3381da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3391da177e4SLinus Torvalds 	}
3401da177e4SLinus Torvalds 
341a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3421da177e4SLinus Torvalds 
34351602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3441da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3451da177e4SLinus Torvalds 	arp_ifdown(dev);
3461da177e4SLinus Torvalds 
3479d40c84cSEric Dumazet 	in_dev_put(in_dev);
3481da177e4SLinus Torvalds }
3491da177e4SLinus Torvalds 
inet_addr_onlink(struct in_device * in_dev,__be32 a,__be32 b)350ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3511da177e4SLinus Torvalds {
352d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
353d519e870SFlorian Westphal 
3541da177e4SLinus Torvalds 	rcu_read_lock();
355d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3561da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3571da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3581da177e4SLinus Torvalds 				rcu_read_unlock();
3591da177e4SLinus Torvalds 				return 1;
3601da177e4SLinus Torvalds 			}
3611da177e4SLinus Torvalds 		}
362d519e870SFlorian Westphal 	}
3631da177e4SLinus Torvalds 	rcu_read_unlock();
3641da177e4SLinus Torvalds 	return 0;
3651da177e4SLinus Torvalds }
3661da177e4SLinus Torvalds 
__inet_del_ifa(struct in_device * in_dev,struct in_ifaddr __rcu ** ifap,int destroy,struct nlmsghdr * nlh,u32 portid)3672638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3682638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
36915e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3701da177e4SLinus Torvalds {
3718f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3722638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
373ac28b1ecSLiu Jian 	struct in_ifaddr __rcu **last_prim;
3740ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3750ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	ASSERT_RTNL();
3781da177e4SLinus Torvalds 
3792638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
380ac28b1ecSLiu Jian 	last_prim = ifap;
381fbd40ea0SDavid S. Miller 	if (in_dev->dead)
382fbd40ea0SDavid S. Miller 		goto no_promotions;
383fbd40ea0SDavid S. Miller 
3848f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3858f937c60SHarald Welte 	 * unless alias promotion is set
3868f937c60SHarald Welte 	 **/
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3892638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3901da177e4SLinus Torvalds 
3912638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3920ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3930ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
394ac28b1ecSLiu Jian 				last_prim = &ifa->ifa_next;
3950ff60a45SJamal Hadi Salim 
3961da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3971da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3981da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3991da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
4000ff60a45SJamal Hadi Salim 				prev_prom = ifa;
4011da177e4SLinus Torvalds 				continue;
4021da177e4SLinus Torvalds 			}
4031da177e4SLinus Torvalds 
4040ff60a45SJamal Hadi Salim 			if (!do_promote) {
405fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
4061da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
4071da177e4SLinus Torvalds 
40815e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
409e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
410e041c683SAlan Stern 						NETDEV_DOWN, ifa);
4111da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4128f937c60SHarald Welte 			} else {
4138f937c60SHarald Welte 				promote = ifa;
4148f937c60SHarald Welte 				break;
4158f937c60SHarald Welte 			}
4161da177e4SLinus Torvalds 		}
4171da177e4SLinus Torvalds 	}
4181da177e4SLinus Torvalds 
4192d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4202d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4212d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4222d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4232d230e2bSJulian Anastasov 	 */
4242638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4252d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4262d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4272d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4282d230e2bSJulian Anastasov 	}
4292d230e2bSJulian Anastasov 
430fbd40ea0SDavid S. Miller no_promotions:
4311da177e4SLinus Torvalds 	/* 2. Unlink it */
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
434fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4391da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4401da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4411da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4421da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4431da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4441da177e4SLinus Torvalds 	   So that, this order is correct.
4451da177e4SLinus Torvalds 	 */
44615e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
447e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4480ff60a45SJamal Hadi Salim 
4490ff60a45SJamal Hadi Salim 	if (promote) {
4502638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4510ff60a45SJamal Hadi Salim 
4522638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4530ff60a45SJamal Hadi Salim 		if (prev_prom) {
4542638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4552638eb8bSFlorian Westphal 
4562638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4576a9e9ceaSFlorian Westphal 
458ac28b1ecSLiu Jian 			last_sec = rtnl_dereference(*last_prim);
4592638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
460ac28b1ecSLiu Jian 			rcu_assign_pointer(*last_prim, promote);
4610ff60a45SJamal Hadi Salim 		}
4620ff60a45SJamal Hadi Salim 
4630ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
46415e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
465e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
466e041c683SAlan Stern 				NETDEV_UP, promote);
4672638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4682638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4690ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4700ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4710ff60a45SJamal Hadi Salim 					continue;
4720ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4730ff60a45SJamal Hadi Salim 		}
4740ff60a45SJamal Hadi Salim 
4750ff60a45SJamal Hadi Salim 	}
4766363097cSHerbert Xu 	if (destroy)
4771da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4781da177e4SLinus Torvalds }
4791da177e4SLinus Torvalds 
inet_del_ifa(struct in_device * in_dev,struct in_ifaddr __rcu ** ifap,int destroy)4802638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4812638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
482d6062cbbSThomas Graf 			 int destroy)
483d6062cbbSThomas Graf {
484d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
485d6062cbbSThomas Graf }
486d6062cbbSThomas Graf 
4875c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4885c766d64SJiri Pirko 
4895c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4905c766d64SJiri Pirko 
__inet_insert_ifa(struct in_ifaddr * ifa,struct nlmsghdr * nlh,u32 portid,struct netlink_ext_ack * extack)491d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
492de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4931da177e4SLinus Torvalds {
4942638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4951da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4963ad7d246SKrister Johansen 	struct in_validator_info ivi;
4972638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4983ad7d246SKrister Johansen 	int ret;
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	ASSERT_RTNL();
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
5031da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5041da177e4SLinus Torvalds 		return 0;
5051da177e4SLinus Torvalds 	}
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
5081da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
5091da177e4SLinus Torvalds 
5102e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
5112e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
5122e605463SMatteo Croce 
5132638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
5142638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
5152638eb8bSFlorian Westphal 
5162638eb8bSFlorian Westphal 	while (ifa1) {
5171da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
5181da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5191da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5201da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5211da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5221da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5231da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5241da177e4SLinus Torvalds 				return -EEXIST;
5251da177e4SLinus Torvalds 			}
5261da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
527b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value");
5281da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5291da177e4SLinus Torvalds 				return -EINVAL;
5301da177e4SLinus Torvalds 			}
5311da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5321da177e4SLinus Torvalds 		}
5332638eb8bSFlorian Westphal 
5342638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5352638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5361da177e4SLinus Torvalds 	}
5371da177e4SLinus Torvalds 
5383ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5393ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5403ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5413ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5423ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5433ad7d246SKrister Johansen 	 * not the flags.
5443ad7d246SKrister Johansen 	 */
5453ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5463ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
547de95e047SDavid Ahern 	ivi.extack = extack;
5483ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5493ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5503ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5513ad7d246SKrister Johansen 	if (ret) {
5523ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5533ad7d246SKrister Johansen 		return ret;
5543ad7d246SKrister Johansen 	}
5553ad7d246SKrister Johansen 
556d4150779SJason A. Donenfeld 	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
5571da177e4SLinus Torvalds 		ifap = last_primary;
5581da177e4SLinus Torvalds 
5592638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5602638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5611da177e4SLinus Torvalds 
562fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
563fd23c3b3SDavid S. Miller 
5645c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
565906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5665c766d64SJiri Pirko 
5671da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5681da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5691da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
57015e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
571e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	return 0;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
inet_insert_ifa(struct in_ifaddr * ifa)576d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
577d6062cbbSThomas Graf {
578de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
579d6062cbbSThomas Graf }
580d6062cbbSThomas Graf 
inet_set_ifa(struct net_device * dev,struct in_ifaddr * ifa)5811da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5821da177e4SLinus Torvalds {
583e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	ASSERT_RTNL();
5861da177e4SLinus Torvalds 
58771e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5881d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5896e701eb9SKuniyuki Iwashima 
590f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5911da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5921da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5931da177e4SLinus Torvalds }
5941da177e4SLinus Torvalds 
5958723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5968723e1b4SEric Dumazet  * We dont take a reference on found in_device
5978723e1b4SEric Dumazet  */
inetdev_by_index(struct net * net,int ifindex)5987fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5991da177e4SLinus Torvalds {
6001da177e4SLinus Torvalds 	struct net_device *dev;
6011da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
602c148fc2eSEric Dumazet 
603c148fc2eSEric Dumazet 	rcu_read_lock();
604c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
6051da177e4SLinus Torvalds 	if (dev)
6068723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
607c148fc2eSEric Dumazet 	rcu_read_unlock();
6081da177e4SLinus Torvalds 	return in_dev;
6091da177e4SLinus Torvalds }
6109f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6131da177e4SLinus Torvalds 
inet_ifa_byprefix(struct in_device * in_dev,__be32 prefix,__be32 mask)61460cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
61560cad5daSAl Viro 				    __be32 mask)
6161da177e4SLinus Torvalds {
617d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
618d519e870SFlorian Westphal 
6191da177e4SLinus Torvalds 	ASSERT_RTNL();
6201da177e4SLinus Torvalds 
621d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6221da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6231da177e4SLinus Torvalds 			return ifa;
624d519e870SFlorian Westphal 	}
6251da177e4SLinus Torvalds 	return NULL;
6261da177e4SLinus Torvalds }
6271da177e4SLinus Torvalds 
ip_mc_autojoin_config(struct net * net,bool join,const struct in_ifaddr * ifa)628690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
629690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
63093a714d6SMadhu Challa {
631690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
63293a714d6SMadhu Challa 	struct ip_mreqn mreq = {
63393a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
63493a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
63593a714d6SMadhu Challa 	};
636690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
63793a714d6SMadhu Challa 	int ret;
63893a714d6SMadhu Challa 
63993a714d6SMadhu Challa 	ASSERT_RTNL();
64093a714d6SMadhu Challa 
64193a714d6SMadhu Challa 	lock_sock(sk);
64293a714d6SMadhu Challa 	if (join)
64354ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
64493a714d6SMadhu Challa 	else
64554ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
64693a714d6SMadhu Challa 	release_sock(sk);
64793a714d6SMadhu Challa 
64893a714d6SMadhu Challa 	return ret;
649690cc863STaras Chornyi #else
650690cc863STaras Chornyi 	return -EOPNOTSUPP;
651690cc863STaras Chornyi #endif
65293a714d6SMadhu Challa }
65393a714d6SMadhu Challa 
inet_rtm_deladdr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)654c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
655c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6561da177e4SLinus Torvalds {
6573b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6582638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
659dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6601da177e4SLinus Torvalds 	struct in_device *in_dev;
661dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6622638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
66330e2379eSMenglong Dong 	int err;
6641da177e4SLinus Torvalds 
6651da177e4SLinus Torvalds 	ASSERT_RTNL();
6661da177e4SLinus Torvalds 
6678cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6688cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
669dfdd5fd4SThomas Graf 	if (err < 0)
670dfdd5fd4SThomas Graf 		goto errout;
671dfdd5fd4SThomas Graf 
672dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6737fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
67451456b29SIan Morris 	if (!in_dev) {
675b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
676dfdd5fd4SThomas Graf 		err = -ENODEV;
677dfdd5fd4SThomas Graf 		goto errout;
678dfdd5fd4SThomas Graf 	}
679dfdd5fd4SThomas Graf 
6802638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6811da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
682dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
68367b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6841da177e4SLinus Torvalds 			continue;
685dfdd5fd4SThomas Graf 
686dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
687dfdd5fd4SThomas Graf 			continue;
688dfdd5fd4SThomas Graf 
689dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
690dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
69167b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
692dfdd5fd4SThomas Graf 			continue;
693dfdd5fd4SThomas Graf 
69493a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
695690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
69615e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6971da177e4SLinus Torvalds 		return 0;
6981da177e4SLinus Torvalds 	}
699dfdd5fd4SThomas Graf 
700b4672c73SHangbin Liu 	NL_SET_ERR_MSG(extack, "ipv4: Address not found");
701dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
702dfdd5fd4SThomas Graf errout:
703dfdd5fd4SThomas Graf 	return err;
7041da177e4SLinus Torvalds }
7051da177e4SLinus Torvalds 
check_lifetime(struct work_struct * work)7065c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
7075c766d64SJiri Pirko {
7085c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
7095c766d64SJiri Pirko 	struct in_ifaddr *ifa;
710c988d1e8SJiri Pirko 	struct hlist_node *n;
7115c766d64SJiri Pirko 	int i;
7125c766d64SJiri Pirko 
7135c766d64SJiri Pirko 	now = jiffies;
7145c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
7155c766d64SJiri Pirko 
7165c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
717c988d1e8SJiri Pirko 		bool change_needed = false;
718c988d1e8SJiri Pirko 
719c988d1e8SJiri Pirko 		rcu_read_lock();
720b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
7213cd3e72cSEric Dumazet 			unsigned long age, tstamp;
7229f6fa3c4SEric Dumazet 			u32 preferred_lft;
723a5fcf74dSEric Dumazet 			u32 valid_lft;
7243ddc2231SEric Dumazet 			u32 flags;
7255c766d64SJiri Pirko 
7263ddc2231SEric Dumazet 			flags = READ_ONCE(ifa->ifa_flags);
7273ddc2231SEric Dumazet 			if (flags & IFA_F_PERMANENT)
7285c766d64SJiri Pirko 				continue;
7295c766d64SJiri Pirko 
7309f6fa3c4SEric Dumazet 			preferred_lft = READ_ONCE(ifa->ifa_preferred_lft);
731a5fcf74dSEric Dumazet 			valid_lft = READ_ONCE(ifa->ifa_valid_lft);
7323cd3e72cSEric Dumazet 			tstamp = READ_ONCE(ifa->ifa_tstamp);
7335c766d64SJiri Pirko 			/* We try to batch several events at once. */
7343cd3e72cSEric Dumazet 			age = (now - tstamp +
7355c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7365c766d64SJiri Pirko 
737a5fcf74dSEric Dumazet 			if (valid_lft != INFINITY_LIFE_TIME &&
738a5fcf74dSEric Dumazet 			    age >= valid_lft) {
739c988d1e8SJiri Pirko 				change_needed = true;
7409f6fa3c4SEric Dumazet 			} else if (preferred_lft ==
741c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
742c988d1e8SJiri Pirko 				continue;
7439f6fa3c4SEric Dumazet 			} else if (age >= preferred_lft) {
744a5fcf74dSEric Dumazet 				if (time_before(tstamp + valid_lft * HZ, next))
745a5fcf74dSEric Dumazet 					next = tstamp + valid_lft * HZ;
746c988d1e8SJiri Pirko 
7473ddc2231SEric Dumazet 				if (!(flags & IFA_F_DEPRECATED))
748c988d1e8SJiri Pirko 					change_needed = true;
7499f6fa3c4SEric Dumazet 			} else if (time_before(tstamp + preferred_lft * HZ,
750c988d1e8SJiri Pirko 					       next)) {
7519f6fa3c4SEric Dumazet 				next = tstamp + preferred_lft * HZ;
752c988d1e8SJiri Pirko 			}
753c988d1e8SJiri Pirko 		}
754c988d1e8SJiri Pirko 		rcu_read_unlock();
755c988d1e8SJiri Pirko 		if (!change_needed)
756c988d1e8SJiri Pirko 			continue;
757c988d1e8SJiri Pirko 		rtnl_lock();
758c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
759c988d1e8SJiri Pirko 			unsigned long age;
760c988d1e8SJiri Pirko 
761c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
762c988d1e8SJiri Pirko 				continue;
763c988d1e8SJiri Pirko 
764c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
765c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
766c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
767c988d1e8SJiri Pirko 
768c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
769c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7702638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7712638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7725c766d64SJiri Pirko 
7732638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7742638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7752638eb8bSFlorian Westphal 				while (tmp) {
77640008e92SFlorian Westphal 					if (tmp == ifa) {
7775c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7785c766d64SJiri Pirko 							     ifap, 1);
779c988d1e8SJiri Pirko 						break;
7805c766d64SJiri Pirko 					}
7812638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7822638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
783c988d1e8SJiri Pirko 				}
784c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
785c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
786c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
787c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7885c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7895c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7905c766d64SJiri Pirko 			}
7915c766d64SJiri Pirko 		}
792c988d1e8SJiri Pirko 		rtnl_unlock();
7935c766d64SJiri Pirko 	}
7945c766d64SJiri Pirko 
7955c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7965c766d64SJiri Pirko 	next_sched = next;
7975c766d64SJiri Pirko 
7985c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7995c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
8005c766d64SJiri Pirko 		next_sched = next_sec;
8015c766d64SJiri Pirko 
8025c766d64SJiri Pirko 	now = jiffies;
8035c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
8045c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
8055c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
8065c766d64SJiri Pirko 
807906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
808906e073fSviresh kumar 			next_sched - now);
8095c766d64SJiri Pirko }
8105c766d64SJiri Pirko 
set_ifa_lifetime(struct in_ifaddr * ifa,__u32 valid_lft,__u32 prefered_lft)8115c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
8125c766d64SJiri Pirko 			     __u32 prefered_lft)
8135c766d64SJiri Pirko {
8145c766d64SJiri Pirko 	unsigned long timeout;
8153ddc2231SEric Dumazet 	u32 flags;
8165c766d64SJiri Pirko 
8173ddc2231SEric Dumazet 	flags = ifa->ifa_flags & ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
8185c766d64SJiri Pirko 
8195c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
8205c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
821a5fcf74dSEric Dumazet 		WRITE_ONCE(ifa->ifa_valid_lft, timeout);
8225c766d64SJiri Pirko 	else
8233ddc2231SEric Dumazet 		flags |= IFA_F_PERMANENT;
8245c766d64SJiri Pirko 
8255c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8265c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8275c766d64SJiri Pirko 		if (timeout == 0)
8283ddc2231SEric Dumazet 			flags |= IFA_F_DEPRECATED;
8299f6fa3c4SEric Dumazet 		WRITE_ONCE(ifa->ifa_preferred_lft, timeout);
8305c766d64SJiri Pirko 	}
8313ddc2231SEric Dumazet 	WRITE_ONCE(ifa->ifa_flags, flags);
8323cd3e72cSEric Dumazet 	WRITE_ONCE(ifa->ifa_tstamp, jiffies);
8335c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
8343cd3e72cSEric Dumazet 		WRITE_ONCE(ifa->ifa_cstamp, ifa->ifa_tstamp);
8355c766d64SJiri Pirko }
8365c766d64SJiri Pirko 
rtm_to_ifaddr(struct net * net,struct nlmsghdr * nlh,__u32 * pvalid_lft,__u32 * pprefered_lft,struct netlink_ext_ack * extack)8375c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
838dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
839dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8401da177e4SLinus Torvalds {
8415c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8425c753978SThomas Graf 	struct in_ifaddr *ifa;
8435c753978SThomas Graf 	struct ifaddrmsg *ifm;
8441da177e4SLinus Torvalds 	struct net_device *dev;
8451da177e4SLinus Torvalds 	struct in_device *in_dev;
8467b218574SDenis V. Lunev 	int err;
8471da177e4SLinus Torvalds 
8488cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8498cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8505c753978SThomas Graf 	if (err < 0)
8515c753978SThomas Graf 		goto errout;
8521da177e4SLinus Torvalds 
8535c753978SThomas Graf 	ifm = nlmsg_data(nlh);
854c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
855b4672c73SHangbin Liu 
856b4672c73SHangbin Liu 	if (ifm->ifa_prefixlen > 32) {
857b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length");
8585c753978SThomas Graf 		goto errout;
859b4672c73SHangbin Liu 	}
860b4672c73SHangbin Liu 
861b4672c73SHangbin Liu 	if (!tb[IFA_LOCAL]) {
862b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied");
863b4672c73SHangbin Liu 		goto errout;
864b4672c73SHangbin Liu 	}
8651da177e4SLinus Torvalds 
8664b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8675c753978SThomas Graf 	err = -ENODEV;
868b4672c73SHangbin Liu 	if (!dev) {
869b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
8705c753978SThomas Graf 		goto errout;
871b4672c73SHangbin Liu 	}
8721da177e4SLinus Torvalds 
8735c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8745c753978SThomas Graf 	err = -ENOBUFS;
87551456b29SIan Morris 	if (!in_dev)
8765c753978SThomas Graf 		goto errout;
87771e27da9SHerbert Xu 
8786e701eb9SKuniyuki Iwashima 	ifa = inet_alloc_ifa(in_dev);
87951456b29SIan Morris 	if (!ifa)
8805c753978SThomas Graf 		/*
8815c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8825c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8835c753978SThomas Graf 		 */
8845c753978SThomas Graf 		goto errout;
8855c753978SThomas Graf 
886a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8871d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8885c753978SThomas Graf 
88951456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8905c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8915c753978SThomas Graf 
8921da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8931da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
894ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
895ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8961da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
89767b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
89867b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8995c753978SThomas Graf 
9005c753978SThomas Graf 	if (tb[IFA_BROADCAST])
90167b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
9025c753978SThomas Graf 
9035c753978SThomas Graf 	if (tb[IFA_LABEL])
904872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
9051da177e4SLinus Torvalds 	else
9061da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
9071da177e4SLinus Torvalds 
908af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
909af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
910af4d768aSDavid Ahern 
91147f0bd50SJacques de Laval 	if (tb[IFA_PROTO])
91247f0bd50SJacques de Laval 		ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
91347f0bd50SJacques de Laval 
9145c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
9155c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
9165c766d64SJiri Pirko 
9175c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
9185c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
919b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid");
9205c766d64SJiri Pirko 			err = -EINVAL;
921446266b0SDaniel Borkmann 			goto errout_free;
9225c766d64SJiri Pirko 		}
9235c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
9245c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9255c766d64SJiri Pirko 	}
9265c766d64SJiri Pirko 
9275c753978SThomas Graf 	return ifa;
9285c753978SThomas Graf 
929446266b0SDaniel Borkmann errout_free:
930446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9315c753978SThomas Graf errout:
9325c753978SThomas Graf 	return ERR_PTR(err);
9335c753978SThomas Graf }
9345c753978SThomas Graf 
find_matching_ifa(struct in_ifaddr * ifa)9355c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9365c766d64SJiri Pirko {
9375c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
938ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9395c766d64SJiri Pirko 
9405c766d64SJiri Pirko 	if (!ifa->ifa_local)
9415c766d64SJiri Pirko 		return NULL;
9425c766d64SJiri Pirko 
943ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9445c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9455c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9465c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9475c766d64SJiri Pirko 			return ifa1;
9485c766d64SJiri Pirko 	}
9495c766d64SJiri Pirko 	return NULL;
9505c766d64SJiri Pirko }
9515c766d64SJiri Pirko 
inet_rtm_newaddr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)952c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
953c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9545c753978SThomas Graf {
9553b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9565c753978SThomas Graf 	struct in_ifaddr *ifa;
9575c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9585c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9595c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9605c753978SThomas Graf 
9615c753978SThomas Graf 	ASSERT_RTNL();
9625c753978SThomas Graf 
963dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9645c753978SThomas Graf 	if (IS_ERR(ifa))
9655c753978SThomas Graf 		return PTR_ERR(ifa);
9665c753978SThomas Graf 
9675c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9685c766d64SJiri Pirko 	if (!ifa_existing) {
9695c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
970614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9715c766d64SJiri Pirko 		 */
9725c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
97393a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
974690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
97593a714d6SMadhu Challa 
97693a714d6SMadhu Challa 			if (ret < 0) {
977b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed");
97893a714d6SMadhu Challa 				inet_free_ifa(ifa);
97993a714d6SMadhu Challa 				return ret;
98093a714d6SMadhu Challa 			}
98193a714d6SMadhu Challa 		}
982de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
983de95e047SDavid Ahern 					 extack);
9845c766d64SJiri Pirko 	} else {
985af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
9865c4a9aa8SPetr Machata 		u8 new_proto = ifa->ifa_proto;
987af4d768aSDavid Ahern 
9885c766d64SJiri Pirko 		inet_free_ifa(ifa);
9895c766d64SJiri Pirko 
9905c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
991b4672c73SHangbin Liu 		    !(nlh->nlmsg_flags & NLM_F_REPLACE)) {
992b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: Address already assigned");
9935c766d64SJiri Pirko 			return -EEXIST;
994b4672c73SHangbin Liu 		}
99534e2ed34SJiri Pirko 		ifa = ifa_existing;
996af4d768aSDavid Ahern 
997af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
998af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
999af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
1000af4d768aSDavid Ahern 		}
1001af4d768aSDavid Ahern 
10025c4a9aa8SPetr Machata 		ifa->ifa_proto = new_proto;
10035c4a9aa8SPetr Machata 
100434e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
100505a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
1006906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
1007906e073fSviresh kumar 				&check_lifetime_work, 0);
100834e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
10095c766d64SJiri Pirko 	}
10105c766d64SJiri Pirko 	return 0;
10111da177e4SLinus Torvalds }
10121da177e4SLinus Torvalds 
10131da177e4SLinus Torvalds /*
10141da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
10151da177e4SLinus Torvalds  */
10161da177e4SLinus Torvalds 
inet_abc_len(__be32 addr)101740384999SEric Dumazet static int inet_abc_len(__be32 addr)
10181da177e4SLinus Torvalds {
10191da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
10201da177e4SLinus Torvalds 
102165cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
10221da177e4SLinus Torvalds 		rc = 0;
10231da177e4SLinus Torvalds 	else {
1024714e85beSAl Viro 		__u32 haddr = ntohl(addr);
1025714e85beSAl Viro 		if (IN_CLASSA(haddr))
10261da177e4SLinus Torvalds 			rc = 8;
1027714e85beSAl Viro 		else if (IN_CLASSB(haddr))
10281da177e4SLinus Torvalds 			rc = 16;
1029714e85beSAl Viro 		else if (IN_CLASSC(haddr))
10301da177e4SLinus Torvalds 			rc = 24;
103165cab850SDave Taht 		else if (IN_CLASSE(haddr))
103265cab850SDave Taht 			rc = 32;
10331da177e4SLinus Torvalds 	}
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	return rc;
10361da177e4SLinus Torvalds }
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds 
devinet_ioctl(struct net * net,unsigned int cmd,struct ifreq * ifr)103903aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10401da177e4SLinus Torvalds {
10411da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
104203aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10432638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10441da177e4SLinus Torvalds 	struct in_device *in_dev;
10451da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10461da177e4SLinus Torvalds 	struct net_device *dev;
10471da177e4SLinus Torvalds 	char *colon;
10481da177e4SLinus Torvalds 	int ret = -EFAULT;
10491da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10501da177e4SLinus Torvalds 
105103aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10521da177e4SLinus Torvalds 
10531da177e4SLinus Torvalds 	/* save original address for comparison */
10541da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10551da177e4SLinus Torvalds 
105603aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10571da177e4SLinus Torvalds 	if (colon)
10581da177e4SLinus Torvalds 		*colon = 0;
10591da177e4SLinus Torvalds 
106003aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10611da177e4SLinus Torvalds 
10621da177e4SLinus Torvalds 	switch (cmd) {
10631da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10641da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10651da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10661da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10671da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10681da177e4SLinus Torvalds 		   so that we do not impose a lock.
10691da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10701da177e4SLinus Torvalds 		 */
10711da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10721da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10731da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10741da177e4SLinus Torvalds 		break;
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1077bf5b30b8SZhao Hongjiang 		ret = -EPERM;
107852e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10791da177e4SLinus Torvalds 			goto out;
10801da177e4SLinus Torvalds 		break;
10811da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10821da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10831da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10841da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1085bf5b30b8SZhao Hongjiang 		ret = -EPERM;
108652e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10871da177e4SLinus Torvalds 			goto out;
10881da177e4SLinus Torvalds 		ret = -EINVAL;
10891da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10901da177e4SLinus Torvalds 			goto out;
10911da177e4SLinus Torvalds 		break;
10921da177e4SLinus Torvalds 	default:
10931da177e4SLinus Torvalds 		ret = -EINVAL;
10941da177e4SLinus Torvalds 		goto out;
10951da177e4SLinus Torvalds 	}
10961da177e4SLinus Torvalds 
10971da177e4SLinus Torvalds 	rtnl_lock();
10981da177e4SLinus Torvalds 
10991da177e4SLinus Torvalds 	ret = -ENODEV;
110003aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
11019f9354b9SEric Dumazet 	if (!dev)
11021da177e4SLinus Torvalds 		goto done;
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	if (colon)
11051da177e4SLinus Torvalds 		*colon = ':';
11061da177e4SLinus Torvalds 
11079f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
11089f9354b9SEric Dumazet 	if (in_dev) {
11091da177e4SLinus Torvalds 		if (tryaddrmatch) {
11101da177e4SLinus Torvalds 			/* Matthias Andree */
11111da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
11121da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
11131da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
11141da177e4SLinus Torvalds 			   This is checked above. */
11152638eb8bSFlorian Westphal 
11162638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11172638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11181da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
111903aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
11201da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
11216c91afe1SDavid S. Miller 							ifa->ifa_local) {
11221da177e4SLinus Torvalds 					break; /* found */
11231da177e4SLinus Torvalds 				}
11241da177e4SLinus Torvalds 			}
11251da177e4SLinus Torvalds 		}
11261da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
11271da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
11281da177e4SLinus Torvalds 		   comparing just the label */
11291da177e4SLinus Torvalds 		if (!ifa) {
11302638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11312638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11321da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
113303aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11341da177e4SLinus Torvalds 					break;
11351da177e4SLinus Torvalds 		}
11361da177e4SLinus Torvalds 	}
11371da177e4SLinus Torvalds 
11381da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11391da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11401da177e4SLinus Torvalds 		goto done;
11411da177e4SLinus Torvalds 
11421da177e4SLinus Torvalds 	switch (cmd) {
11431da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
114430e948a3STonghao Zhang 		ret = 0;
11451da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
114603aef17bSAl Viro 		break;
11471da177e4SLinus Torvalds 
11481da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
114930e948a3STonghao Zhang 		ret = 0;
11501da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
115103aef17bSAl Viro 		break;
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
115430e948a3STonghao Zhang 		ret = 0;
11551da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
115603aef17bSAl Viro 		break;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
115930e948a3STonghao Zhang 		ret = 0;
11601da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
116103aef17bSAl Viro 		break;
11621da177e4SLinus Torvalds 
11631da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11641da177e4SLinus Torvalds 		if (colon) {
11651da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11661da177e4SLinus Torvalds 			if (!ifa)
11671da177e4SLinus Torvalds 				break;
11681da177e4SLinus Torvalds 			ret = 0;
116903aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11701da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11711da177e4SLinus Torvalds 			break;
11721da177e4SLinus Torvalds 		}
1173567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11741da177e4SLinus Torvalds 		break;
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11771da177e4SLinus Torvalds 		ret = -EINVAL;
11781da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11791da177e4SLinus Torvalds 			break;
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds 		if (!ifa) {
11821da177e4SLinus Torvalds 			ret = -ENOBUFS;
1183e3af3d3cSKuniyuki Iwashima 			if (!in_dev)
1184e3af3d3cSKuniyuki Iwashima 				break;
11856e701eb9SKuniyuki Iwashima 			ifa = inet_alloc_ifa(in_dev);
11869f9354b9SEric Dumazet 			if (!ifa)
11871da177e4SLinus Torvalds 				break;
1188*100465a9SKuniyuki Iwashima 
11891da177e4SLinus Torvalds 			if (colon)
119003aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11911da177e4SLinus Torvalds 			else
11921da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11931da177e4SLinus Torvalds 		} else {
11941da177e4SLinus Torvalds 			ret = 0;
11951da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11961da177e4SLinus Torvalds 				break;
11971da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11981da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1199148f9729SBjorn Mork 			ifa->ifa_scope = 0;
12001da177e4SLinus Torvalds 		}
12011da177e4SLinus Torvalds 
12021da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
12051da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
12061da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
12071da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12081da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
12091da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
12101da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
12111da177e4SLinus Torvalds 		} else {
12121da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
12131da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
12141da177e4SLinus Torvalds 		}
12155c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
12161da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
12171da177e4SLinus Torvalds 		break;
12181da177e4SLinus Torvalds 
12191da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
12201da177e4SLinus Torvalds 		ret = 0;
12211da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
12221da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12231da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
12241da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12251da177e4SLinus Torvalds 		}
12261da177e4SLinus Torvalds 		break;
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
12291da177e4SLinus Torvalds 		ret = 0;
12301da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
12311da177e4SLinus Torvalds 			break;
12321da177e4SLinus Torvalds 		ret = -EINVAL;
12331da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12341da177e4SLinus Torvalds 			break;
12351da177e4SLinus Torvalds 		ret = 0;
12361da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12371da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12381da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12391da177e4SLinus Torvalds 		break;
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12421da177e4SLinus Torvalds 
12431da177e4SLinus Torvalds 		/*
12441da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12451da177e4SLinus Torvalds 		 */
12461da177e4SLinus Torvalds 		ret = -EINVAL;
12471da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12481da177e4SLinus Torvalds 			break;
12491da177e4SLinus Torvalds 		ret = 0;
12501da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1251a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12521da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12531da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12541da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12551da177e4SLinus Torvalds 
12561da177e4SLinus Torvalds 			/* See if current broadcast address matches
12571da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12581da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12591da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12601da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12611da177e4SLinus Torvalds 			 */
12621da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12631da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12641da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1265dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12661da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12671da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12681da177e4SLinus Torvalds 			}
12691da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12701da177e4SLinus Torvalds 		}
12711da177e4SLinus Torvalds 		break;
12721da177e4SLinus Torvalds 	}
12731da177e4SLinus Torvalds done:
12741da177e4SLinus Torvalds 	rtnl_unlock();
12751da177e4SLinus Torvalds out:
12761da177e4SLinus Torvalds 	return ret;
12771da177e4SLinus Torvalds }
12781da177e4SLinus Torvalds 
inet_gifconf(struct net_device * dev,char __user * buf,int len,int size)1279b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12801da177e4SLinus Torvalds {
1281e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1282ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12831da177e4SLinus Torvalds 	struct ifreq ifr;
12841da177e4SLinus Torvalds 	int done = 0;
12851da177e4SLinus Torvalds 
128636fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
128736fd633eSAl Viro 		goto out;
128836fd633eSAl Viro 
12899f9354b9SEric Dumazet 	if (!in_dev)
12901da177e4SLinus Torvalds 		goto out;
12911da177e4SLinus Torvalds 
1292ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12931da177e4SLinus Torvalds 		if (!buf) {
129436fd633eSAl Viro 			done += size;
12951da177e4SLinus Torvalds 			continue;
12961da177e4SLinus Torvalds 		}
129736fd633eSAl Viro 		if (len < size)
12981da177e4SLinus Torvalds 			break;
12991da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
13001da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
13011da177e4SLinus Torvalds 
13021da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
13031da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
13041da177e4SLinus Torvalds 								ifa->ifa_local;
13051da177e4SLinus Torvalds 
130636fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
13071da177e4SLinus Torvalds 			done = -EFAULT;
13081da177e4SLinus Torvalds 			break;
13091da177e4SLinus Torvalds 		}
131036fd633eSAl Viro 		len  -= size;
131136fd633eSAl Viro 		done += size;
13121da177e4SLinus Torvalds 	}
13131da177e4SLinus Torvalds out:
13141da177e4SLinus Torvalds 	return done;
13151da177e4SLinus Torvalds }
13161da177e4SLinus Torvalds 
in_dev_select_addr(const struct in_device * in_dev,int scope)13178b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
13188b57fd1eSGao Feng 				 int scope)
13198b57fd1eSGao Feng {
1320d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1321d519e870SFlorian Westphal 
1322d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
13233ddc2231SEric Dumazet 		if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY)
1324d519e870SFlorian Westphal 			continue;
13258b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
13268b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
13278b57fd1eSGao Feng 			return ifa->ifa_local;
1328d519e870SFlorian Westphal 	}
13298b57fd1eSGao Feng 
13308b57fd1eSGao Feng 	return 0;
13318b57fd1eSGao Feng }
13328b57fd1eSGao Feng 
inet_select_addr(const struct net_device * dev,__be32 dst,int scope)1333a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13341da177e4SLinus Torvalds {
1335d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1336a61ced5dSAl Viro 	__be32 addr = 0;
1337d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13381da177e4SLinus Torvalds 	struct in_device *in_dev;
1339c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13403f2fb9a8SDavid Ahern 	int master_idx;
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 	rcu_read_lock();
1343e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13441da177e4SLinus Torvalds 	if (!in_dev)
13451da177e4SLinus Torvalds 		goto no_in_dev;
13461da177e4SLinus Torvalds 
1347d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1348d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1349d8c444d5SShijie Luo 
1350d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
13513ddc2231SEric Dumazet 		if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY)
1352d519e870SFlorian Westphal 			continue;
1353d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13541da177e4SLinus Torvalds 			continue;
13551da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13561da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13571da177e4SLinus Torvalds 			break;
13581da177e4SLinus Torvalds 		}
13591da177e4SLinus Torvalds 		if (!addr)
13601da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1361d519e870SFlorian Westphal 	}
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 	if (addr)
1364c6d14c84SEric Dumazet 		goto out_unlock;
13659f9354b9SEric Dumazet no_in_dev:
13663f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13671da177e4SLinus Torvalds 
136817b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
136917b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
137017b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
137117b693cdSDavid Lamparter 	 * equality check in the loop below.
137217b693cdSDavid Lamparter 	 */
137317b693cdSDavid Lamparter 	if (master_idx &&
137417b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
137517b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13768b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13778b57fd1eSGao Feng 		if (addr)
137817b693cdSDavid Lamparter 			goto out_unlock;
137917b693cdSDavid Lamparter 	}
138017b693cdSDavid Lamparter 
13811da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1382ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13831da177e4SLinus Torvalds 	   in dev_base list.
13841da177e4SLinus Torvalds 	 */
1385c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13863f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13873f2fb9a8SDavid Ahern 			continue;
13883f2fb9a8SDavid Ahern 
13899f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13909f9354b9SEric Dumazet 		if (!in_dev)
13911da177e4SLinus Torvalds 			continue;
13921da177e4SLinus Torvalds 
13938b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13948b57fd1eSGao Feng 		if (addr)
1395c6d14c84SEric Dumazet 			goto out_unlock;
13961da177e4SLinus Torvalds 	}
1397c6d14c84SEric Dumazet out_unlock:
13981da177e4SLinus Torvalds 	rcu_read_unlock();
13991da177e4SLinus Torvalds 	return addr;
14001da177e4SLinus Torvalds }
14019f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
14021da177e4SLinus Torvalds 
confirm_addr_indev(struct in_device * in_dev,__be32 dst,__be32 local,int scope)140360cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
140460cad5daSAl Viro 			      __be32 local, int scope)
14051da177e4SLinus Torvalds {
1406650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1407ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1408a144ea4bSAl Viro 	__be32 addr = 0;
1409ef11db33SFlorian Westphal 	int same = 0;
14101da177e4SLinus Torvalds 
1411650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1412650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1413650638a7SShijie Luo 
1414ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1415650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1416650638a7SShijie Luo 
14171da177e4SLinus Torvalds 		if (!addr &&
14181da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1419650638a7SShijie Luo 		    min_scope <= scope) {
14201da177e4SLinus Torvalds 			addr = ifa->ifa_local;
14211da177e4SLinus Torvalds 			if (same)
14221da177e4SLinus Torvalds 				break;
14231da177e4SLinus Torvalds 		}
14241da177e4SLinus Torvalds 		if (!same) {
14251da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
14261da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
14271da177e4SLinus Torvalds 			if (same && addr) {
14281da177e4SLinus Torvalds 				if (local || !dst)
14291da177e4SLinus Torvalds 					break;
14301da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
14311da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
14321da177e4SLinus Torvalds 					break;
14331da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1434650638a7SShijie Luo 				if (min_scope <= scope) {
14351da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14361da177e4SLinus Torvalds 					break;
14371da177e4SLinus Torvalds 				}
14381da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14391da177e4SLinus Torvalds 				same = 0;
14401da177e4SLinus Torvalds 			}
14411da177e4SLinus Torvalds 		}
1442ef11db33SFlorian Westphal 	}
14431da177e4SLinus Torvalds 
14441da177e4SLinus Torvalds 	return same ? addr : 0;
14451da177e4SLinus Torvalds }
14461da177e4SLinus Torvalds 
14471da177e4SLinus Torvalds /*
14481da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1449b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1450b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14511da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14521da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14531da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14541da177e4SLinus Torvalds  */
inet_confirm_addr(struct net * net,struct in_device * in_dev,__be32 dst,__be32 local,int scope)1455b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14569bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14571da177e4SLinus Torvalds {
145860cad5daSAl Viro 	__be32 addr = 0;
14599bd85e32SDenis V. Lunev 	struct net_device *dev;
14601da177e4SLinus Torvalds 
146100db4124SIan Morris 	if (in_dev)
14629bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14631da177e4SLinus Torvalds 
14641da177e4SLinus Torvalds 	rcu_read_lock();
1465c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14669f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14679f9354b9SEric Dumazet 		if (in_dev) {
14681da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14691da177e4SLinus Torvalds 			if (addr)
14701da177e4SLinus Torvalds 				break;
14711da177e4SLinus Torvalds 		}
14721da177e4SLinus Torvalds 	}
14731da177e4SLinus Torvalds 	rcu_read_unlock();
14741da177e4SLinus Torvalds 
14751da177e4SLinus Torvalds 	return addr;
14761da177e4SLinus Torvalds }
1477eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14781da177e4SLinus Torvalds 
14791da177e4SLinus Torvalds /*
14801da177e4SLinus Torvalds  *	Device notifier
14811da177e4SLinus Torvalds  */
14821da177e4SLinus Torvalds 
register_inetaddr_notifier(struct notifier_block * nb)14831da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14841da177e4SLinus Torvalds {
1485e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14861da177e4SLinus Torvalds }
14879f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14881da177e4SLinus Torvalds 
unregister_inetaddr_notifier(struct notifier_block * nb)14891da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14901da177e4SLinus Torvalds {
1491e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14921da177e4SLinus Torvalds }
14939f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14941da177e4SLinus Torvalds 
register_inetaddr_validator_notifier(struct notifier_block * nb)14953ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14963ad7d246SKrister Johansen {
14973ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14983ad7d246SKrister Johansen }
14993ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
15003ad7d246SKrister Johansen 
unregister_inetaddr_validator_notifier(struct notifier_block * nb)15013ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
15023ad7d246SKrister Johansen {
15033ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
15043ad7d246SKrister Johansen 	    nb);
15053ad7d246SKrister Johansen }
15063ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
15073ad7d246SKrister Johansen 
15089f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
15099f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
15101da177e4SLinus Torvalds */
inetdev_changename(struct net_device * dev,struct in_device * in_dev)15111da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
15121da177e4SLinus Torvalds {
15131da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
15141da177e4SLinus Torvalds 	int named = 0;
15151da177e4SLinus Torvalds 
1516ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
15171da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
15181da177e4SLinus Torvalds 
15191da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
15201da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15211da177e4SLinus Torvalds 		if (named++ == 0)
1522573bf470SThomas Graf 			goto skip;
152344344b2aSMark McLoughlin 		dot = strchr(old, ':');
152451456b29SIan Morris 		if (!dot) {
15251da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
15261da177e4SLinus Torvalds 			dot = old;
15271da177e4SLinus Torvalds 		}
15289f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
15291da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
15309f9354b9SEric Dumazet 		else
15311da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1532573bf470SThomas Graf skip:
1533573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15341da177e4SLinus Torvalds 	}
15351da177e4SLinus Torvalds }
15361da177e4SLinus Torvalds 
inetdev_send_gratuitous_arp(struct net_device * dev,struct in_device * in_dev)1537d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1538d11327adSIan Campbell 					struct in_device *in_dev)
1539d11327adSIan Campbell 
1540d11327adSIan Campbell {
1541ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1542d11327adSIan Campbell 
1543ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1544d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15456c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15466c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1547d11327adSIan Campbell 			 dev->dev_addr, NULL);
1548d11327adSIan Campbell 	}
1549b76d0789SZoltan Kiss }
1550d11327adSIan Campbell 
15511da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15521da177e4SLinus Torvalds 
inetdev_event(struct notifier_block * this,unsigned long event,void * ptr)15531da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15541da177e4SLinus Torvalds 			 void *ptr)
15551da177e4SLinus Torvalds {
1556351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1557748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15581da177e4SLinus Torvalds 
15591da177e4SLinus Torvalds 	ASSERT_RTNL();
15601da177e4SLinus Torvalds 
15611da177e4SLinus Torvalds 	if (!in_dev) {
15628030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15631da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
156420e61da7SWANG Cong 			if (IS_ERR(in_dev))
156520e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15660cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
156742f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
156842f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15691da177e4SLinus Torvalds 			}
157006770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
157106770843SBreno Leitao 			/* Re-enabling IP */
157206770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
157306770843SBreno Leitao 				in_dev = inetdev_init(dev);
15748030f544SHerbert Xu 		}
15751da177e4SLinus Torvalds 		goto out;
15761da177e4SLinus Torvalds 	}
15771da177e4SLinus Torvalds 
15781da177e4SLinus Torvalds 	switch (event) {
15791da177e4SLinus Torvalds 	case NETDEV_REGISTER:
158091df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1581a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15821da177e4SLinus Torvalds 		break;
15831da177e4SLinus Torvalds 	case NETDEV_UP:
158406770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15851da177e4SLinus Torvalds 			break;
15860cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15876e701eb9SKuniyuki Iwashima 			struct in_ifaddr *ifa = inet_alloc_ifa(in_dev);
15889f9354b9SEric Dumazet 
15899f9354b9SEric Dumazet 			if (ifa) {
15901da177e4SLinus Torvalds 				ifa->ifa_local =
15911da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15921da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15931da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15941da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15951da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15965c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15975c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1598dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1599dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
16001da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
16011da177e4SLinus Torvalds 			}
16021da177e4SLinus Torvalds 		}
16031da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1604a8eceea8SJoe Perches 		fallthrough;
1605eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1606d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1607d11327adSIan Campbell 			break;
1608a8eceea8SJoe Perches 		fallthrough;
1609d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1610a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1611d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
16121da177e4SLinus Torvalds 		break;
16131da177e4SLinus Torvalds 	case NETDEV_DOWN:
16141da177e4SLinus Torvalds 		ip_mc_down(in_dev);
16151da177e4SLinus Torvalds 		break;
161693d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
161775c78500SMoni Shoua 		ip_mc_unmap(in_dev);
161875c78500SMoni Shoua 		break;
161993d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
162075c78500SMoni Shoua 		ip_mc_remap(in_dev);
162175c78500SMoni Shoua 		break;
16221da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
162306770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
16241da177e4SLinus Torvalds 			break;
162506770843SBreno Leitao 		/* disable IP when MTU is not enough */
1626a8eceea8SJoe Perches 		fallthrough;
16271da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
16281da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
16291da177e4SLinus Torvalds 		break;
16301da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16311da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16321da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16331da177e4SLinus Torvalds 		 */
16341da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16351da177e4SLinus Torvalds 
163651602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
163766f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16381da177e4SLinus Torvalds 		break;
16391da177e4SLinus Torvalds 	}
16401da177e4SLinus Torvalds out:
16411da177e4SLinus Torvalds 	return NOTIFY_DONE;
16421da177e4SLinus Torvalds }
16431da177e4SLinus Torvalds 
16441da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16451da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16461da177e4SLinus Torvalds };
16471da177e4SLinus Torvalds 
inet_nlmsg_size(void)164840384999SEric Dumazet static size_t inet_nlmsg_size(void)
1649339bf98fSThomas Graf {
1650339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1651339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1652339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1653339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1654ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
165563b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
165647f0bd50SJacques de Laval 	       + nla_total_size(1)  /* IFA_PROTO */
1657af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
165863b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1659339bf98fSThomas Graf }
1660339bf98fSThomas Graf 
cstamp_delta(unsigned long cstamp)16615c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16625c766d64SJiri Pirko {
16635c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16645c766d64SJiri Pirko }
16655c766d64SJiri Pirko 
put_cacheinfo(struct sk_buff * skb,unsigned long cstamp,unsigned long tstamp,u32 preferred,u32 valid)16665c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16675c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16685c766d64SJiri Pirko {
16695c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16705c766d64SJiri Pirko 
16715c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16725c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16735c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16745c766d64SJiri Pirko 	ci.ifa_valid = valid;
16755c766d64SJiri Pirko 
16765c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16775c766d64SJiri Pirko }
16785c766d64SJiri Pirko 
inet_fill_ifaddr(struct sk_buff * skb,const struct in_ifaddr * ifa,struct inet_fill_args * args)1679cdb2f80fSEric Dumazet static int inet_fill_ifaddr(struct sk_buff *skb, const struct in_ifaddr *ifa,
1680978a46faSChristian Brauner 			    struct inet_fill_args *args)
16811da177e4SLinus Torvalds {
16821da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16831da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16843cd3e72cSEric Dumazet 	unsigned long tstamp;
16855c766d64SJiri Pirko 	u32 preferred, valid;
16861af7f88aSEric Dumazet 	u32 flags;
16871da177e4SLinus Torvalds 
1688978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1689978a46faSChristian Brauner 			args->flags);
169051456b29SIan Morris 	if (!nlh)
169126932566SPatrick McHardy 		return -EMSGSIZE;
169247f68512SThomas Graf 
169347f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16941da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16951da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16961af7f88aSEric Dumazet 
16971af7f88aSEric Dumazet 	flags = READ_ONCE(ifa->ifa_flags);
16981af7f88aSEric Dumazet 	/* Warning : ifm->ifa_flags is an __u8, it holds only 8 bits.
16991af7f88aSEric Dumazet 	 * The 32bit value is given in IFA_FLAGS attribute.
17001af7f88aSEric Dumazet 	 */
17011af7f88aSEric Dumazet 	ifm->ifa_flags = (__u8)flags;
17021af7f88aSEric Dumazet 
17031da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
17041da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
17051da177e4SLinus Torvalds 
1706978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1707978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1708d3807145SChristian Brauner 		goto nla_put_failure;
1709d3807145SChristian Brauner 
17103cd3e72cSEric Dumazet 	tstamp = READ_ONCE(ifa->ifa_tstamp);
17111af7f88aSEric Dumazet 	if (!(flags & IFA_F_PERMANENT)) {
17129f6fa3c4SEric Dumazet 		preferred = READ_ONCE(ifa->ifa_preferred_lft);
1713a5fcf74dSEric Dumazet 		valid = READ_ONCE(ifa->ifa_valid_lft);
17145c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
17153cd3e72cSEric Dumazet 			long tval = (jiffies - tstamp) / HZ;
17165c766d64SJiri Pirko 
17175c766d64SJiri Pirko 			if (preferred > tval)
17185c766d64SJiri Pirko 				preferred -= tval;
17195c766d64SJiri Pirko 			else
17205c766d64SJiri Pirko 				preferred = 0;
17215c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
17225c766d64SJiri Pirko 				if (valid > tval)
17235c766d64SJiri Pirko 					valid -= tval;
17245c766d64SJiri Pirko 				else
17255c766d64SJiri Pirko 					valid = 0;
17265c766d64SJiri Pirko 			}
17275c766d64SJiri Pirko 		}
17285c766d64SJiri Pirko 	} else {
17295c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
17305c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
17315c766d64SJiri Pirko 	}
1732f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1733930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1734f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1735930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1736f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1737930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1738f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
17395c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
174047f0bd50SJacques de Laval 	    (ifa->ifa_proto &&
174147f0bd50SJacques de Laval 	     nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
17421af7f88aSEric Dumazet 	    nla_put_u32(skb, IFA_FLAGS, flags) ||
1743af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1744af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
17453cd3e72cSEric Dumazet 	    put_cacheinfo(skb, READ_ONCE(ifa->ifa_cstamp), tstamp,
17465c766d64SJiri Pirko 			  preferred, valid))
1747f3756b79SDavid S. Miller 		goto nla_put_failure;
174847f68512SThomas Graf 
1749053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1750053c095aSJohannes Berg 	return 0;
175147f68512SThomas Graf 
175247f68512SThomas Graf nla_put_failure:
175326932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
175426932566SPatrick McHardy 	return -EMSGSIZE;
17551da177e4SLinus Torvalds }
17561da177e4SLinus Torvalds 
inet_valid_dump_ifaddr_req(const struct nlmsghdr * nlh,struct inet_fill_args * fillargs,struct net ** tgt_net,struct sock * sk,struct netlink_callback * cb)1757c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1758c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1759c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17605fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1761c33078e3SDavid Ahern {
17625fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1763c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1764c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1765c33078e3SDavid Ahern 	int err, i;
1766c33078e3SDavid Ahern 
1767c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1768c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1769c33078e3SDavid Ahern 		return -EINVAL;
1770c33078e3SDavid Ahern 	}
1771c33078e3SDavid Ahern 
1772c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1773c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1774c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1775c33078e3SDavid Ahern 		return -EINVAL;
1776c33078e3SDavid Ahern 	}
17775fcd266aSDavid Ahern 
17785fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17795fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17805fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17815fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1782c33078e3SDavid Ahern 	}
1783c33078e3SDavid Ahern 
17848cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1785c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1786c33078e3SDavid Ahern 	if (err < 0)
1787c33078e3SDavid Ahern 		return err;
1788c33078e3SDavid Ahern 
1789c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1790c33078e3SDavid Ahern 		if (!tb[i])
1791c33078e3SDavid Ahern 			continue;
1792c33078e3SDavid Ahern 
1793c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1794c33078e3SDavid Ahern 			struct net *net;
1795c33078e3SDavid Ahern 
1796c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1797c33078e3SDavid Ahern 
1798c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1799c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1800bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1801c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1802c33078e3SDavid Ahern 				return PTR_ERR(net);
1803c33078e3SDavid Ahern 			}
1804c33078e3SDavid Ahern 			*tgt_net = net;
1805c33078e3SDavid Ahern 		} else {
1806c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1807c33078e3SDavid Ahern 			return -EINVAL;
1808c33078e3SDavid Ahern 		}
1809c33078e3SDavid Ahern 	}
1810c33078e3SDavid Ahern 
1811c33078e3SDavid Ahern 	return 0;
1812c33078e3SDavid Ahern }
1813c33078e3SDavid Ahern 
in_dev_dump_addr(struct in_device * in_dev,struct sk_buff * skb,struct netlink_callback * cb,int * s_ip_idx,struct inet_fill_args * fillargs)18141c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
1815cdb2f80fSEric Dumazet 			    struct netlink_callback *cb, int *s_ip_idx,
18161c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
18171c98eca4SDavid Ahern {
18181c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
18191c98eca4SDavid Ahern 	int ip_idx = 0;
18201c98eca4SDavid Ahern 	int err;
18211c98eca4SDavid Ahern 
1822cdb2f80fSEric Dumazet 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1823cdb2f80fSEric Dumazet 		if (ip_idx < *s_ip_idx) {
1824ef11db33SFlorian Westphal 			ip_idx++;
18251c98eca4SDavid Ahern 			continue;
1826ef11db33SFlorian Westphal 		}
18271c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
18281c98eca4SDavid Ahern 		if (err < 0)
18291c98eca4SDavid Ahern 			goto done;
18301c98eca4SDavid Ahern 
18311c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1832ef11db33SFlorian Westphal 		ip_idx++;
18331c98eca4SDavid Ahern 	}
18341c98eca4SDavid Ahern 	err = 0;
1835cdb2f80fSEric Dumazet 	ip_idx = 0;
18361c98eca4SDavid Ahern done:
1837cdb2f80fSEric Dumazet 	*s_ip_idx = ip_idx;
18381c98eca4SDavid Ahern 
18391c98eca4SDavid Ahern 	return err;
18401c98eca4SDavid Ahern }
18411c98eca4SDavid Ahern 
1842081a0e3bSEric Dumazet /* Combine dev_addr_genid and dev_base_seq to detect changes.
1843081a0e3bSEric Dumazet  */
inet_base_seq(const struct net * net)1844081a0e3bSEric Dumazet static u32 inet_base_seq(const struct net *net)
1845081a0e3bSEric Dumazet {
1846081a0e3bSEric Dumazet 	u32 res = atomic_read(&net->ipv4.dev_addr_genid) +
1847590e92cdSEric Dumazet 		  READ_ONCE(net->dev_base_seq);
1848081a0e3bSEric Dumazet 
1849081a0e3bSEric Dumazet 	/* Must not return 0 (see nl_dump_check_consistent()).
1850081a0e3bSEric Dumazet 	 * Chose a value far away from 0.
1851081a0e3bSEric Dumazet 	 */
1852081a0e3bSEric Dumazet 	if (!res)
1853081a0e3bSEric Dumazet 		res = 0x80000000;
1854081a0e3bSEric Dumazet 	return res;
1855081a0e3bSEric Dumazet }
1856081a0e3bSEric Dumazet 
inet_dump_ifaddr(struct sk_buff * skb,struct netlink_callback * cb)18571da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18581da177e4SLinus Torvalds {
1859c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1860978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1861978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1862c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1863978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1864978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1865978a46faSChristian Brauner 		.netnsid = -1,
1866978a46faSChristian Brauner 	};
18673b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1868d3807145SChristian Brauner 	struct net *tgt_net = net;
1869cdb2f80fSEric Dumazet 	struct {
1870cdb2f80fSEric Dumazet 		unsigned long ifindex;
1871cdb2f80fSEric Dumazet 		int ip_idx;
1872cdb2f80fSEric Dumazet 	} *ctx = (void *)cb->ctx;
18731da177e4SLinus Torvalds 	struct in_device *in_dev;
1874cdb2f80fSEric Dumazet 	struct net_device *dev;
1875d7e38611SDavid Ahern 	int err = 0;
18761da177e4SLinus Torvalds 
1877cdb2f80fSEric Dumazet 	rcu_read_lock();
1878c33078e3SDavid Ahern 	if (cb->strict_check) {
1879c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18805fcd266aSDavid Ahern 						 skb->sk, cb);
1881c33078e3SDavid Ahern 		if (err < 0)
1882cdb2f80fSEric Dumazet 			goto done;
18835fcd266aSDavid Ahern 
18845fcd266aSDavid Ahern 		if (fillargs.ifindex) {
1885cdb2f80fSEric Dumazet 			dev = dev_get_by_index_rcu(tgt_net, fillargs.ifindex);
18867b05ab85SIdo Schimmel 			if (!dev) {
18877b05ab85SIdo Schimmel 				err = -ENODEV;
1888cdb2f80fSEric Dumazet 				goto done;
18897b05ab85SIdo Schimmel 			}
1890eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18919f9354b9SEric Dumazet 			if (!in_dev)
1892cdb2f80fSEric Dumazet 				goto done;
1893cdb2f80fSEric Dumazet 			err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx,
18941c98eca4SDavid Ahern 					       &fillargs);
18951da177e4SLinus Torvalds 			goto done;
18961da177e4SLinus Torvalds 		}
1897eec4df98SEric Dumazet 	}
18981da177e4SLinus Torvalds 
1899cdb2f80fSEric Dumazet 	cb->seq = inet_base_seq(tgt_net);
1900cdb2f80fSEric Dumazet 
1901b8c8abefSAlexander Mikhalitsyn 	for_each_netdev_dump(tgt_net, dev, ctx->ifindex) {
1902cdb2f80fSEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
1903cdb2f80fSEric Dumazet 		if (!in_dev)
1904cdb2f80fSEric Dumazet 			continue;
1905cdb2f80fSEric Dumazet 		err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx,
1906cdb2f80fSEric Dumazet 				       &fillargs);
1907cdb2f80fSEric Dumazet 		if (err < 0)
1908cdb2f80fSEric Dumazet 			goto done;
1909cdb2f80fSEric Dumazet 	}
19101da177e4SLinus Torvalds done:
1911978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1912d3807145SChristian Brauner 		put_net(tgt_net);
1913cdb2f80fSEric Dumazet 	rcu_read_unlock();
1914cdb2f80fSEric Dumazet 	return err;
19151da177e4SLinus Torvalds }
19161da177e4SLinus Torvalds 
rtmsg_ifa(int event,struct in_ifaddr * ifa,struct nlmsghdr * nlh,u32 portid)1917d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
191815e47304SEric W. Biederman 		      u32 portid)
19191da177e4SLinus Torvalds {
1920978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1921978a46faSChristian Brauner 		.portid = portid,
1922978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1923978a46faSChristian Brauner 		.event = event,
1924978a46faSChristian Brauner 		.flags = 0,
1925978a46faSChristian Brauner 		.netnsid = -1,
1926978a46faSChristian Brauner 	};
192747f68512SThomas Graf 	struct sk_buff *skb;
1928d6062cbbSThomas Graf 	int err = -ENOBUFS;
19294b8aa9abSDenis V. Lunev 	struct net *net;
19301da177e4SLinus Torvalds 
1931c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1932339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
193351456b29SIan Morris 	if (!skb)
1934d6062cbbSThomas Graf 		goto errout;
1935d6062cbbSThomas Graf 
1936978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
193726932566SPatrick McHardy 	if (err < 0) {
193826932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
193926932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
194026932566SPatrick McHardy 		kfree_skb(skb);
194126932566SPatrick McHardy 		goto errout;
194226932566SPatrick McHardy 	}
194315e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19441ce85fe4SPablo Neira Ayuso 	return;
1945d6062cbbSThomas Graf errout:
19464b8aa9abSDenis V. Lunev 	rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19471da177e4SLinus Torvalds }
19481da177e4SLinus Torvalds 
inet_get_link_af_size(const struct net_device * dev,u32 ext_filter_mask)1949b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1950b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19519f0f7272SThomas Graf {
19521fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19539f0f7272SThomas Graf 
19549f0f7272SThomas Graf 	if (!in_dev)
19559f0f7272SThomas Graf 		return 0;
19569f0f7272SThomas Graf 
19579f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19589f0f7272SThomas Graf }
19599f0f7272SThomas Graf 
inet_fill_link_af(struct sk_buff * skb,const struct net_device * dev,u32 ext_filter_mask)1960d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1961d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19629f0f7272SThomas Graf {
19631fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19649f0f7272SThomas Graf 	struct nlattr *nla;
19659f0f7272SThomas Graf 	int i;
19669f0f7272SThomas Graf 
19679f0f7272SThomas Graf 	if (!in_dev)
19689f0f7272SThomas Graf 		return -ENODATA;
19699f0f7272SThomas Graf 
19709f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
197151456b29SIan Morris 	if (!nla)
19729f0f7272SThomas Graf 		return -EMSGSIZE;
19739f0f7272SThomas Graf 
19749f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19750598f8f3SEric Dumazet 		((u32 *) nla_data(nla))[i] = READ_ONCE(in_dev->cnf.data[i]);
19769f0f7272SThomas Graf 
19779f0f7272SThomas Graf 	return 0;
19789f0f7272SThomas Graf }
19799f0f7272SThomas Graf 
19809f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19819f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19829f0f7272SThomas Graf };
19839f0f7272SThomas Graf 
inet_validate_link_af(const struct net_device * dev,const struct nlattr * nla,struct netlink_ext_ack * extack)1984cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
19858679c31eSRocco Yue 				 const struct nlattr *nla,
19868679c31eSRocco Yue 				 struct netlink_ext_ack *extack)
19879f0f7272SThomas Graf {
19889f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19899f0f7272SThomas Graf 	int err, rem;
19909f0f7272SThomas Graf 
1991a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
1992cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19939f0f7272SThomas Graf 
19948cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
19958679c31eSRocco Yue 					  inet_af_policy, extack);
19969f0f7272SThomas Graf 	if (err < 0)
19979f0f7272SThomas Graf 		return err;
19989f0f7272SThomas Graf 
19999f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20009f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
20019f0f7272SThomas Graf 			int cfgid = nla_type(a);
20029f0f7272SThomas Graf 
20039f0f7272SThomas Graf 			if (nla_len(a) < 4)
20049f0f7272SThomas Graf 				return -EINVAL;
20059f0f7272SThomas Graf 
20069f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
20079f0f7272SThomas Graf 				return -EINVAL;
20089f0f7272SThomas Graf 		}
20099f0f7272SThomas Graf 	}
20109f0f7272SThomas Graf 
2011cf7afbfeSThomas Graf 	return 0;
2012cf7afbfeSThomas Graf }
2013cf7afbfeSThomas Graf 
inet_set_link_af(struct net_device * dev,const struct nlattr * nla,struct netlink_ext_ack * extack)20143583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
20153583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
2016cf7afbfeSThomas Graf {
2017a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
2018cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
2019cf7afbfeSThomas Graf 	int rem;
2020cf7afbfeSThomas Graf 
2021cf7afbfeSThomas Graf 	if (!in_dev)
2022cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
2023cf7afbfeSThomas Graf 
20248cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
20255ac6b198SZheng Yongjun 		return -EINVAL;
2026cf7afbfeSThomas Graf 
20279f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20289f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
20299f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
20309f0f7272SThomas Graf 	}
20319f0f7272SThomas Graf 
20329f0f7272SThomas Graf 	return 0;
20339f0f7272SThomas Graf }
20349f0f7272SThomas Graf 
inet_netconf_msgsize_devconf(int type)2035edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2036edc9e748SNicolas Dichtel {
2037edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2038edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2039136ba622SZhang Shengju 	bool all = false;
2040edc9e748SNicolas Dichtel 
2041136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2042136ba622SZhang Shengju 		all = true;
2043136ba622SZhang Shengju 
2044136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2045edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2046136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2047cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2048136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2049d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20505cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20515cbf777cSXin Long 		size += nla_total_size(4);
2052136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2053f085ff1cSstephen hemminger 		size += nla_total_size(4);
2054136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2055974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2056edc9e748SNicolas Dichtel 
2057edc9e748SNicolas Dichtel 	return size;
2058edc9e748SNicolas Dichtel }
2059edc9e748SNicolas Dichtel 
inet_netconf_fill_devconf(struct sk_buff * skb,int ifindex,const struct ipv4_devconf * devconf,u32 portid,u32 seq,int event,unsigned int flags,int type)2060edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
20610598f8f3SEric Dumazet 				     const struct ipv4_devconf *devconf,
20620598f8f3SEric Dumazet 				     u32 portid, u32 seq, int event,
20630598f8f3SEric Dumazet 				     unsigned int flags, int type)
2064edc9e748SNicolas Dichtel {
2065edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2066edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2067136ba622SZhang Shengju 	bool all = false;
2068edc9e748SNicolas Dichtel 
2069edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2070edc9e748SNicolas Dichtel 			flags);
207151456b29SIan Morris 	if (!nlh)
2072edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2073edc9e748SNicolas Dichtel 
2074136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2075136ba622SZhang Shengju 		all = true;
2076136ba622SZhang Shengju 
2077edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2078edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2079edc9e748SNicolas Dichtel 
2080edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2081edc9e748SNicolas Dichtel 		goto nla_put_failure;
2082edc9e748SNicolas Dichtel 
2083b5c9641dSDavid Ahern 	if (!devconf)
2084b5c9641dSDavid Ahern 		goto out;
2085b5c9641dSDavid Ahern 
2086136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2087edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
20880598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, FORWARDING)) < 0)
2089edc9e748SNicolas Dichtel 		goto nla_put_failure;
2090136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2091cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
20920598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, RP_FILTER)) < 0)
2093cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2094136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2095d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
20960598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, MC_FORWARDING)) < 0)
2097d67b8c61SNicolas Dichtel 		goto nla_put_failure;
20985cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
20995cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
21000598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, BC_FORWARDING)) < 0)
21015cbf777cSXin Long 		goto nla_put_failure;
2102136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
210309aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
21040598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, PROXY_ARP)) < 0)
2105f085ff1cSstephen hemminger 		goto nla_put_failure;
2106136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2107974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
21080598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf,
21090598f8f3SEric Dumazet 					IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2110974d7af5SAndy Gospodarek 		goto nla_put_failure;
2111edc9e748SNicolas Dichtel 
2112b5c9641dSDavid Ahern out:
2113053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2114053c095aSJohannes Berg 	return 0;
2115edc9e748SNicolas Dichtel 
2116edc9e748SNicolas Dichtel nla_put_failure:
2117edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2118edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2119edc9e748SNicolas Dichtel }
2120edc9e748SNicolas Dichtel 
inet_netconf_notify_devconf(struct net * net,int event,int type,int ifindex,struct ipv4_devconf * devconf)21213b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
21223b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2123edc9e748SNicolas Dichtel {
2124edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2125edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2126edc9e748SNicolas Dichtel 
2127fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
212851456b29SIan Morris 	if (!skb)
2129edc9e748SNicolas Dichtel 		goto errout;
2130edc9e748SNicolas Dichtel 
2131edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
21323b022865SDavid Ahern 					event, 0, type);
2133edc9e748SNicolas Dichtel 	if (err < 0) {
2134edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2135edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2136edc9e748SNicolas Dichtel 		kfree_skb(skb);
2137edc9e748SNicolas Dichtel 		goto errout;
2138edc9e748SNicolas Dichtel 	}
2139fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2140edc9e748SNicolas Dichtel 	return;
2141edc9e748SNicolas Dichtel errout:
2142edc9e748SNicolas Dichtel 	rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2143edc9e748SNicolas Dichtel }
2144edc9e748SNicolas Dichtel 
21459e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21469e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21479e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2148cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
214909aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2150974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21519e551110SNicolas Dichtel };
21529e551110SNicolas Dichtel 
inet_netconf_valid_get_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)2153eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2154eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2155eede370dSJakub Kicinski 				      struct nlattr **tb,
2156eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2157eede370dSJakub Kicinski {
2158eede370dSJakub Kicinski 	int i, err;
2159eede370dSJakub Kicinski 
2160eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2161eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2162eede370dSJakub Kicinski 		return -EINVAL;
2163eede370dSJakub Kicinski 	}
2164eede370dSJakub Kicinski 
2165eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21668cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21678cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21688cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2169eede370dSJakub Kicinski 
21708cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21718cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21728cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2173eede370dSJakub Kicinski 	if (err)
2174eede370dSJakub Kicinski 		return err;
2175eede370dSJakub Kicinski 
2176eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2177eede370dSJakub Kicinski 		if (!tb[i])
2178eede370dSJakub Kicinski 			continue;
2179eede370dSJakub Kicinski 
2180eede370dSJakub Kicinski 		switch (i) {
2181eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2182eede370dSJakub Kicinski 			break;
2183eede370dSJakub Kicinski 		default:
2184eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2185eede370dSJakub Kicinski 			return -EINVAL;
2186eede370dSJakub Kicinski 		}
2187eede370dSJakub Kicinski 	}
2188eede370dSJakub Kicinski 
2189eede370dSJakub Kicinski 	return 0;
2190eede370dSJakub Kicinski }
2191eede370dSJakub Kicinski 
inet_netconf_get_devconf(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)21929e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2193c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2194c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
21959e551110SNicolas Dichtel {
21969e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
21979e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX + 1];
2198bbcf9105SEric Dumazet 	const struct ipv4_devconf *devconf;
2199bbcf9105SEric Dumazet 	struct in_device *in_dev = NULL;
2200bbcf9105SEric Dumazet 	struct net_device *dev = NULL;
22019e551110SNicolas Dichtel 	struct sk_buff *skb;
22029e551110SNicolas Dichtel 	int ifindex;
22039e551110SNicolas Dichtel 	int err;
22049e551110SNicolas Dichtel 
2205eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2206eede370dSJakub Kicinski 	if (err)
2207bbcf9105SEric Dumazet 		return err;
22089e551110SNicolas Dichtel 
22099e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
2210bbcf9105SEric Dumazet 		return -EINVAL;
22119e551110SNicolas Dichtel 
22129e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
22139e551110SNicolas Dichtel 	switch (ifindex) {
22149e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
22159e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
22169e551110SNicolas Dichtel 		break;
22179e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
22189e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
22199e551110SNicolas Dichtel 		break;
22209e551110SNicolas Dichtel 	default:
2221bbcf9105SEric Dumazet 		err = -ENODEV;
2222bbcf9105SEric Dumazet 		dev = dev_get_by_index(net, ifindex);
2223bbcf9105SEric Dumazet 		if (dev)
2224bbcf9105SEric Dumazet 			in_dev = in_dev_get(dev);
222551456b29SIan Morris 		if (!in_dev)
22269e551110SNicolas Dichtel 			goto errout;
22279e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
22289e551110SNicolas Dichtel 		break;
22299e551110SNicolas Dichtel 	}
22309e551110SNicolas Dichtel 
22319e551110SNicolas Dichtel 	err = -ENOBUFS;
2232fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
223351456b29SIan Morris 	if (!skb)
22349e551110SNicolas Dichtel 		goto errout;
22359e551110SNicolas Dichtel 
22369e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22379e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22389e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2239136ba622SZhang Shengju 					NETCONFA_ALL);
22409e551110SNicolas Dichtel 	if (err < 0) {
22419e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22429e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22439e551110SNicolas Dichtel 		kfree_skb(skb);
22449e551110SNicolas Dichtel 		goto errout;
22459e551110SNicolas Dichtel 	}
22469e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22479e551110SNicolas Dichtel errout:
2248bbcf9105SEric Dumazet 	if (in_dev)
2249bbcf9105SEric Dumazet 		in_dev_put(in_dev);
2250bbcf9105SEric Dumazet 	dev_put(dev);
22519e551110SNicolas Dichtel 	return err;
22529e551110SNicolas Dichtel }
22539e551110SNicolas Dichtel 
inet_netconf_dump_devconf(struct sk_buff * skb,struct netlink_callback * cb)22547a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22557a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22567a674200SNicolas Dichtel {
2257addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22587a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
225916748707SEric Dumazet 	struct {
226016748707SEric Dumazet 		unsigned long ifindex;
226116748707SEric Dumazet 		unsigned int all_default;
226216748707SEric Dumazet 	} *ctx = (void *)cb->ctx;
226316748707SEric Dumazet 	const struct in_device *in_dev;
22647a674200SNicolas Dichtel 	struct net_device *dev;
226516748707SEric Dumazet 	int err = 0;
22667a674200SNicolas Dichtel 
2267addd383fSDavid Ahern 	if (cb->strict_check) {
2268addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2269addd383fSDavid Ahern 		struct netconfmsg *ncm;
2270addd383fSDavid Ahern 
2271addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2272addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2273addd383fSDavid Ahern 			return -EINVAL;
2274addd383fSDavid Ahern 		}
2275addd383fSDavid Ahern 
2276addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2277addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2278addd383fSDavid Ahern 			return -EINVAL;
2279addd383fSDavid Ahern 		}
2280addd383fSDavid Ahern 	}
2281addd383fSDavid Ahern 
22827a674200SNicolas Dichtel 	rcu_read_lock();
228316748707SEric Dumazet 	for_each_netdev_dump(net, dev, ctx->ifindex) {
22847a674200SNicolas Dichtel 		in_dev = __in_dev_get_rcu(dev);
22857a674200SNicolas Dichtel 		if (!in_dev)
228616748707SEric Dumazet 			continue;
228716748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, dev->ifindex,
22887a674200SNicolas Dichtel 						&in_dev->cnf,
22897a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2290addd383fSDavid Ahern 						nlh->nlmsg_seq,
229116748707SEric Dumazet 						RTM_NEWNETCONF, NLM_F_MULTI,
229216748707SEric Dumazet 						NETCONFA_ALL);
229316748707SEric Dumazet 		if (err < 0)
22947a674200SNicolas Dichtel 			goto done;
22957a674200SNicolas Dichtel 	}
229616748707SEric Dumazet 	if (ctx->all_default == 0) {
229716748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
22987a674200SNicolas Dichtel 						net->ipv4.devconf_all,
22997a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2300addd383fSDavid Ahern 						nlh->nlmsg_seq,
23017a674200SNicolas Dichtel 						RTM_NEWNETCONF, NLM_F_MULTI,
230216748707SEric Dumazet 						NETCONFA_ALL);
230316748707SEric Dumazet 		if (err < 0)
23047a674200SNicolas Dichtel 			goto done;
230516748707SEric Dumazet 		ctx->all_default++;
23067a674200SNicolas Dichtel 	}
230716748707SEric Dumazet 	if (ctx->all_default == 1) {
230816748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
23097a674200SNicolas Dichtel 						net->ipv4.devconf_dflt,
23107a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2311addd383fSDavid Ahern 						nlh->nlmsg_seq,
23127a674200SNicolas Dichtel 						RTM_NEWNETCONF, NLM_F_MULTI,
231316748707SEric Dumazet 						NETCONFA_ALL);
231416748707SEric Dumazet 		if (err < 0)
23157a674200SNicolas Dichtel 			goto done;
231616748707SEric Dumazet 		ctx->all_default++;
23177a674200SNicolas Dichtel 	}
23187a674200SNicolas Dichtel done:
231916748707SEric Dumazet 	rcu_read_unlock();
232016748707SEric Dumazet 	return err;
23217a674200SNicolas Dichtel }
23227a674200SNicolas Dichtel 
23231da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23241da177e4SLinus Torvalds 
devinet_copy_dflt_conf(struct net * net,int i)2325c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
232631be3085SHerbert Xu {
232731be3085SHerbert Xu 	struct net_device *dev;
232831be3085SHerbert Xu 
232931be3085SHerbert Xu 	rcu_read_lock();
2330c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2331c6d14c84SEric Dumazet 		struct in_device *in_dev;
2332c6d14c84SEric Dumazet 
233331be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
233431be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23359355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2336c6d14c84SEric Dumazet 	}
233731be3085SHerbert Xu 	rcu_read_unlock();
233831be3085SHerbert Xu }
233931be3085SHerbert Xu 
2340c6d14c84SEric Dumazet /* called with RTNL locked */
inet_forward_change(struct net * net)2341c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
234268dd299bSPavel Emelyanov {
234368dd299bSPavel Emelyanov 	struct net_device *dev;
2344586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
234568dd299bSPavel Emelyanov 
2346586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23479355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23483b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23493b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2350edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2351edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23523b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23533b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2354edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2355edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
235668dd299bSPavel Emelyanov 
2357c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
235868dd299bSPavel Emelyanov 		struct in_device *in_dev;
2359fa17806cSEric Dumazet 
23600187bdfbSBen Hutchings 		if (on)
23610187bdfbSBen Hutchings 			dev_disable_lro(dev);
2362fa17806cSEric Dumazet 
2363fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2364edc9e748SNicolas Dichtel 		if (in_dev) {
236568dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23663b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23673b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2368edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2369edc9e748SNicolas Dichtel 		}
237068dd299bSPavel Emelyanov 	}
237168dd299bSPavel Emelyanov }
237268dd299bSPavel Emelyanov 
devinet_conf_ifindex(struct net * net,struct ipv4_devconf * cnf)2373f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2374f085ff1cSstephen hemminger {
2375f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2376f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2377f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2378f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2379f085ff1cSstephen hemminger 	else {
2380f085ff1cSstephen hemminger 		struct in_device *idev
2381f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2382f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2383f085ff1cSstephen hemminger 	}
2384f085ff1cSstephen hemminger }
2385f085ff1cSstephen hemminger 
devinet_conf_proc(const struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)238678eb4ea2SJoel Granados static int devinet_conf_proc(const struct ctl_table *ctl, int write,
238732927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
238831be3085SHerbert Xu {
2389d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23908d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2391d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
239231be3085SHerbert Xu 
239331be3085SHerbert Xu 	if (write) {
239431be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2395c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
239631be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2397f085ff1cSstephen hemminger 		int ifindex;
239831be3085SHerbert Xu 
239931be3085SHerbert Xu 		set_bit(i, cnf->state);
240031be3085SHerbert Xu 
24019355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2402c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2403d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2404d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2405d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
24064ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2407f085ff1cSstephen hemminger 
24085cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
24095cbf777cSXin Long 		    new_value != old_value)
24105cbf777cSXin Long 			rt_cache_flush(net);
24115cbf777cSXin Long 
2412cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2413cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2414f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24153b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24163b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2417cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2418cc535dfbSNicolas Dichtel 		}
2419f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2420f085ff1cSstephen hemminger 		    new_value != old_value) {
2421f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24223b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24233b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2424f085ff1cSstephen hemminger 						    ifindex, cnf);
2425f085ff1cSstephen hemminger 		}
2426974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2427974d7af5SAndy Gospodarek 		    new_value != old_value) {
2428974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24293b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24303b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2431974d7af5SAndy Gospodarek 						    ifindex, cnf);
2432974d7af5SAndy Gospodarek 		}
243331be3085SHerbert Xu 	}
243431be3085SHerbert Xu 
243531be3085SHerbert Xu 	return ret;
243631be3085SHerbert Xu }
243731be3085SHerbert Xu 
devinet_sysctl_forward(const struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)243878eb4ea2SJoel Granados static int devinet_sysctl_forward(const struct ctl_table *ctl, int write,
243932927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24401da177e4SLinus Torvalds {
24411da177e4SLinus Torvalds 	int *valp = ctl->data;
24421da177e4SLinus Torvalds 	int val = *valp;
244388af182eSEric W. Biederman 	loff_t pos = *ppos;
24448292d7f6SYang Yang 	struct net *net = ctl->extra2;
24458292d7f6SYang Yang 	int ret;
24468292d7f6SYang Yang 
24478292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24488292d7f6SYang Yang 		return -EPERM;
24498292d7f6SYang Yang 
24508292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24511da177e4SLinus Torvalds 
24521da177e4SLinus Torvalds 	if (write && *valp != val) {
24530187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
245488af182eSEric W. Biederman 			if (!rtnl_trylock()) {
245588af182eSEric W. Biederman 				/* Restore the original values before restarting */
245688af182eSEric W. Biederman 				*valp = val;
245788af182eSEric W. Biederman 				*ppos = pos;
24589b8adb5eSEric W. Biederman 				return restart_syscall();
245988af182eSEric W. Biederman 			}
24600187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2461c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2462edc9e748SNicolas Dichtel 			} else {
24630187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24640187bdfbSBen Hutchings 				struct in_device *idev =
24650187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2466edc9e748SNicolas Dichtel 				if (*valp)
24670187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24683b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2469edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2470edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2471edc9e748SNicolas Dichtel 							    cnf);
24720187bdfbSBen Hutchings 			}
24730187bdfbSBen Hutchings 			rtnl_unlock();
24744ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2475edc9e748SNicolas Dichtel 		} else
24763b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24773b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2478edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2479edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24800187bdfbSBen Hutchings 	}
24811da177e4SLinus Torvalds 
24821da177e4SLinus Torvalds 	return ret;
24831da177e4SLinus Torvalds }
24841da177e4SLinus Torvalds 
ipv4_doint_and_flush(const struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)248578eb4ea2SJoel Granados static int ipv4_doint_and_flush(const struct ctl_table *ctl, int write,
248632927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
24871da177e4SLinus Torvalds {
24881da177e4SLinus Torvalds 	int *valp = ctl->data;
24891da177e4SLinus Torvalds 	int val = *valp;
24908d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
249176e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24921da177e4SLinus Torvalds 
24931da177e4SLinus Torvalds 	if (write && *valp != val)
24944ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
24951da177e4SLinus Torvalds 
24961da177e4SLinus Torvalds 	return ret;
24971da177e4SLinus Torvalds }
24981da177e4SLinus Torvalds 
2499f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
250042f811b8SHerbert Xu 	{ \
250142f811b8SHerbert Xu 		.procname	= name, \
250242f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
250302291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
250442f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
250542f811b8SHerbert Xu 		.mode		= mval, \
250642f811b8SHerbert Xu 		.proc_handler	= proc, \
250731be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
250842f811b8SHerbert Xu 	}
250942f811b8SHerbert Xu 
251042f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2511f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
251242f811b8SHerbert Xu 
251342f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2514f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
251542f811b8SHerbert Xu 
2516f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2517f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
251842f811b8SHerbert Xu 
251942f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2520f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
252142f811b8SHerbert Xu 
25221da177e4SLinus Torvalds static struct devinet_sysctl_table {
25231da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
25241c106eb0SJoel Granados 	struct ctl_table devinet_vars[IPV4_DEVCONF_MAX];
25251da177e4SLinus Torvalds } devinet_sysctl = {
25261da177e4SLinus Torvalds 	.devinet_vars = {
252742f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2528f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
252942f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25305cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
253142f811b8SHerbert Xu 
253242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
253342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
253442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
253542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
253642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
253742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
253842f811b8SHerbert Xu 					"accept_source_route"),
25398153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
254028f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
254142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
254242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
254342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
254442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
254542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
254642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
254742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
254842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
254942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2550eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
2551fcdb44d0SJames Prestwood 		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
2552fcdb44d0SJames Prestwood 					"arp_evict_nocarrier"),
255365324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25545c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25555c6fe01cSWilliam Manley 					"force_igmp_version"),
25562690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25572690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25582690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25592690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25600eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25610eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
256297daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
256397daf331SJohannes Berg 					"drop_gratuitous_arp"),
256442f811b8SHerbert Xu 
256542f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
256642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
256742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
256842f811b8SHerbert Xu 					      "promote_secondaries"),
2569d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2570d0daebc3SThomas Graf 					      "route_localnet"),
257112b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
257212b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25731da177e4SLinus Torvalds 	},
25741da177e4SLinus Torvalds };
25751da177e4SLinus Torvalds 
__devinet_sysctl_register(struct net * net,char * dev_name,int ifindex,struct ipv4_devconf * p)2576ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
257729c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25781da177e4SLinus Torvalds {
25791da177e4SLinus Torvalds 	int i;
25809fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25818607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2582bfada697SPavel Emelyanov 
2583425b9c7fSVasily Averin 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT);
25841da177e4SLinus Torvalds 	if (!t)
25859fa89642SPavel Emelyanov 		goto out;
25869fa89642SPavel Emelyanov 
25871c106eb0SJoel Granados 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars); i++) {
25881da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
258931be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2590c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25911da177e4SLinus Torvalds 	}
25921da177e4SLinus Torvalds 
25938607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
25941da177e4SLinus Torvalds 
25958607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
25961da177e4SLinus Torvalds 	if (!t->sysctl_header)
25978607ddb8SEric W. Biederman 		goto free;
25981da177e4SLinus Torvalds 
25991da177e4SLinus Torvalds 	p->sysctl = t;
260029c994e3SNicolas Dichtel 
26013b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
26023b022865SDavid Ahern 				    ifindex, p);
2603ea40b324SPavel Emelyanov 	return 0;
26041da177e4SLinus Torvalds 
26051da177e4SLinus Torvalds free:
26061da177e4SLinus Torvalds 	kfree(t);
26079fa89642SPavel Emelyanov out:
26086def4801Sliuguoqiang 	return -ENOMEM;
26091da177e4SLinus Torvalds }
26101da177e4SLinus Torvalds 
__devinet_sysctl_unregister(struct net * net,struct ipv4_devconf * cnf,int ifindex)2611b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2612b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
261366f27a52SPavel Emelyanov {
261451602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
261566f27a52SPavel Emelyanov 
2616b5c9641dSDavid Ahern 	if (t) {
261751602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2618ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26191da177e4SLinus Torvalds 		kfree(t);
26201da177e4SLinus Torvalds 	}
262151602b2aSPavel Emelyanov 
2622b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2623b5c9641dSDavid Ahern }
2624b5c9641dSDavid Ahern 
devinet_sysctl_register(struct in_device * idev)262520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
262651602b2aSPavel Emelyanov {
262720e61da7SWANG Cong 	int err;
262820e61da7SWANG Cong 
262920e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
263020e61da7SWANG Cong 		return -EINVAL;
263120e61da7SWANG Cong 
263220e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
263320e61da7SWANG Cong 	if (err)
263420e61da7SWANG Cong 		return err;
263520e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
263629c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
263720e61da7SWANG Cong 	if (err)
263820e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
263920e61da7SWANG Cong 	return err;
264051602b2aSPavel Emelyanov }
264151602b2aSPavel Emelyanov 
devinet_sysctl_unregister(struct in_device * idev)264251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
264351602b2aSPavel Emelyanov {
2644b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2645b5c9641dSDavid Ahern 
2646b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
264751602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26481da177e4SLinus Torvalds }
26491da177e4SLinus Torvalds 
265068dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
265168dd299bSPavel Emelyanov 	{
265268dd299bSPavel Emelyanov 		.procname	= "ip_forward",
265368dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
265402291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
265568dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
265668dd299bSPavel Emelyanov 		.mode		= 0644,
265768dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
265868dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2659c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
266068dd299bSPavel Emelyanov 	},
266168dd299bSPavel Emelyanov };
26622a75de0cSEric Dumazet #endif
266368dd299bSPavel Emelyanov 
devinet_init_net(struct net * net)2664752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2665752d14dcSPavel Emelyanov {
2666752d14dcSPavel Emelyanov 	int err;
2667752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26682a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2669856c395cSCong Wang 	struct ctl_table *tbl;
2670752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26712a75de0cSEric Dumazet #endif
2672752d14dcSPavel Emelyanov 
2673752d14dcSPavel Emelyanov 	err = -ENOMEM;
2674856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
267551456b29SIan Morris 	if (!all)
2676752d14dcSPavel Emelyanov 		goto err_alloc_all;
2677752d14dcSPavel Emelyanov 
2678856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
267951456b29SIan Morris 	if (!dflt)
2680752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2681752d14dcSPavel Emelyanov 
26822a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2683856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
268451456b29SIan Morris 	if (!tbl)
2685752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2686752d14dcSPavel Emelyanov 
268702291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2688752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2689752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26902a75de0cSEric Dumazet #endif
2691856c395cSCong Wang 
26929efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
2693a5612ca1SKuniyuki Iwashima 		switch (net_inherit_devconf()) {
2694a5612ca1SKuniyuki Iwashima 		case 3:
26959efd6a3cSNicolas Dichtel 			/* copy from the current netns */
26969efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
26979efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
26989efd6a3cSNicolas Dichtel 			memcpy(dflt,
26999efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
27009efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2701a5612ca1SKuniyuki Iwashima 			break;
2702a5612ca1SKuniyuki Iwashima 		case 0:
2703a5612ca1SKuniyuki Iwashima 		case 1:
2704a5612ca1SKuniyuki Iwashima 			/* copy from init_net */
27059efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
27069efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27079efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
27089efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2709a5612ca1SKuniyuki Iwashima 			break;
2710a5612ca1SKuniyuki Iwashima 		case 2:
2711a5612ca1SKuniyuki Iwashima 			/* use compiled values */
2712a5612ca1SKuniyuki Iwashima 			break;
27139efd6a3cSNicolas Dichtel 		}
2714752d14dcSPavel Emelyanov 	}
2715752d14dcSPavel Emelyanov 
2716752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
271729c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2718752d14dcSPavel Emelyanov 	if (err < 0)
2719752d14dcSPavel Emelyanov 		goto err_reg_all;
2720752d14dcSPavel Emelyanov 
272129c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
272229c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2723752d14dcSPavel Emelyanov 	if (err < 0)
2724752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2725752d14dcSPavel Emelyanov 
2726752d14dcSPavel Emelyanov 	err = -ENOMEM;
2727c899710fSJoel Granados 	forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl,
2728c899710fSJoel Granados 					  ARRAY_SIZE(ctl_forward_entry));
272951456b29SIan Morris 	if (!forw_hdr)
2730752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27312a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2732752d14dcSPavel Emelyanov #endif
2733752d14dcSPavel Emelyanov 
2734752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2735752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2736752d14dcSPavel Emelyanov 	return 0;
2737752d14dcSPavel Emelyanov 
2738752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2739752d14dcSPavel Emelyanov err_reg_ctl:
2740b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2741752d14dcSPavel Emelyanov err_reg_dflt:
2742b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2743752d14dcSPavel Emelyanov err_reg_all:
2744752d14dcSPavel Emelyanov 	kfree(tbl);
2745752d14dcSPavel Emelyanov err_alloc_ctl:
27462a75de0cSEric Dumazet #endif
2747752d14dcSPavel Emelyanov 	kfree(dflt);
2748752d14dcSPavel Emelyanov err_alloc_dflt:
2749752d14dcSPavel Emelyanov 	kfree(all);
2750752d14dcSPavel Emelyanov err_alloc_all:
2751752d14dcSPavel Emelyanov 	return err;
2752752d14dcSPavel Emelyanov }
2753752d14dcSPavel Emelyanov 
devinet_exit_net(struct net * net)2754752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2755752d14dcSPavel Emelyanov {
27562a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2757bfa858f2SThomas Weißschuh 	const struct ctl_table *tbl;
2758752d14dcSPavel Emelyanov 
2759752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2760752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2761b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2762b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2763b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2764b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2765752d14dcSPavel Emelyanov 	kfree(tbl);
27662a75de0cSEric Dumazet #endif
2767752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2768752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2769752d14dcSPavel Emelyanov }
2770752d14dcSPavel Emelyanov 
2771752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2772752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2773752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2774752d14dcSPavel Emelyanov };
2775752d14dcSPavel Emelyanov 
2776207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
27779f0f7272SThomas Graf 	.family		  = AF_INET,
27789f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
27799f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2780cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2781cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
27829f0f7272SThomas Graf };
27839f0f7272SThomas Graf 
devinet_init(void)27841da177e4SLinus Torvalds void __init devinet_init(void)
27851da177e4SLinus Torvalds {
2786fd23c3b3SDavid S. Miller 	int i;
2787fd23c3b3SDavid S. Miller 
2788fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2789fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2790fd23c3b3SDavid S. Miller 
2791752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
27921da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
279363f3444fSThomas Graf 
2794906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
27955c766d64SJiri Pirko 
27969f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
27979f0f7272SThomas Graf 
2798b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2799b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2800cdb2f80fSEric Dumazet 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr,
28015b4b62a1SJakub Kicinski 		      RTNL_FLAG_DUMP_UNLOCKED | RTNL_FLAG_DUMP_SPLIT_NLM_DONE);
28029e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2803bbcf9105SEric Dumazet 		      inet_netconf_dump_devconf,
280416748707SEric Dumazet 		      RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED);
28051da177e4SLinus Torvalds }
2806