xref: /linux/net/ipv4/devinet.c (revision 7b05ab85e28f615e70520d24c075249b4512044e)
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 
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 
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 
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  */
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 */
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
20820e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20951602b2aSPavel Emelyanov {
21020e61da7SWANG Cong 	return 0;
21151602b2aSPavel Emelyanov }
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 
2191da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2201da177e4SLinus Torvalds {
2216126891cSVasily Averin 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
22761f5338dSEric Dumazet 
2281da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2291da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2301da177e4SLinus Torvalds 	kfree(ifa);
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds 
23340384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2341da177e4SLinus Torvalds {
23561f5338dSEric Dumazet 	/* Our reference to ifa->ifa_dev must be freed ASAP
23661f5338dSEric Dumazet 	 * to release the reference to the netdev the same way.
23761f5338dSEric Dumazet 	 * in_dev_put() -> in_dev_finish_destroy() -> netdev_put()
23861f5338dSEric Dumazet 	 */
23961f5338dSEric Dumazet 	call_rcu_hurry(&ifa->rcu_head, inet_rcu_free_ifa);
2401da177e4SLinus Torvalds }
2411da177e4SLinus Torvalds 
2429d40c84cSEric Dumazet static void in_dev_free_rcu(struct rcu_head *head)
2439d40c84cSEric Dumazet {
2449d40c84cSEric Dumazet 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2459d40c84cSEric Dumazet 
2469d40c84cSEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2479d40c84cSEric Dumazet 	kfree(idev);
2489d40c84cSEric Dumazet }
2499d40c84cSEric Dumazet 
2501da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2511da177e4SLinus Torvalds {
2521da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2531da177e4SLinus Torvalds 
254547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
255547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2561da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
25791df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2581da177e4SLinus Torvalds #endif
259d62607c3SJakub Kicinski 	netdev_put(dev, &idev->dev_tracker);
2601da177e4SLinus Torvalds 	if (!idev->dead)
2619f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2629f9354b9SEric Dumazet 	else
2639d40c84cSEric Dumazet 		call_rcu(&idev->rcu_head, in_dev_free_rcu);
2641da177e4SLinus Torvalds }
2659f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2661da177e4SLinus Torvalds 
26771e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2681da177e4SLinus Torvalds {
2691da177e4SLinus Torvalds 	struct in_device *in_dev;
27020e61da7SWANG Cong 	int err = -ENOMEM;
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds 	ASSERT_RTNL();
2731da177e4SLinus Torvalds 
2740da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2751da177e4SLinus Torvalds 	if (!in_dev)
2761da177e4SLinus Torvalds 		goto out;
277c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2789355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2791da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2801da177e4SLinus Torvalds 	in_dev->dev = dev;
2819f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2829f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2831da177e4SLinus Torvalds 		goto out_kfree;
2840187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2850187bdfbSBen Hutchings 		dev_disable_lro(dev);
2861da177e4SLinus Torvalds 	/* Reference in_dev->dev */
287d62607c3SJakub Kicinski 	netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL);
28830c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2897658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2901da177e4SLinus Torvalds 
29120e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
29220e61da7SWANG Cong 	if (err) {
29320e61da7SWANG Cong 		in_dev->dead = 1;
2941b49cd71SYang Yingliang 		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
29520e61da7SWANG Cong 		in_dev_put(in_dev);
29620e61da7SWANG Cong 		in_dev = NULL;
29720e61da7SWANG Cong 		goto out;
29820e61da7SWANG Cong 	}
2991da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
3001da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
3011da177e4SLinus Torvalds 		ip_mc_up(in_dev);
302483479ecSJarek Poplawski 
30330c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
304cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
305483479ecSJarek Poplawski out:
30620e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
3071da177e4SLinus Torvalds out_kfree:
3081da177e4SLinus Torvalds 	kfree(in_dev);
3091da177e4SLinus Torvalds 	in_dev = NULL;
3101da177e4SLinus Torvalds 	goto out;
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3141da177e4SLinus Torvalds {
3151da177e4SLinus Torvalds 	struct net_device *dev;
3162638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	ASSERT_RTNL();
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	dev = in_dev->dev;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	in_dev->dead = 1;
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3251da177e4SLinus Torvalds 
3262638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3271da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3281da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3291da177e4SLinus Torvalds 	}
3301da177e4SLinus Torvalds 
331a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3321da177e4SLinus Torvalds 
33351602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3341da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3351da177e4SLinus Torvalds 	arp_ifdown(dev);
3361da177e4SLinus Torvalds 
3379d40c84cSEric Dumazet 	in_dev_put(in_dev);
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
340ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3411da177e4SLinus Torvalds {
342d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
343d519e870SFlorian Westphal 
3441da177e4SLinus Torvalds 	rcu_read_lock();
345d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3461da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3471da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3481da177e4SLinus Torvalds 				rcu_read_unlock();
3491da177e4SLinus Torvalds 				return 1;
3501da177e4SLinus Torvalds 			}
3511da177e4SLinus Torvalds 		}
352d519e870SFlorian Westphal 	}
3531da177e4SLinus Torvalds 	rcu_read_unlock();
3541da177e4SLinus Torvalds 	return 0;
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
3572638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3582638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
35915e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3601da177e4SLinus Torvalds {
3618f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3622638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
363ac28b1ecSLiu Jian 	struct in_ifaddr __rcu **last_prim;
3640ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3650ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	ASSERT_RTNL();
3681da177e4SLinus Torvalds 
3692638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
370ac28b1ecSLiu Jian 	last_prim = ifap;
371fbd40ea0SDavid S. Miller 	if (in_dev->dead)
372fbd40ea0SDavid S. Miller 		goto no_promotions;
373fbd40ea0SDavid S. Miller 
3748f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3758f937c60SHarald Welte 	 * unless alias promotion is set
3768f937c60SHarald Welte 	 **/
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3792638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3801da177e4SLinus Torvalds 
3812638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3820ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3830ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
384ac28b1ecSLiu Jian 				last_prim = &ifa->ifa_next;
3850ff60a45SJamal Hadi Salim 
3861da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3871da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3881da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3891da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3900ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3911da177e4SLinus Torvalds 				continue;
3921da177e4SLinus Torvalds 			}
3931da177e4SLinus Torvalds 
3940ff60a45SJamal Hadi Salim 			if (!do_promote) {
395fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3961da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3971da177e4SLinus Torvalds 
39815e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
399e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
400e041c683SAlan Stern 						NETDEV_DOWN, ifa);
4011da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4028f937c60SHarald Welte 			} else {
4038f937c60SHarald Welte 				promote = ifa;
4048f937c60SHarald Welte 				break;
4058f937c60SHarald Welte 			}
4061da177e4SLinus Torvalds 		}
4071da177e4SLinus Torvalds 	}
4081da177e4SLinus Torvalds 
4092d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4102d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4112d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4122d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4132d230e2bSJulian Anastasov 	 */
4142638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4152d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4162d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4172d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4182d230e2bSJulian Anastasov 	}
4192d230e2bSJulian Anastasov 
420fbd40ea0SDavid S. Miller no_promotions:
4211da177e4SLinus Torvalds 	/* 2. Unlink it */
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
424fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4291da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4301da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4311da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4321da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4331da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4341da177e4SLinus Torvalds 	   So that, this order is correct.
4351da177e4SLinus Torvalds 	 */
43615e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
437e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4380ff60a45SJamal Hadi Salim 
4390ff60a45SJamal Hadi Salim 	if (promote) {
4402638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4410ff60a45SJamal Hadi Salim 
4422638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4430ff60a45SJamal Hadi Salim 		if (prev_prom) {
4442638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4452638eb8bSFlorian Westphal 
4462638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4476a9e9ceaSFlorian Westphal 
448ac28b1ecSLiu Jian 			last_sec = rtnl_dereference(*last_prim);
4492638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
450ac28b1ecSLiu Jian 			rcu_assign_pointer(*last_prim, promote);
4510ff60a45SJamal Hadi Salim 		}
4520ff60a45SJamal Hadi Salim 
4530ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
45415e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
455e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
456e041c683SAlan Stern 				NETDEV_UP, promote);
4572638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4582638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4590ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4600ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4610ff60a45SJamal Hadi Salim 					continue;
4620ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4630ff60a45SJamal Hadi Salim 		}
4640ff60a45SJamal Hadi Salim 
4650ff60a45SJamal Hadi Salim 	}
4666363097cSHerbert Xu 	if (destroy)
4671da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4681da177e4SLinus Torvalds }
4691da177e4SLinus Torvalds 
4702638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4712638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
472d6062cbbSThomas Graf 			 int destroy)
473d6062cbbSThomas Graf {
474d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
475d6062cbbSThomas Graf }
476d6062cbbSThomas Graf 
4775c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4785c766d64SJiri Pirko 
4795c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4805c766d64SJiri Pirko 
481d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
482de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4831da177e4SLinus Torvalds {
4842638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4851da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4863ad7d246SKrister Johansen 	struct in_validator_info ivi;
4872638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4883ad7d246SKrister Johansen 	int ret;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	ASSERT_RTNL();
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4931da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4941da177e4SLinus Torvalds 		return 0;
4951da177e4SLinus Torvalds 	}
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4981da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4991da177e4SLinus Torvalds 
5002e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
5012e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
5022e605463SMatteo Croce 
5032638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
5042638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
5052638eb8bSFlorian Westphal 
5062638eb8bSFlorian Westphal 	while (ifa1) {
5071da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
5081da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5091da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5101da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5111da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5121da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5131da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5141da177e4SLinus Torvalds 				return -EEXIST;
5151da177e4SLinus Torvalds 			}
5161da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
517b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value");
5181da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5191da177e4SLinus Torvalds 				return -EINVAL;
5201da177e4SLinus Torvalds 			}
5211da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5221da177e4SLinus Torvalds 		}
5232638eb8bSFlorian Westphal 
5242638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5252638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5261da177e4SLinus Torvalds 	}
5271da177e4SLinus Torvalds 
5283ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5293ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5303ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5313ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5323ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5333ad7d246SKrister Johansen 	 * not the flags.
5343ad7d246SKrister Johansen 	 */
5353ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5363ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
537de95e047SDavid Ahern 	ivi.extack = extack;
5383ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5393ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5403ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5413ad7d246SKrister Johansen 	if (ret) {
5423ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5433ad7d246SKrister Johansen 		return ret;
5443ad7d246SKrister Johansen 	}
5453ad7d246SKrister Johansen 
546d4150779SJason A. Donenfeld 	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
5471da177e4SLinus Torvalds 		ifap = last_primary;
5481da177e4SLinus Torvalds 
5492638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5502638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5511da177e4SLinus Torvalds 
552fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
553fd23c3b3SDavid S. Miller 
5545c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
555906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5565c766d64SJiri Pirko 
5571da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5581da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5591da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
56015e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
561e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	return 0;
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds 
566d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
567d6062cbbSThomas Graf {
568de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
569d6062cbbSThomas Graf }
570d6062cbbSThomas Graf 
5711da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5721da177e4SLinus Torvalds {
573e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5741da177e4SLinus Torvalds 
5751da177e4SLinus Torvalds 	ASSERT_RTNL();
5761da177e4SLinus Torvalds 
5771da177e4SLinus Torvalds 	if (!in_dev) {
5781da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5791da177e4SLinus Torvalds 		return -ENOBUFS;
5801da177e4SLinus Torvalds 	}
58171e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5821d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5831da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
584547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5851da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5861da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5871da177e4SLinus Torvalds 	}
588f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5891da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5901da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds 
5938723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5948723e1b4SEric Dumazet  * We dont take a reference on found in_device
5958723e1b4SEric Dumazet  */
5967fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5971da177e4SLinus Torvalds {
5981da177e4SLinus Torvalds 	struct net_device *dev;
5991da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
600c148fc2eSEric Dumazet 
601c148fc2eSEric Dumazet 	rcu_read_lock();
602c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
6031da177e4SLinus Torvalds 	if (dev)
6048723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
605c148fc2eSEric Dumazet 	rcu_read_unlock();
6061da177e4SLinus Torvalds 	return in_dev;
6071da177e4SLinus Torvalds }
6089f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6111da177e4SLinus Torvalds 
61260cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
61360cad5daSAl Viro 				    __be32 mask)
6141da177e4SLinus Torvalds {
615d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
616d519e870SFlorian Westphal 
6171da177e4SLinus Torvalds 	ASSERT_RTNL();
6181da177e4SLinus Torvalds 
619d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6201da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6211da177e4SLinus Torvalds 			return ifa;
622d519e870SFlorian Westphal 	}
6231da177e4SLinus Torvalds 	return NULL;
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds 
626690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
627690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
62893a714d6SMadhu Challa {
629690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
63093a714d6SMadhu Challa 	struct ip_mreqn mreq = {
63193a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
63293a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
63393a714d6SMadhu Challa 	};
634690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
63593a714d6SMadhu Challa 	int ret;
63693a714d6SMadhu Challa 
63793a714d6SMadhu Challa 	ASSERT_RTNL();
63893a714d6SMadhu Challa 
63993a714d6SMadhu Challa 	lock_sock(sk);
64093a714d6SMadhu Challa 	if (join)
64154ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
64293a714d6SMadhu Challa 	else
64354ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
64493a714d6SMadhu Challa 	release_sock(sk);
64593a714d6SMadhu Challa 
64693a714d6SMadhu Challa 	return ret;
647690cc863STaras Chornyi #else
648690cc863STaras Chornyi 	return -EOPNOTSUPP;
649690cc863STaras Chornyi #endif
65093a714d6SMadhu Challa }
65193a714d6SMadhu Challa 
652c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
653c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6541da177e4SLinus Torvalds {
6553b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6562638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
657dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6581da177e4SLinus Torvalds 	struct in_device *in_dev;
659dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6602638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
66130e2379eSMenglong Dong 	int err;
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds 	ASSERT_RTNL();
6641da177e4SLinus Torvalds 
6658cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6668cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
667dfdd5fd4SThomas Graf 	if (err < 0)
668dfdd5fd4SThomas Graf 		goto errout;
669dfdd5fd4SThomas Graf 
670dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6717fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
67251456b29SIan Morris 	if (!in_dev) {
673b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
674dfdd5fd4SThomas Graf 		err = -ENODEV;
675dfdd5fd4SThomas Graf 		goto errout;
676dfdd5fd4SThomas Graf 	}
677dfdd5fd4SThomas Graf 
6782638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6791da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
680dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
68167b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6821da177e4SLinus Torvalds 			continue;
683dfdd5fd4SThomas Graf 
684dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
685dfdd5fd4SThomas Graf 			continue;
686dfdd5fd4SThomas Graf 
687dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
688dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
68967b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
690dfdd5fd4SThomas Graf 			continue;
691dfdd5fd4SThomas Graf 
69293a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
693690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
69415e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6951da177e4SLinus Torvalds 		return 0;
6961da177e4SLinus Torvalds 	}
697dfdd5fd4SThomas Graf 
698b4672c73SHangbin Liu 	NL_SET_ERR_MSG(extack, "ipv4: Address not found");
699dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
700dfdd5fd4SThomas Graf errout:
701dfdd5fd4SThomas Graf 	return err;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
7045c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
7055c766d64SJiri Pirko 
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 
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 
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 
8785c753978SThomas Graf 	ifa = inet_alloc_ifa();
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 	in_dev_hold(in_dev);
8895c753978SThomas Graf 
89051456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8915c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8925c753978SThomas Graf 
893fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8941da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8951da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
896ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
897ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8981da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8991da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
9005c753978SThomas Graf 
90167b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
90267b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
9035c753978SThomas Graf 
9045c753978SThomas Graf 	if (tb[IFA_BROADCAST])
90567b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
9065c753978SThomas Graf 
9075c753978SThomas Graf 	if (tb[IFA_LABEL])
908872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
9091da177e4SLinus Torvalds 	else
9101da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
9111da177e4SLinus Torvalds 
912af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
913af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
914af4d768aSDavid Ahern 
91547f0bd50SJacques de Laval 	if (tb[IFA_PROTO])
91647f0bd50SJacques de Laval 		ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
91747f0bd50SJacques de Laval 
9185c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
9195c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
9205c766d64SJiri Pirko 
9215c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
9225c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
923b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid");
9245c766d64SJiri Pirko 			err = -EINVAL;
925446266b0SDaniel Borkmann 			goto errout_free;
9265c766d64SJiri Pirko 		}
9275c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
9285c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9295c766d64SJiri Pirko 	}
9305c766d64SJiri Pirko 
9315c753978SThomas Graf 	return ifa;
9325c753978SThomas Graf 
933446266b0SDaniel Borkmann errout_free:
934446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9355c753978SThomas Graf errout:
9365c753978SThomas Graf 	return ERR_PTR(err);
9375c753978SThomas Graf }
9385c753978SThomas Graf 
9395c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9405c766d64SJiri Pirko {
9415c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
942ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9435c766d64SJiri Pirko 
9445c766d64SJiri Pirko 	if (!ifa->ifa_local)
9455c766d64SJiri Pirko 		return NULL;
9465c766d64SJiri Pirko 
947ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9485c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9495c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9505c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9515c766d64SJiri Pirko 			return ifa1;
9525c766d64SJiri Pirko 	}
9535c766d64SJiri Pirko 	return NULL;
9545c766d64SJiri Pirko }
9555c766d64SJiri Pirko 
956c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
957c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9585c753978SThomas Graf {
9593b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9605c753978SThomas Graf 	struct in_ifaddr *ifa;
9615c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9625c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9635c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9645c753978SThomas Graf 
9655c753978SThomas Graf 	ASSERT_RTNL();
9665c753978SThomas Graf 
967dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9685c753978SThomas Graf 	if (IS_ERR(ifa))
9695c753978SThomas Graf 		return PTR_ERR(ifa);
9705c753978SThomas Graf 
9715c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9725c766d64SJiri Pirko 	if (!ifa_existing) {
9735c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
974614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9755c766d64SJiri Pirko 		 */
9765c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
97793a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
978690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
97993a714d6SMadhu Challa 
98093a714d6SMadhu Challa 			if (ret < 0) {
981b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed");
98293a714d6SMadhu Challa 				inet_free_ifa(ifa);
98393a714d6SMadhu Challa 				return ret;
98493a714d6SMadhu Challa 			}
98593a714d6SMadhu Challa 		}
986de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
987de95e047SDavid Ahern 					 extack);
9885c766d64SJiri Pirko 	} else {
989af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
9905c4a9aa8SPetr Machata 		u8 new_proto = ifa->ifa_proto;
991af4d768aSDavid Ahern 
9925c766d64SJiri Pirko 		inet_free_ifa(ifa);
9935c766d64SJiri Pirko 
9945c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
995b4672c73SHangbin Liu 		    !(nlh->nlmsg_flags & NLM_F_REPLACE)) {
996b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: Address already assigned");
9975c766d64SJiri Pirko 			return -EEXIST;
998b4672c73SHangbin Liu 		}
99934e2ed34SJiri Pirko 		ifa = ifa_existing;
1000af4d768aSDavid Ahern 
1001af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
1002af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
1003af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
1004af4d768aSDavid Ahern 		}
1005af4d768aSDavid Ahern 
10065c4a9aa8SPetr Machata 		ifa->ifa_proto = new_proto;
10075c4a9aa8SPetr Machata 
100834e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
100905a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
1010906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
1011906e073fSviresh kumar 				&check_lifetime_work, 0);
101234e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
10135c766d64SJiri Pirko 	}
10145c766d64SJiri Pirko 	return 0;
10151da177e4SLinus Torvalds }
10161da177e4SLinus Torvalds 
10171da177e4SLinus Torvalds /*
10181da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
10191da177e4SLinus Torvalds  */
10201da177e4SLinus Torvalds 
102140384999SEric Dumazet static int inet_abc_len(__be32 addr)
10221da177e4SLinus Torvalds {
10231da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
10241da177e4SLinus Torvalds 
102565cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
10261da177e4SLinus Torvalds 		rc = 0;
10271da177e4SLinus Torvalds 	else {
1028714e85beSAl Viro 		__u32 haddr = ntohl(addr);
1029714e85beSAl Viro 		if (IN_CLASSA(haddr))
10301da177e4SLinus Torvalds 			rc = 8;
1031714e85beSAl Viro 		else if (IN_CLASSB(haddr))
10321da177e4SLinus Torvalds 			rc = 16;
1033714e85beSAl Viro 		else if (IN_CLASSC(haddr))
10341da177e4SLinus Torvalds 			rc = 24;
103565cab850SDave Taht 		else if (IN_CLASSE(haddr))
103665cab850SDave Taht 			rc = 32;
10371da177e4SLinus Torvalds 	}
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds 	return rc;
10401da177e4SLinus Torvalds }
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds 
104303aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10441da177e4SLinus Torvalds {
10451da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
104603aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10472638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10481da177e4SLinus Torvalds 	struct in_device *in_dev;
10491da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10501da177e4SLinus Torvalds 	struct net_device *dev;
10511da177e4SLinus Torvalds 	char *colon;
10521da177e4SLinus Torvalds 	int ret = -EFAULT;
10531da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10541da177e4SLinus Torvalds 
105503aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	/* save original address for comparison */
10581da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10591da177e4SLinus Torvalds 
106003aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10611da177e4SLinus Torvalds 	if (colon)
10621da177e4SLinus Torvalds 		*colon = 0;
10631da177e4SLinus Torvalds 
106403aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	switch (cmd) {
10671da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10681da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10691da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10701da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10711da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10721da177e4SLinus Torvalds 		   so that we do not impose a lock.
10731da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10741da177e4SLinus Torvalds 		 */
10751da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10761da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10771da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10781da177e4SLinus Torvalds 		break;
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1081bf5b30b8SZhao Hongjiang 		ret = -EPERM;
108252e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10831da177e4SLinus Torvalds 			goto out;
10841da177e4SLinus Torvalds 		break;
10851da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10861da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10871da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10881da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1089bf5b30b8SZhao Hongjiang 		ret = -EPERM;
109052e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10911da177e4SLinus Torvalds 			goto out;
10921da177e4SLinus Torvalds 		ret = -EINVAL;
10931da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10941da177e4SLinus Torvalds 			goto out;
10951da177e4SLinus Torvalds 		break;
10961da177e4SLinus Torvalds 	default:
10971da177e4SLinus Torvalds 		ret = -EINVAL;
10981da177e4SLinus Torvalds 		goto out;
10991da177e4SLinus Torvalds 	}
11001da177e4SLinus Torvalds 
11011da177e4SLinus Torvalds 	rtnl_lock();
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds 	ret = -ENODEV;
110403aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
11059f9354b9SEric Dumazet 	if (!dev)
11061da177e4SLinus Torvalds 		goto done;
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds 	if (colon)
11091da177e4SLinus Torvalds 		*colon = ':';
11101da177e4SLinus Torvalds 
11119f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
11129f9354b9SEric Dumazet 	if (in_dev) {
11131da177e4SLinus Torvalds 		if (tryaddrmatch) {
11141da177e4SLinus Torvalds 			/* Matthias Andree */
11151da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
11161da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
11171da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
11181da177e4SLinus Torvalds 			   This is checked above. */
11192638eb8bSFlorian Westphal 
11202638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11212638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11221da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
112303aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
11241da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
11256c91afe1SDavid S. Miller 							ifa->ifa_local) {
11261da177e4SLinus Torvalds 					break; /* found */
11271da177e4SLinus Torvalds 				}
11281da177e4SLinus Torvalds 			}
11291da177e4SLinus Torvalds 		}
11301da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
11311da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
11321da177e4SLinus Torvalds 		   comparing just the label */
11331da177e4SLinus Torvalds 		if (!ifa) {
11342638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11352638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11361da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
113703aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11381da177e4SLinus Torvalds 					break;
11391da177e4SLinus Torvalds 		}
11401da177e4SLinus Torvalds 	}
11411da177e4SLinus Torvalds 
11421da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11431da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11441da177e4SLinus Torvalds 		goto done;
11451da177e4SLinus Torvalds 
11461da177e4SLinus Torvalds 	switch (cmd) {
11471da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
114830e948a3STonghao Zhang 		ret = 0;
11491da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
115003aef17bSAl Viro 		break;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
115330e948a3STonghao Zhang 		ret = 0;
11541da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
115503aef17bSAl Viro 		break;
11561da177e4SLinus Torvalds 
11571da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
115830e948a3STonghao Zhang 		ret = 0;
11591da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
116003aef17bSAl Viro 		break;
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
116330e948a3STonghao Zhang 		ret = 0;
11641da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
116503aef17bSAl Viro 		break;
11661da177e4SLinus Torvalds 
11671da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11681da177e4SLinus Torvalds 		if (colon) {
11691da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11701da177e4SLinus Torvalds 			if (!ifa)
11711da177e4SLinus Torvalds 				break;
11721da177e4SLinus Torvalds 			ret = 0;
117303aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11741da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11751da177e4SLinus Torvalds 			break;
11761da177e4SLinus Torvalds 		}
1177567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11781da177e4SLinus Torvalds 		break;
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11811da177e4SLinus Torvalds 		ret = -EINVAL;
11821da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11831da177e4SLinus Torvalds 			break;
11841da177e4SLinus Torvalds 
11851da177e4SLinus Torvalds 		if (!ifa) {
11861da177e4SLinus Torvalds 			ret = -ENOBUFS;
11879f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11889f9354b9SEric Dumazet 			if (!ifa)
11891da177e4SLinus Torvalds 				break;
1190c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11911da177e4SLinus Torvalds 			if (colon)
119203aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11931da177e4SLinus Torvalds 			else
11941da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11951da177e4SLinus Torvalds 		} else {
11961da177e4SLinus Torvalds 			ret = 0;
11971da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11981da177e4SLinus Torvalds 				break;
11991da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12001da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1201148f9729SBjorn Mork 			ifa->ifa_scope = 0;
12021da177e4SLinus Torvalds 		}
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
12051da177e4SLinus Torvalds 
12061da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
12071da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
12081da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
12091da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12101da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
12111da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
12121da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
12131da177e4SLinus Torvalds 		} else {
12141da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
12151da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
12161da177e4SLinus Torvalds 		}
12175c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
12181da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
12191da177e4SLinus Torvalds 		break;
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
12221da177e4SLinus Torvalds 		ret = 0;
12231da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
12241da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12251da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
12261da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12271da177e4SLinus Torvalds 		}
12281da177e4SLinus Torvalds 		break;
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
12311da177e4SLinus Torvalds 		ret = 0;
12321da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
12331da177e4SLinus Torvalds 			break;
12341da177e4SLinus Torvalds 		ret = -EINVAL;
12351da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12361da177e4SLinus Torvalds 			break;
12371da177e4SLinus Torvalds 		ret = 0;
12381da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12391da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12401da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12411da177e4SLinus Torvalds 		break;
12421da177e4SLinus Torvalds 
12431da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12441da177e4SLinus Torvalds 
12451da177e4SLinus Torvalds 		/*
12461da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12471da177e4SLinus Torvalds 		 */
12481da177e4SLinus Torvalds 		ret = -EINVAL;
12491da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12501da177e4SLinus Torvalds 			break;
12511da177e4SLinus Torvalds 		ret = 0;
12521da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1253a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12541da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12551da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12561da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12571da177e4SLinus Torvalds 
12581da177e4SLinus Torvalds 			/* See if current broadcast address matches
12591da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12601da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12611da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12621da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12631da177e4SLinus Torvalds 			 */
12641da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12651da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12661da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1267dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12681da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12691da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12701da177e4SLinus Torvalds 			}
12711da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12721da177e4SLinus Torvalds 		}
12731da177e4SLinus Torvalds 		break;
12741da177e4SLinus Torvalds 	}
12751da177e4SLinus Torvalds done:
12761da177e4SLinus Torvalds 	rtnl_unlock();
12771da177e4SLinus Torvalds out:
12781da177e4SLinus Torvalds 	return ret;
12791da177e4SLinus Torvalds }
12801da177e4SLinus Torvalds 
1281b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12821da177e4SLinus Torvalds {
1283e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1284ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12851da177e4SLinus Torvalds 	struct ifreq ifr;
12861da177e4SLinus Torvalds 	int done = 0;
12871da177e4SLinus Torvalds 
128836fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
128936fd633eSAl Viro 		goto out;
129036fd633eSAl Viro 
12919f9354b9SEric Dumazet 	if (!in_dev)
12921da177e4SLinus Torvalds 		goto out;
12931da177e4SLinus Torvalds 
1294ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12951da177e4SLinus Torvalds 		if (!buf) {
129636fd633eSAl Viro 			done += size;
12971da177e4SLinus Torvalds 			continue;
12981da177e4SLinus Torvalds 		}
129936fd633eSAl Viro 		if (len < size)
13001da177e4SLinus Torvalds 			break;
13011da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
13021da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
13051da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
13061da177e4SLinus Torvalds 								ifa->ifa_local;
13071da177e4SLinus Torvalds 
130836fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
13091da177e4SLinus Torvalds 			done = -EFAULT;
13101da177e4SLinus Torvalds 			break;
13111da177e4SLinus Torvalds 		}
131236fd633eSAl Viro 		len  -= size;
131336fd633eSAl Viro 		done += size;
13141da177e4SLinus Torvalds 	}
13151da177e4SLinus Torvalds out:
13161da177e4SLinus Torvalds 	return done;
13171da177e4SLinus Torvalds }
13181da177e4SLinus Torvalds 
13198b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
13208b57fd1eSGao Feng 				 int scope)
13218b57fd1eSGao Feng {
1322d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1323d519e870SFlorian Westphal 
1324d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
13253ddc2231SEric Dumazet 		if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY)
1326d519e870SFlorian Westphal 			continue;
13278b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
13288b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
13298b57fd1eSGao Feng 			return ifa->ifa_local;
1330d519e870SFlorian Westphal 	}
13318b57fd1eSGao Feng 
13328b57fd1eSGao Feng 	return 0;
13338b57fd1eSGao Feng }
13348b57fd1eSGao Feng 
1335a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13361da177e4SLinus Torvalds {
1337d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1338a61ced5dSAl Viro 	__be32 addr = 0;
1339d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13401da177e4SLinus Torvalds 	struct in_device *in_dev;
1341c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13423f2fb9a8SDavid Ahern 	int master_idx;
13431da177e4SLinus Torvalds 
13441da177e4SLinus Torvalds 	rcu_read_lock();
1345e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13461da177e4SLinus Torvalds 	if (!in_dev)
13471da177e4SLinus Torvalds 		goto no_in_dev;
13481da177e4SLinus Torvalds 
1349d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1350d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1351d8c444d5SShijie Luo 
1352d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
13533ddc2231SEric Dumazet 		if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY)
1354d519e870SFlorian Westphal 			continue;
1355d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13561da177e4SLinus Torvalds 			continue;
13571da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13581da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13591da177e4SLinus Torvalds 			break;
13601da177e4SLinus Torvalds 		}
13611da177e4SLinus Torvalds 		if (!addr)
13621da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1363d519e870SFlorian Westphal 	}
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 	if (addr)
1366c6d14c84SEric Dumazet 		goto out_unlock;
13679f9354b9SEric Dumazet no_in_dev:
13683f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13691da177e4SLinus Torvalds 
137017b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
137117b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
137217b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
137317b693cdSDavid Lamparter 	 * equality check in the loop below.
137417b693cdSDavid Lamparter 	 */
137517b693cdSDavid Lamparter 	if (master_idx &&
137617b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
137717b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13788b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13798b57fd1eSGao Feng 		if (addr)
138017b693cdSDavid Lamparter 			goto out_unlock;
138117b693cdSDavid Lamparter 	}
138217b693cdSDavid Lamparter 
13831da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1384ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13851da177e4SLinus Torvalds 	   in dev_base list.
13861da177e4SLinus Torvalds 	 */
1387c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13883f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13893f2fb9a8SDavid Ahern 			continue;
13903f2fb9a8SDavid Ahern 
13919f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13929f9354b9SEric Dumazet 		if (!in_dev)
13931da177e4SLinus Torvalds 			continue;
13941da177e4SLinus Torvalds 
13958b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13968b57fd1eSGao Feng 		if (addr)
1397c6d14c84SEric Dumazet 			goto out_unlock;
13981da177e4SLinus Torvalds 	}
1399c6d14c84SEric Dumazet out_unlock:
14001da177e4SLinus Torvalds 	rcu_read_unlock();
14011da177e4SLinus Torvalds 	return addr;
14021da177e4SLinus Torvalds }
14039f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
14041da177e4SLinus Torvalds 
140560cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
140660cad5daSAl Viro 			      __be32 local, int scope)
14071da177e4SLinus Torvalds {
1408650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1409ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1410a144ea4bSAl Viro 	__be32 addr = 0;
1411ef11db33SFlorian Westphal 	int same = 0;
14121da177e4SLinus Torvalds 
1413650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1414650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1415650638a7SShijie Luo 
1416ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1417650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1418650638a7SShijie Luo 
14191da177e4SLinus Torvalds 		if (!addr &&
14201da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1421650638a7SShijie Luo 		    min_scope <= scope) {
14221da177e4SLinus Torvalds 			addr = ifa->ifa_local;
14231da177e4SLinus Torvalds 			if (same)
14241da177e4SLinus Torvalds 				break;
14251da177e4SLinus Torvalds 		}
14261da177e4SLinus Torvalds 		if (!same) {
14271da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
14281da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
14291da177e4SLinus Torvalds 			if (same && addr) {
14301da177e4SLinus Torvalds 				if (local || !dst)
14311da177e4SLinus Torvalds 					break;
14321da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
14331da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
14341da177e4SLinus Torvalds 					break;
14351da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1436650638a7SShijie Luo 				if (min_scope <= scope) {
14371da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14381da177e4SLinus Torvalds 					break;
14391da177e4SLinus Torvalds 				}
14401da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14411da177e4SLinus Torvalds 				same = 0;
14421da177e4SLinus Torvalds 			}
14431da177e4SLinus Torvalds 		}
1444ef11db33SFlorian Westphal 	}
14451da177e4SLinus Torvalds 
14461da177e4SLinus Torvalds 	return same ? addr : 0;
14471da177e4SLinus Torvalds }
14481da177e4SLinus Torvalds 
14491da177e4SLinus Torvalds /*
14501da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1451b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1452b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14531da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14541da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14551da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14561da177e4SLinus Torvalds  */
1457b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14589bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14591da177e4SLinus Torvalds {
146060cad5daSAl Viro 	__be32 addr = 0;
14619bd85e32SDenis V. Lunev 	struct net_device *dev;
14621da177e4SLinus Torvalds 
146300db4124SIan Morris 	if (in_dev)
14649bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 	rcu_read_lock();
1467c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14689f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14699f9354b9SEric Dumazet 		if (in_dev) {
14701da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14711da177e4SLinus Torvalds 			if (addr)
14721da177e4SLinus Torvalds 				break;
14731da177e4SLinus Torvalds 		}
14741da177e4SLinus Torvalds 	}
14751da177e4SLinus Torvalds 	rcu_read_unlock();
14761da177e4SLinus Torvalds 
14771da177e4SLinus Torvalds 	return addr;
14781da177e4SLinus Torvalds }
1479eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds /*
14821da177e4SLinus Torvalds  *	Device notifier
14831da177e4SLinus Torvalds  */
14841da177e4SLinus Torvalds 
14851da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14861da177e4SLinus Torvalds {
1487e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14881da177e4SLinus Torvalds }
14899f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14901da177e4SLinus Torvalds 
14911da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14921da177e4SLinus Torvalds {
1493e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14941da177e4SLinus Torvalds }
14959f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14961da177e4SLinus Torvalds 
14973ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14983ad7d246SKrister Johansen {
14993ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
15003ad7d246SKrister Johansen }
15013ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
15023ad7d246SKrister Johansen 
15033ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
15043ad7d246SKrister Johansen {
15053ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
15063ad7d246SKrister Johansen 	    nb);
15073ad7d246SKrister Johansen }
15083ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
15093ad7d246SKrister Johansen 
15109f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
15119f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
15121da177e4SLinus Torvalds */
15131da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
15141da177e4SLinus Torvalds {
15151da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
15161da177e4SLinus Torvalds 	int named = 0;
15171da177e4SLinus Torvalds 
1518ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
15191da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
15201da177e4SLinus Torvalds 
15211da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
15221da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15231da177e4SLinus Torvalds 		if (named++ == 0)
1524573bf470SThomas Graf 			goto skip;
152544344b2aSMark McLoughlin 		dot = strchr(old, ':');
152651456b29SIan Morris 		if (!dot) {
15271da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
15281da177e4SLinus Torvalds 			dot = old;
15291da177e4SLinus Torvalds 		}
15309f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
15311da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
15329f9354b9SEric Dumazet 		else
15331da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1534573bf470SThomas Graf skip:
1535573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15361da177e4SLinus Torvalds 	}
15371da177e4SLinus Torvalds }
15381da177e4SLinus Torvalds 
1539d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1540d11327adSIan Campbell 					struct in_device *in_dev)
1541d11327adSIan Campbell 
1542d11327adSIan Campbell {
1543ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1544d11327adSIan Campbell 
1545ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1546d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15476c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15486c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1549d11327adSIan Campbell 			 dev->dev_addr, NULL);
1550d11327adSIan Campbell 	}
1551b76d0789SZoltan Kiss }
1552d11327adSIan Campbell 
15531da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15541da177e4SLinus Torvalds 
15551da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15561da177e4SLinus Torvalds 			 void *ptr)
15571da177e4SLinus Torvalds {
1558351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1559748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15601da177e4SLinus Torvalds 
15611da177e4SLinus Torvalds 	ASSERT_RTNL();
15621da177e4SLinus Torvalds 
15631da177e4SLinus Torvalds 	if (!in_dev) {
15648030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15651da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
156620e61da7SWANG Cong 			if (IS_ERR(in_dev))
156720e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15680cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
156942f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
157042f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15711da177e4SLinus Torvalds 			}
157206770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
157306770843SBreno Leitao 			/* Re-enabling IP */
157406770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
157506770843SBreno Leitao 				in_dev = inetdev_init(dev);
15768030f544SHerbert Xu 		}
15771da177e4SLinus Torvalds 		goto out;
15781da177e4SLinus Torvalds 	}
15791da177e4SLinus Torvalds 
15801da177e4SLinus Torvalds 	switch (event) {
15811da177e4SLinus Torvalds 	case NETDEV_REGISTER:
158291df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1583a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15841da177e4SLinus Torvalds 		break;
15851da177e4SLinus Torvalds 	case NETDEV_UP:
158606770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15871da177e4SLinus Torvalds 			break;
15880cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15899f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15909f9354b9SEric Dumazet 
15919f9354b9SEric Dumazet 			if (ifa) {
1592fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15931da177e4SLinus Torvalds 				ifa->ifa_local =
15941da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15951da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15961da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15971da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15981da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15991da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
16001da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
16015c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
16025c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1603dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1604dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
16051da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
16061da177e4SLinus Torvalds 			}
16071da177e4SLinus Torvalds 		}
16081da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1609a8eceea8SJoe Perches 		fallthrough;
1610eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1611d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1612d11327adSIan Campbell 			break;
1613a8eceea8SJoe Perches 		fallthrough;
1614d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1615a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1616d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
16171da177e4SLinus Torvalds 		break;
16181da177e4SLinus Torvalds 	case NETDEV_DOWN:
16191da177e4SLinus Torvalds 		ip_mc_down(in_dev);
16201da177e4SLinus Torvalds 		break;
162193d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
162275c78500SMoni Shoua 		ip_mc_unmap(in_dev);
162375c78500SMoni Shoua 		break;
162493d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
162575c78500SMoni Shoua 		ip_mc_remap(in_dev);
162675c78500SMoni Shoua 		break;
16271da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
162806770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
16291da177e4SLinus Torvalds 			break;
163006770843SBreno Leitao 		/* disable IP when MTU is not enough */
1631a8eceea8SJoe Perches 		fallthrough;
16321da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
16331da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
16341da177e4SLinus Torvalds 		break;
16351da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16361da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16371da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16381da177e4SLinus Torvalds 		 */
16391da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16401da177e4SLinus Torvalds 
164151602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
164266f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16431da177e4SLinus Torvalds 		break;
16441da177e4SLinus Torvalds 	}
16451da177e4SLinus Torvalds out:
16461da177e4SLinus Torvalds 	return NOTIFY_DONE;
16471da177e4SLinus Torvalds }
16481da177e4SLinus Torvalds 
16491da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16501da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16511da177e4SLinus Torvalds };
16521da177e4SLinus Torvalds 
165340384999SEric Dumazet static size_t inet_nlmsg_size(void)
1654339bf98fSThomas Graf {
1655339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1656339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1657339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1658339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1659ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
166063b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
166147f0bd50SJacques de Laval 	       + nla_total_size(1)  /* IFA_PROTO */
1662af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
166363b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1664339bf98fSThomas Graf }
1665339bf98fSThomas Graf 
16665c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16675c766d64SJiri Pirko {
16685c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16695c766d64SJiri Pirko }
16705c766d64SJiri Pirko 
16715c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16725c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16735c766d64SJiri Pirko {
16745c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16755c766d64SJiri Pirko 
16765c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16775c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16785c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16795c766d64SJiri Pirko 	ci.ifa_valid = valid;
16805c766d64SJiri Pirko 
16815c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16825c766d64SJiri Pirko }
16835c766d64SJiri Pirko 
1684cdb2f80fSEric Dumazet static int inet_fill_ifaddr(struct sk_buff *skb, const struct in_ifaddr *ifa,
1685978a46faSChristian Brauner 			    struct inet_fill_args *args)
16861da177e4SLinus Torvalds {
16871da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16881da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16893cd3e72cSEric Dumazet 	unsigned long tstamp;
16905c766d64SJiri Pirko 	u32 preferred, valid;
16911af7f88aSEric Dumazet 	u32 flags;
16921da177e4SLinus Torvalds 
1693978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1694978a46faSChristian Brauner 			args->flags);
169551456b29SIan Morris 	if (!nlh)
169626932566SPatrick McHardy 		return -EMSGSIZE;
169747f68512SThomas Graf 
169847f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16991da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
17001da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
17011af7f88aSEric Dumazet 
17021af7f88aSEric Dumazet 	flags = READ_ONCE(ifa->ifa_flags);
17031af7f88aSEric Dumazet 	/* Warning : ifm->ifa_flags is an __u8, it holds only 8 bits.
17041af7f88aSEric Dumazet 	 * The 32bit value is given in IFA_FLAGS attribute.
17051af7f88aSEric Dumazet 	 */
17061af7f88aSEric Dumazet 	ifm->ifa_flags = (__u8)flags;
17071af7f88aSEric Dumazet 
17081da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
17091da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
17101da177e4SLinus Torvalds 
1711978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1712978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1713d3807145SChristian Brauner 		goto nla_put_failure;
1714d3807145SChristian Brauner 
17153cd3e72cSEric Dumazet 	tstamp = READ_ONCE(ifa->ifa_tstamp);
17161af7f88aSEric Dumazet 	if (!(flags & IFA_F_PERMANENT)) {
17179f6fa3c4SEric Dumazet 		preferred = READ_ONCE(ifa->ifa_preferred_lft);
1718a5fcf74dSEric Dumazet 		valid = READ_ONCE(ifa->ifa_valid_lft);
17195c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
17203cd3e72cSEric Dumazet 			long tval = (jiffies - tstamp) / HZ;
17215c766d64SJiri Pirko 
17225c766d64SJiri Pirko 			if (preferred > tval)
17235c766d64SJiri Pirko 				preferred -= tval;
17245c766d64SJiri Pirko 			else
17255c766d64SJiri Pirko 				preferred = 0;
17265c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
17275c766d64SJiri Pirko 				if (valid > tval)
17285c766d64SJiri Pirko 					valid -= tval;
17295c766d64SJiri Pirko 				else
17305c766d64SJiri Pirko 					valid = 0;
17315c766d64SJiri Pirko 			}
17325c766d64SJiri Pirko 		}
17335c766d64SJiri Pirko 	} else {
17345c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
17355c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
17365c766d64SJiri Pirko 	}
1737f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1738930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1739f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1740930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1741f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1742930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1743f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
17445c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
174547f0bd50SJacques de Laval 	    (ifa->ifa_proto &&
174647f0bd50SJacques de Laval 	     nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
17471af7f88aSEric Dumazet 	    nla_put_u32(skb, IFA_FLAGS, flags) ||
1748af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1749af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
17503cd3e72cSEric Dumazet 	    put_cacheinfo(skb, READ_ONCE(ifa->ifa_cstamp), tstamp,
17515c766d64SJiri Pirko 			  preferred, valid))
1752f3756b79SDavid S. Miller 		goto nla_put_failure;
175347f68512SThomas Graf 
1754053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1755053c095aSJohannes Berg 	return 0;
175647f68512SThomas Graf 
175747f68512SThomas Graf nla_put_failure:
175826932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
175926932566SPatrick McHardy 	return -EMSGSIZE;
17601da177e4SLinus Torvalds }
17611da177e4SLinus Torvalds 
1762c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1763c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1764c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17655fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1766c33078e3SDavid Ahern {
17675fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1768c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1769c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1770c33078e3SDavid Ahern 	int err, i;
1771c33078e3SDavid Ahern 
1772c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1773c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1774c33078e3SDavid Ahern 		return -EINVAL;
1775c33078e3SDavid Ahern 	}
1776c33078e3SDavid Ahern 
1777c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1778c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1779c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1780c33078e3SDavid Ahern 		return -EINVAL;
1781c33078e3SDavid Ahern 	}
17825fcd266aSDavid Ahern 
17835fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17845fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17855fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17865fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1787c33078e3SDavid Ahern 	}
1788c33078e3SDavid Ahern 
17898cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1790c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1791c33078e3SDavid Ahern 	if (err < 0)
1792c33078e3SDavid Ahern 		return err;
1793c33078e3SDavid Ahern 
1794c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1795c33078e3SDavid Ahern 		if (!tb[i])
1796c33078e3SDavid Ahern 			continue;
1797c33078e3SDavid Ahern 
1798c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1799c33078e3SDavid Ahern 			struct net *net;
1800c33078e3SDavid Ahern 
1801c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1802c33078e3SDavid Ahern 
1803c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1804c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1805bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1806c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1807c33078e3SDavid Ahern 				return PTR_ERR(net);
1808c33078e3SDavid Ahern 			}
1809c33078e3SDavid Ahern 			*tgt_net = net;
1810c33078e3SDavid Ahern 		} else {
1811c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1812c33078e3SDavid Ahern 			return -EINVAL;
1813c33078e3SDavid Ahern 		}
1814c33078e3SDavid Ahern 	}
1815c33078e3SDavid Ahern 
1816c33078e3SDavid Ahern 	return 0;
1817c33078e3SDavid Ahern }
1818c33078e3SDavid Ahern 
18191c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
1820cdb2f80fSEric Dumazet 			    struct netlink_callback *cb, int *s_ip_idx,
18211c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
18221c98eca4SDavid Ahern {
18231c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
18241c98eca4SDavid Ahern 	int ip_idx = 0;
18251c98eca4SDavid Ahern 	int err;
18261c98eca4SDavid Ahern 
1827cdb2f80fSEric Dumazet 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1828cdb2f80fSEric Dumazet 		if (ip_idx < *s_ip_idx) {
1829ef11db33SFlorian Westphal 			ip_idx++;
18301c98eca4SDavid Ahern 			continue;
1831ef11db33SFlorian Westphal 		}
18321c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
18331c98eca4SDavid Ahern 		if (err < 0)
18341c98eca4SDavid Ahern 			goto done;
18351c98eca4SDavid Ahern 
18361c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1837ef11db33SFlorian Westphal 		ip_idx++;
18381c98eca4SDavid Ahern 	}
18391c98eca4SDavid Ahern 	err = 0;
1840cdb2f80fSEric Dumazet 	ip_idx = 0;
18411c98eca4SDavid Ahern done:
1842cdb2f80fSEric Dumazet 	*s_ip_idx = ip_idx;
18431c98eca4SDavid Ahern 
18441c98eca4SDavid Ahern 	return err;
18451c98eca4SDavid Ahern }
18461c98eca4SDavid Ahern 
1847081a0e3bSEric Dumazet /* Combine dev_addr_genid and dev_base_seq to detect changes.
1848081a0e3bSEric Dumazet  */
1849081a0e3bSEric Dumazet static u32 inet_base_seq(const struct net *net)
1850081a0e3bSEric Dumazet {
1851081a0e3bSEric Dumazet 	u32 res = atomic_read(&net->ipv4.dev_addr_genid) +
1852590e92cdSEric Dumazet 		  READ_ONCE(net->dev_base_seq);
1853081a0e3bSEric Dumazet 
1854081a0e3bSEric Dumazet 	/* Must not return 0 (see nl_dump_check_consistent()).
1855081a0e3bSEric Dumazet 	 * Chose a value far away from 0.
1856081a0e3bSEric Dumazet 	 */
1857081a0e3bSEric Dumazet 	if (!res)
1858081a0e3bSEric Dumazet 		res = 0x80000000;
1859081a0e3bSEric Dumazet 	return res;
1860081a0e3bSEric Dumazet }
1861081a0e3bSEric Dumazet 
18621da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18631da177e4SLinus Torvalds {
1864c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1865978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1866978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1867c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1868978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1869978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1870978a46faSChristian Brauner 		.netnsid = -1,
1871978a46faSChristian Brauner 	};
18723b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1873d3807145SChristian Brauner 	struct net *tgt_net = net;
1874cdb2f80fSEric Dumazet 	struct {
1875cdb2f80fSEric Dumazet 		unsigned long ifindex;
1876cdb2f80fSEric Dumazet 		int ip_idx;
1877cdb2f80fSEric Dumazet 	} *ctx = (void *)cb->ctx;
18781da177e4SLinus Torvalds 	struct in_device *in_dev;
1879cdb2f80fSEric Dumazet 	struct net_device *dev;
1880d7e38611SDavid Ahern 	int err = 0;
18811da177e4SLinus Torvalds 
1882cdb2f80fSEric Dumazet 	rcu_read_lock();
1883c33078e3SDavid Ahern 	if (cb->strict_check) {
1884c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18855fcd266aSDavid Ahern 						 skb->sk, cb);
1886c33078e3SDavid Ahern 		if (err < 0)
1887cdb2f80fSEric Dumazet 			goto done;
18885fcd266aSDavid Ahern 
18895fcd266aSDavid Ahern 		if (fillargs.ifindex) {
1890cdb2f80fSEric Dumazet 			dev = dev_get_by_index_rcu(tgt_net, fillargs.ifindex);
1891*7b05ab85SIdo Schimmel 			if (!dev) {
1892*7b05ab85SIdo Schimmel 				err = -ENODEV;
1893cdb2f80fSEric Dumazet 				goto done;
1894*7b05ab85SIdo Schimmel 			}
1895eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18969f9354b9SEric Dumazet 			if (!in_dev)
1897cdb2f80fSEric Dumazet 				goto done;
1898cdb2f80fSEric Dumazet 			err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx,
18991c98eca4SDavid Ahern 					       &fillargs);
19001da177e4SLinus Torvalds 			goto done;
19011da177e4SLinus Torvalds 		}
1902eec4df98SEric Dumazet 	}
19031da177e4SLinus Torvalds 
1904cdb2f80fSEric Dumazet 	cb->seq = inet_base_seq(tgt_net);
1905cdb2f80fSEric Dumazet 
1906cdb2f80fSEric Dumazet 	for_each_netdev_dump(net, dev, ctx->ifindex) {
1907cdb2f80fSEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
1908cdb2f80fSEric Dumazet 		if (!in_dev)
1909cdb2f80fSEric Dumazet 			continue;
1910cdb2f80fSEric Dumazet 		err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx,
1911cdb2f80fSEric Dumazet 				       &fillargs);
1912cdb2f80fSEric Dumazet 		if (err < 0)
1913cdb2f80fSEric Dumazet 			goto done;
1914cdb2f80fSEric Dumazet 	}
19151da177e4SLinus Torvalds done:
1916978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1917d3807145SChristian Brauner 		put_net(tgt_net);
1918cdb2f80fSEric Dumazet 	rcu_read_unlock();
1919cdb2f80fSEric Dumazet 	return err;
19201da177e4SLinus Torvalds }
19211da177e4SLinus Torvalds 
1922d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
192315e47304SEric W. Biederman 		      u32 portid)
19241da177e4SLinus Torvalds {
1925978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1926978a46faSChristian Brauner 		.portid = portid,
1927978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1928978a46faSChristian Brauner 		.event = event,
1929978a46faSChristian Brauner 		.flags = 0,
1930978a46faSChristian Brauner 		.netnsid = -1,
1931978a46faSChristian Brauner 	};
193247f68512SThomas Graf 	struct sk_buff *skb;
1933d6062cbbSThomas Graf 	int err = -ENOBUFS;
19344b8aa9abSDenis V. Lunev 	struct net *net;
19351da177e4SLinus Torvalds 
1936c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1937339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
193851456b29SIan Morris 	if (!skb)
1939d6062cbbSThomas Graf 		goto errout;
1940d6062cbbSThomas Graf 
1941978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
194226932566SPatrick McHardy 	if (err < 0) {
194326932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
194426932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
194526932566SPatrick McHardy 		kfree_skb(skb);
194626932566SPatrick McHardy 		goto errout;
194726932566SPatrick McHardy 	}
194815e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19491ce85fe4SPablo Neira Ayuso 	return;
1950d6062cbbSThomas Graf errout:
1951d6062cbbSThomas Graf 	if (err < 0)
19524b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19531da177e4SLinus Torvalds }
19541da177e4SLinus Torvalds 
1955b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1956b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19579f0f7272SThomas Graf {
19581fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19599f0f7272SThomas Graf 
19609f0f7272SThomas Graf 	if (!in_dev)
19619f0f7272SThomas Graf 		return 0;
19629f0f7272SThomas Graf 
19639f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19649f0f7272SThomas Graf }
19659f0f7272SThomas Graf 
1966d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1967d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19689f0f7272SThomas Graf {
19691fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19709f0f7272SThomas Graf 	struct nlattr *nla;
19719f0f7272SThomas Graf 	int i;
19729f0f7272SThomas Graf 
19739f0f7272SThomas Graf 	if (!in_dev)
19749f0f7272SThomas Graf 		return -ENODATA;
19759f0f7272SThomas Graf 
19769f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
197751456b29SIan Morris 	if (!nla)
19789f0f7272SThomas Graf 		return -EMSGSIZE;
19799f0f7272SThomas Graf 
19809f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19810598f8f3SEric Dumazet 		((u32 *) nla_data(nla))[i] = READ_ONCE(in_dev->cnf.data[i]);
19829f0f7272SThomas Graf 
19839f0f7272SThomas Graf 	return 0;
19849f0f7272SThomas Graf }
19859f0f7272SThomas Graf 
19869f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19879f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19889f0f7272SThomas Graf };
19899f0f7272SThomas Graf 
1990cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
19918679c31eSRocco Yue 				 const struct nlattr *nla,
19928679c31eSRocco Yue 				 struct netlink_ext_ack *extack)
19939f0f7272SThomas Graf {
19949f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19959f0f7272SThomas Graf 	int err, rem;
19969f0f7272SThomas Graf 
1997a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
1998cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19999f0f7272SThomas Graf 
20008cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
20018679c31eSRocco Yue 					  inet_af_policy, extack);
20029f0f7272SThomas Graf 	if (err < 0)
20039f0f7272SThomas Graf 		return err;
20049f0f7272SThomas Graf 
20059f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20069f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
20079f0f7272SThomas Graf 			int cfgid = nla_type(a);
20089f0f7272SThomas Graf 
20099f0f7272SThomas Graf 			if (nla_len(a) < 4)
20109f0f7272SThomas Graf 				return -EINVAL;
20119f0f7272SThomas Graf 
20129f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
20139f0f7272SThomas Graf 				return -EINVAL;
20149f0f7272SThomas Graf 		}
20159f0f7272SThomas Graf 	}
20169f0f7272SThomas Graf 
2017cf7afbfeSThomas Graf 	return 0;
2018cf7afbfeSThomas Graf }
2019cf7afbfeSThomas Graf 
20203583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
20213583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
2022cf7afbfeSThomas Graf {
2023a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
2024cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
2025cf7afbfeSThomas Graf 	int rem;
2026cf7afbfeSThomas Graf 
2027cf7afbfeSThomas Graf 	if (!in_dev)
2028cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
2029cf7afbfeSThomas Graf 
20308cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
20315ac6b198SZheng Yongjun 		return -EINVAL;
2032cf7afbfeSThomas Graf 
20339f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20349f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
20359f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
20369f0f7272SThomas Graf 	}
20379f0f7272SThomas Graf 
20389f0f7272SThomas Graf 	return 0;
20399f0f7272SThomas Graf }
20409f0f7272SThomas Graf 
2041edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2042edc9e748SNicolas Dichtel {
2043edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2044edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2045136ba622SZhang Shengju 	bool all = false;
2046edc9e748SNicolas Dichtel 
2047136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2048136ba622SZhang Shengju 		all = true;
2049136ba622SZhang Shengju 
2050136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2051edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2052136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2053cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2054136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2055d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20565cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20575cbf777cSXin Long 		size += nla_total_size(4);
2058136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2059f085ff1cSstephen hemminger 		size += nla_total_size(4);
2060136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2061974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2062edc9e748SNicolas Dichtel 
2063edc9e748SNicolas Dichtel 	return size;
2064edc9e748SNicolas Dichtel }
2065edc9e748SNicolas Dichtel 
2066edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
20670598f8f3SEric Dumazet 				     const struct ipv4_devconf *devconf,
20680598f8f3SEric Dumazet 				     u32 portid, u32 seq, int event,
20690598f8f3SEric Dumazet 				     unsigned int flags, int type)
2070edc9e748SNicolas Dichtel {
2071edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2072edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2073136ba622SZhang Shengju 	bool all = false;
2074edc9e748SNicolas Dichtel 
2075edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2076edc9e748SNicolas Dichtel 			flags);
207751456b29SIan Morris 	if (!nlh)
2078edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2079edc9e748SNicolas Dichtel 
2080136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2081136ba622SZhang Shengju 		all = true;
2082136ba622SZhang Shengju 
2083edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2084edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2085edc9e748SNicolas Dichtel 
2086edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2087edc9e748SNicolas Dichtel 		goto nla_put_failure;
2088edc9e748SNicolas Dichtel 
2089b5c9641dSDavid Ahern 	if (!devconf)
2090b5c9641dSDavid Ahern 		goto out;
2091b5c9641dSDavid Ahern 
2092136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2093edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
20940598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, FORWARDING)) < 0)
2095edc9e748SNicolas Dichtel 		goto nla_put_failure;
2096136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2097cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
20980598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, RP_FILTER)) < 0)
2099cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2100136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2101d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
21020598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, MC_FORWARDING)) < 0)
2103d67b8c61SNicolas Dichtel 		goto nla_put_failure;
21045cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
21055cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
21060598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, BC_FORWARDING)) < 0)
21075cbf777cSXin Long 		goto nla_put_failure;
2108136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
210909aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
21100598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, PROXY_ARP)) < 0)
2111f085ff1cSstephen hemminger 		goto nla_put_failure;
2112136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2113974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
21140598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf,
21150598f8f3SEric Dumazet 					IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2116974d7af5SAndy Gospodarek 		goto nla_put_failure;
2117edc9e748SNicolas Dichtel 
2118b5c9641dSDavid Ahern out:
2119053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2120053c095aSJohannes Berg 	return 0;
2121edc9e748SNicolas Dichtel 
2122edc9e748SNicolas Dichtel nla_put_failure:
2123edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2124edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2125edc9e748SNicolas Dichtel }
2126edc9e748SNicolas Dichtel 
21273b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
21283b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2129edc9e748SNicolas Dichtel {
2130edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2131edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2132edc9e748SNicolas Dichtel 
2133fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
213451456b29SIan Morris 	if (!skb)
2135edc9e748SNicolas Dichtel 		goto errout;
2136edc9e748SNicolas Dichtel 
2137edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
21383b022865SDavid Ahern 					event, 0, type);
2139edc9e748SNicolas Dichtel 	if (err < 0) {
2140edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2141edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2142edc9e748SNicolas Dichtel 		kfree_skb(skb);
2143edc9e748SNicolas Dichtel 		goto errout;
2144edc9e748SNicolas Dichtel 	}
2145fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2146edc9e748SNicolas Dichtel 	return;
2147edc9e748SNicolas Dichtel errout:
2148edc9e748SNicolas Dichtel 	if (err < 0)
2149edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2150edc9e748SNicolas Dichtel }
2151edc9e748SNicolas Dichtel 
21529e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21539e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21549e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2155cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
215609aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2157974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21589e551110SNicolas Dichtel };
21599e551110SNicolas Dichtel 
2160eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2161eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2162eede370dSJakub Kicinski 				      struct nlattr **tb,
2163eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2164eede370dSJakub Kicinski {
2165eede370dSJakub Kicinski 	int i, err;
2166eede370dSJakub Kicinski 
2167eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2168eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2169eede370dSJakub Kicinski 		return -EINVAL;
2170eede370dSJakub Kicinski 	}
2171eede370dSJakub Kicinski 
2172eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21738cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21748cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21758cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2176eede370dSJakub Kicinski 
21778cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21788cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21798cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2180eede370dSJakub Kicinski 	if (err)
2181eede370dSJakub Kicinski 		return err;
2182eede370dSJakub Kicinski 
2183eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2184eede370dSJakub Kicinski 		if (!tb[i])
2185eede370dSJakub Kicinski 			continue;
2186eede370dSJakub Kicinski 
2187eede370dSJakub Kicinski 		switch (i) {
2188eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2189eede370dSJakub Kicinski 			break;
2190eede370dSJakub Kicinski 		default:
2191eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2192eede370dSJakub Kicinski 			return -EINVAL;
2193eede370dSJakub Kicinski 		}
2194eede370dSJakub Kicinski 	}
2195eede370dSJakub Kicinski 
2196eede370dSJakub Kicinski 	return 0;
2197eede370dSJakub Kicinski }
2198eede370dSJakub Kicinski 
21999e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2200c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2201c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
22029e551110SNicolas Dichtel {
22039e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
22049e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX + 1];
2205bbcf9105SEric Dumazet 	const struct ipv4_devconf *devconf;
2206bbcf9105SEric Dumazet 	struct in_device *in_dev = NULL;
2207bbcf9105SEric Dumazet 	struct net_device *dev = NULL;
22089e551110SNicolas Dichtel 	struct sk_buff *skb;
22099e551110SNicolas Dichtel 	int ifindex;
22109e551110SNicolas Dichtel 	int err;
22119e551110SNicolas Dichtel 
2212eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2213eede370dSJakub Kicinski 	if (err)
2214bbcf9105SEric Dumazet 		return err;
22159e551110SNicolas Dichtel 
22169e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
2217bbcf9105SEric Dumazet 		return -EINVAL;
22189e551110SNicolas Dichtel 
22199e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
22209e551110SNicolas Dichtel 	switch (ifindex) {
22219e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
22229e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
22239e551110SNicolas Dichtel 		break;
22249e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
22259e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
22269e551110SNicolas Dichtel 		break;
22279e551110SNicolas Dichtel 	default:
2228bbcf9105SEric Dumazet 		err = -ENODEV;
2229bbcf9105SEric Dumazet 		dev = dev_get_by_index(net, ifindex);
2230bbcf9105SEric Dumazet 		if (dev)
2231bbcf9105SEric Dumazet 			in_dev = in_dev_get(dev);
223251456b29SIan Morris 		if (!in_dev)
22339e551110SNicolas Dichtel 			goto errout;
22349e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
22359e551110SNicolas Dichtel 		break;
22369e551110SNicolas Dichtel 	}
22379e551110SNicolas Dichtel 
22389e551110SNicolas Dichtel 	err = -ENOBUFS;
2239fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
224051456b29SIan Morris 	if (!skb)
22419e551110SNicolas Dichtel 		goto errout;
22429e551110SNicolas Dichtel 
22439e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22449e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22459e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2246136ba622SZhang Shengju 					NETCONFA_ALL);
22479e551110SNicolas Dichtel 	if (err < 0) {
22489e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22499e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22509e551110SNicolas Dichtel 		kfree_skb(skb);
22519e551110SNicolas Dichtel 		goto errout;
22529e551110SNicolas Dichtel 	}
22539e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22549e551110SNicolas Dichtel errout:
2255bbcf9105SEric Dumazet 	if (in_dev)
2256bbcf9105SEric Dumazet 		in_dev_put(in_dev);
2257bbcf9105SEric Dumazet 	dev_put(dev);
22589e551110SNicolas Dichtel 	return err;
22599e551110SNicolas Dichtel }
22609e551110SNicolas Dichtel 
22617a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22627a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22637a674200SNicolas Dichtel {
2264addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22657a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
226616748707SEric Dumazet 	struct {
226716748707SEric Dumazet 		unsigned long ifindex;
226816748707SEric Dumazet 		unsigned int all_default;
226916748707SEric Dumazet 	} *ctx = (void *)cb->ctx;
227016748707SEric Dumazet 	const struct in_device *in_dev;
22717a674200SNicolas Dichtel 	struct net_device *dev;
227216748707SEric Dumazet 	int err = 0;
22737a674200SNicolas Dichtel 
2274addd383fSDavid Ahern 	if (cb->strict_check) {
2275addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2276addd383fSDavid Ahern 		struct netconfmsg *ncm;
2277addd383fSDavid Ahern 
2278addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2279addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2280addd383fSDavid Ahern 			return -EINVAL;
2281addd383fSDavid Ahern 		}
2282addd383fSDavid Ahern 
2283addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2284addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2285addd383fSDavid Ahern 			return -EINVAL;
2286addd383fSDavid Ahern 		}
2287addd383fSDavid Ahern 	}
2288addd383fSDavid Ahern 
22897a674200SNicolas Dichtel 	rcu_read_lock();
229016748707SEric Dumazet 	for_each_netdev_dump(net, dev, ctx->ifindex) {
22917a674200SNicolas Dichtel 		in_dev = __in_dev_get_rcu(dev);
22927a674200SNicolas Dichtel 		if (!in_dev)
229316748707SEric Dumazet 			continue;
229416748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, dev->ifindex,
22957a674200SNicolas Dichtel 						&in_dev->cnf,
22967a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2297addd383fSDavid Ahern 						nlh->nlmsg_seq,
229816748707SEric Dumazet 						RTM_NEWNETCONF, NLM_F_MULTI,
229916748707SEric Dumazet 						NETCONFA_ALL);
230016748707SEric Dumazet 		if (err < 0)
23017a674200SNicolas Dichtel 			goto done;
23027a674200SNicolas Dichtel 	}
230316748707SEric Dumazet 	if (ctx->all_default == 0) {
230416748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
23057a674200SNicolas Dichtel 						net->ipv4.devconf_all,
23067a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2307addd383fSDavid Ahern 						nlh->nlmsg_seq,
23087a674200SNicolas Dichtel 						RTM_NEWNETCONF, NLM_F_MULTI,
230916748707SEric Dumazet 						NETCONFA_ALL);
231016748707SEric Dumazet 		if (err < 0)
23117a674200SNicolas Dichtel 			goto done;
231216748707SEric Dumazet 		ctx->all_default++;
23137a674200SNicolas Dichtel 	}
231416748707SEric Dumazet 	if (ctx->all_default == 1) {
231516748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
23167a674200SNicolas Dichtel 						net->ipv4.devconf_dflt,
23177a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2318addd383fSDavid Ahern 						nlh->nlmsg_seq,
23197a674200SNicolas Dichtel 						RTM_NEWNETCONF, NLM_F_MULTI,
232016748707SEric Dumazet 						NETCONFA_ALL);
232116748707SEric Dumazet 		if (err < 0)
23227a674200SNicolas Dichtel 			goto done;
232316748707SEric Dumazet 		ctx->all_default++;
23247a674200SNicolas Dichtel 	}
23257a674200SNicolas Dichtel done:
232616748707SEric Dumazet 	rcu_read_unlock();
232716748707SEric Dumazet 	return err;
23287a674200SNicolas Dichtel }
23297a674200SNicolas Dichtel 
23301da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23311da177e4SLinus Torvalds 
2332c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
233331be3085SHerbert Xu {
233431be3085SHerbert Xu 	struct net_device *dev;
233531be3085SHerbert Xu 
233631be3085SHerbert Xu 	rcu_read_lock();
2337c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2338c6d14c84SEric Dumazet 		struct in_device *in_dev;
2339c6d14c84SEric Dumazet 
234031be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
234131be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23429355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2343c6d14c84SEric Dumazet 	}
234431be3085SHerbert Xu 	rcu_read_unlock();
234531be3085SHerbert Xu }
234631be3085SHerbert Xu 
2347c6d14c84SEric Dumazet /* called with RTNL locked */
2348c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
234968dd299bSPavel Emelyanov {
235068dd299bSPavel Emelyanov 	struct net_device *dev;
2351586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
235268dd299bSPavel Emelyanov 
2353586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23549355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23553b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23563b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2357edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2358edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23593b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23603b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2361edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2362edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
236368dd299bSPavel Emelyanov 
2364c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
236568dd299bSPavel Emelyanov 		struct in_device *in_dev;
2366fa17806cSEric Dumazet 
23670187bdfbSBen Hutchings 		if (on)
23680187bdfbSBen Hutchings 			dev_disable_lro(dev);
2369fa17806cSEric Dumazet 
2370fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2371edc9e748SNicolas Dichtel 		if (in_dev) {
237268dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23733b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23743b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2375edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2376edc9e748SNicolas Dichtel 		}
237768dd299bSPavel Emelyanov 	}
237868dd299bSPavel Emelyanov }
237968dd299bSPavel Emelyanov 
2380f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2381f085ff1cSstephen hemminger {
2382f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2383f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2384f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2385f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2386f085ff1cSstephen hemminger 	else {
2387f085ff1cSstephen hemminger 		struct in_device *idev
2388f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2389f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2390f085ff1cSstephen hemminger 	}
2391f085ff1cSstephen hemminger }
2392f085ff1cSstephen hemminger 
2393fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
239432927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
239531be3085SHerbert Xu {
2396d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23978d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2398d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
239931be3085SHerbert Xu 
240031be3085SHerbert Xu 	if (write) {
240131be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2402c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
240331be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2404f085ff1cSstephen hemminger 		int ifindex;
240531be3085SHerbert Xu 
240631be3085SHerbert Xu 		set_bit(i, cnf->state);
240731be3085SHerbert Xu 
24089355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2409c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2410d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2411d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2412d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
24134ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2414f085ff1cSstephen hemminger 
24155cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
24165cbf777cSXin Long 		    new_value != old_value)
24175cbf777cSXin Long 			rt_cache_flush(net);
24185cbf777cSXin Long 
2419cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2420cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2421f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24223b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24233b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2424cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2425cc535dfbSNicolas Dichtel 		}
2426f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2427f085ff1cSstephen hemminger 		    new_value != old_value) {
2428f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24293b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24303b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2431f085ff1cSstephen hemminger 						    ifindex, cnf);
2432f085ff1cSstephen hemminger 		}
2433974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2434974d7af5SAndy Gospodarek 		    new_value != old_value) {
2435974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24363b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24373b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2438974d7af5SAndy Gospodarek 						    ifindex, cnf);
2439974d7af5SAndy Gospodarek 		}
244031be3085SHerbert Xu 	}
244131be3085SHerbert Xu 
244231be3085SHerbert Xu 	return ret;
244331be3085SHerbert Xu }
244431be3085SHerbert Xu 
2445fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
244632927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24471da177e4SLinus Torvalds {
24481da177e4SLinus Torvalds 	int *valp = ctl->data;
24491da177e4SLinus Torvalds 	int val = *valp;
245088af182eSEric W. Biederman 	loff_t pos = *ppos;
24518292d7f6SYang Yang 	struct net *net = ctl->extra2;
24528292d7f6SYang Yang 	int ret;
24538292d7f6SYang Yang 
24548292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24558292d7f6SYang Yang 		return -EPERM;
24568292d7f6SYang Yang 
24578292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24581da177e4SLinus Torvalds 
24591da177e4SLinus Torvalds 	if (write && *valp != val) {
24600187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
246188af182eSEric W. Biederman 			if (!rtnl_trylock()) {
246288af182eSEric W. Biederman 				/* Restore the original values before restarting */
246388af182eSEric W. Biederman 				*valp = val;
246488af182eSEric W. Biederman 				*ppos = pos;
24659b8adb5eSEric W. Biederman 				return restart_syscall();
246688af182eSEric W. Biederman 			}
24670187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2468c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2469edc9e748SNicolas Dichtel 			} else {
24700187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24710187bdfbSBen Hutchings 				struct in_device *idev =
24720187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2473edc9e748SNicolas Dichtel 				if (*valp)
24740187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24753b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2476edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2477edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2478edc9e748SNicolas Dichtel 							    cnf);
24790187bdfbSBen Hutchings 			}
24800187bdfbSBen Hutchings 			rtnl_unlock();
24814ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2482edc9e748SNicolas Dichtel 		} else
24833b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24843b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2485edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2486edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24870187bdfbSBen Hutchings 	}
24881da177e4SLinus Torvalds 
24891da177e4SLinus Torvalds 	return ret;
24901da177e4SLinus Torvalds }
24911da177e4SLinus Torvalds 
2492fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
249332927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
24941da177e4SLinus Torvalds {
24951da177e4SLinus Torvalds 	int *valp = ctl->data;
24961da177e4SLinus Torvalds 	int val = *valp;
24978d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
249876e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24991da177e4SLinus Torvalds 
25001da177e4SLinus Torvalds 	if (write && *valp != val)
25014ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
25021da177e4SLinus Torvalds 
25031da177e4SLinus Torvalds 	return ret;
25041da177e4SLinus Torvalds }
25051da177e4SLinus Torvalds 
2506f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
250742f811b8SHerbert Xu 	{ \
250842f811b8SHerbert Xu 		.procname	= name, \
250942f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
251002291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
251142f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
251242f811b8SHerbert Xu 		.mode		= mval, \
251342f811b8SHerbert Xu 		.proc_handler	= proc, \
251431be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
251542f811b8SHerbert Xu 	}
251642f811b8SHerbert Xu 
251742f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2518f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
251942f811b8SHerbert Xu 
252042f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2521f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
252242f811b8SHerbert Xu 
2523f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2524f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
252542f811b8SHerbert Xu 
252642f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2527f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
252842f811b8SHerbert Xu 
25291da177e4SLinus Torvalds static struct devinet_sysctl_table {
25301da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
25311c106eb0SJoel Granados 	struct ctl_table devinet_vars[IPV4_DEVCONF_MAX];
25321da177e4SLinus Torvalds } devinet_sysctl = {
25331da177e4SLinus Torvalds 	.devinet_vars = {
253442f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2535f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
253642f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25375cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
253842f811b8SHerbert Xu 
253942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
254042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
254142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
254242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
254342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
254442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
254542f811b8SHerbert Xu 					"accept_source_route"),
25468153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
254728f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
254842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
254942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
255042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
255142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
255242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
255342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
255442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
255542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
255642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2557eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
2558fcdb44d0SJames Prestwood 		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
2559fcdb44d0SJames Prestwood 					"arp_evict_nocarrier"),
256065324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25615c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25625c6fe01cSWilliam Manley 					"force_igmp_version"),
25632690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25642690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25652690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25662690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25670eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25680eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
256997daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
257097daf331SJohannes Berg 					"drop_gratuitous_arp"),
257142f811b8SHerbert Xu 
257242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
257342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
257442f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
257542f811b8SHerbert Xu 					      "promote_secondaries"),
2576d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2577d0daebc3SThomas Graf 					      "route_localnet"),
257812b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
257912b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25801da177e4SLinus Torvalds 	},
25811da177e4SLinus Torvalds };
25821da177e4SLinus Torvalds 
2583ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
258429c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25851da177e4SLinus Torvalds {
25861da177e4SLinus Torvalds 	int i;
25879fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25888607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2589bfada697SPavel Emelyanov 
2590425b9c7fSVasily Averin 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT);
25911da177e4SLinus Torvalds 	if (!t)
25929fa89642SPavel Emelyanov 		goto out;
25939fa89642SPavel Emelyanov 
25941c106eb0SJoel Granados 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars); i++) {
25951da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
259631be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2597c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25981da177e4SLinus Torvalds 	}
25991da177e4SLinus Torvalds 
26008607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
26011da177e4SLinus Torvalds 
26028607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
26031da177e4SLinus Torvalds 	if (!t->sysctl_header)
26048607ddb8SEric W. Biederman 		goto free;
26051da177e4SLinus Torvalds 
26061da177e4SLinus Torvalds 	p->sysctl = t;
260729c994e3SNicolas Dichtel 
26083b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
26093b022865SDavid Ahern 				    ifindex, p);
2610ea40b324SPavel Emelyanov 	return 0;
26111da177e4SLinus Torvalds 
26121da177e4SLinus Torvalds free:
26131da177e4SLinus Torvalds 	kfree(t);
26149fa89642SPavel Emelyanov out:
26156def4801Sliuguoqiang 	return -ENOMEM;
26161da177e4SLinus Torvalds }
26171da177e4SLinus Torvalds 
2618b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2619b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
262066f27a52SPavel Emelyanov {
262151602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
262266f27a52SPavel Emelyanov 
2623b5c9641dSDavid Ahern 	if (t) {
262451602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2625ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26261da177e4SLinus Torvalds 		kfree(t);
26271da177e4SLinus Torvalds 	}
262851602b2aSPavel Emelyanov 
2629b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2630b5c9641dSDavid Ahern }
2631b5c9641dSDavid Ahern 
263220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
263351602b2aSPavel Emelyanov {
263420e61da7SWANG Cong 	int err;
263520e61da7SWANG Cong 
263620e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
263720e61da7SWANG Cong 		return -EINVAL;
263820e61da7SWANG Cong 
263920e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
264020e61da7SWANG Cong 	if (err)
264120e61da7SWANG Cong 		return err;
264220e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
264329c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
264420e61da7SWANG Cong 	if (err)
264520e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
264620e61da7SWANG Cong 	return err;
264751602b2aSPavel Emelyanov }
264851602b2aSPavel Emelyanov 
264951602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
265051602b2aSPavel Emelyanov {
2651b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2652b5c9641dSDavid Ahern 
2653b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
265451602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26551da177e4SLinus Torvalds }
26561da177e4SLinus Torvalds 
265768dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
265868dd299bSPavel Emelyanov 	{
265968dd299bSPavel Emelyanov 		.procname	= "ip_forward",
266068dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
266102291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
266268dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
266368dd299bSPavel Emelyanov 		.mode		= 0644,
266468dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
266568dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2666c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
266768dd299bSPavel Emelyanov 	},
266868dd299bSPavel Emelyanov };
26692a75de0cSEric Dumazet #endif
267068dd299bSPavel Emelyanov 
2671752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2672752d14dcSPavel Emelyanov {
2673752d14dcSPavel Emelyanov 	int err;
2674752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26752a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2676856c395cSCong Wang 	struct ctl_table *tbl;
2677752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26782a75de0cSEric Dumazet #endif
2679752d14dcSPavel Emelyanov 
2680752d14dcSPavel Emelyanov 	err = -ENOMEM;
2681856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
268251456b29SIan Morris 	if (!all)
2683752d14dcSPavel Emelyanov 		goto err_alloc_all;
2684752d14dcSPavel Emelyanov 
2685856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
268651456b29SIan Morris 	if (!dflt)
2687752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2688752d14dcSPavel Emelyanov 
26892a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2690856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
269151456b29SIan Morris 	if (!tbl)
2692752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2693752d14dcSPavel Emelyanov 
269402291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2695752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2696752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26972a75de0cSEric Dumazet #endif
2698856c395cSCong Wang 
26999efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
2700a5612ca1SKuniyuki Iwashima 		switch (net_inherit_devconf()) {
2701a5612ca1SKuniyuki Iwashima 		case 3:
27029efd6a3cSNicolas Dichtel 			/* copy from the current netns */
27039efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
27049efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27059efd6a3cSNicolas Dichtel 			memcpy(dflt,
27069efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
27079efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2708a5612ca1SKuniyuki Iwashima 			break;
2709a5612ca1SKuniyuki Iwashima 		case 0:
2710a5612ca1SKuniyuki Iwashima 		case 1:
2711a5612ca1SKuniyuki Iwashima 			/* copy from init_net */
27129efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
27139efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27149efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
27159efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2716a5612ca1SKuniyuki Iwashima 			break;
2717a5612ca1SKuniyuki Iwashima 		case 2:
2718a5612ca1SKuniyuki Iwashima 			/* use compiled values */
2719a5612ca1SKuniyuki Iwashima 			break;
27209efd6a3cSNicolas Dichtel 		}
2721752d14dcSPavel Emelyanov 	}
2722752d14dcSPavel Emelyanov 
2723752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
272429c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2725752d14dcSPavel Emelyanov 	if (err < 0)
2726752d14dcSPavel Emelyanov 		goto err_reg_all;
2727752d14dcSPavel Emelyanov 
272829c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
272929c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2730752d14dcSPavel Emelyanov 	if (err < 0)
2731752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2732752d14dcSPavel Emelyanov 
2733752d14dcSPavel Emelyanov 	err = -ENOMEM;
2734c899710fSJoel Granados 	forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl,
2735c899710fSJoel Granados 					  ARRAY_SIZE(ctl_forward_entry));
273651456b29SIan Morris 	if (!forw_hdr)
2737752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27382a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2739752d14dcSPavel Emelyanov #endif
2740752d14dcSPavel Emelyanov 
2741752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2742752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2743752d14dcSPavel Emelyanov 	return 0;
2744752d14dcSPavel Emelyanov 
2745752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2746752d14dcSPavel Emelyanov err_reg_ctl:
2747b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2748752d14dcSPavel Emelyanov err_reg_dflt:
2749b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2750752d14dcSPavel Emelyanov err_reg_all:
2751752d14dcSPavel Emelyanov 	kfree(tbl);
2752752d14dcSPavel Emelyanov err_alloc_ctl:
27532a75de0cSEric Dumazet #endif
2754752d14dcSPavel Emelyanov 	kfree(dflt);
2755752d14dcSPavel Emelyanov err_alloc_dflt:
2756752d14dcSPavel Emelyanov 	kfree(all);
2757752d14dcSPavel Emelyanov err_alloc_all:
2758752d14dcSPavel Emelyanov 	return err;
2759752d14dcSPavel Emelyanov }
2760752d14dcSPavel Emelyanov 
2761752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2762752d14dcSPavel Emelyanov {
27632a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2764bfa858f2SThomas Weißschuh 	const struct ctl_table *tbl;
2765752d14dcSPavel Emelyanov 
2766752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2767752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2768b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2769b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2770b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2771b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2772752d14dcSPavel Emelyanov 	kfree(tbl);
27732a75de0cSEric Dumazet #endif
2774752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2775752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2776752d14dcSPavel Emelyanov }
2777752d14dcSPavel Emelyanov 
2778752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2779752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2780752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2781752d14dcSPavel Emelyanov };
2782752d14dcSPavel Emelyanov 
2783207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
27849f0f7272SThomas Graf 	.family		  = AF_INET,
27859f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
27869f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2787cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2788cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
27899f0f7272SThomas Graf };
27909f0f7272SThomas Graf 
27911da177e4SLinus Torvalds void __init devinet_init(void)
27921da177e4SLinus Torvalds {
2793fd23c3b3SDavid S. Miller 	int i;
2794fd23c3b3SDavid S. Miller 
2795fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2796fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2797fd23c3b3SDavid S. Miller 
2798752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
27991da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
280063f3444fSThomas Graf 
2801906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
28025c766d64SJiri Pirko 
28039f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
28049f0f7272SThomas Graf 
2805b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2806b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2807cdb2f80fSEric Dumazet 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr,
2808cdb2f80fSEric Dumazet 		      RTNL_FLAG_DUMP_UNLOCKED);
28099e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2810bbcf9105SEric Dumazet 		      inet_netconf_dump_devconf,
281116748707SEric Dumazet 		      RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED);
28121da177e4SLinus Torvalds }
2813