xref: /linux/net/ipv4/devinet.c (revision 3cd3e72ccb3aade0e8fe037ef07a44b341ab577c)
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);
2271da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2281da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2291da177e4SLinus Torvalds 	kfree(ifa);
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
23240384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
2379d40c84cSEric Dumazet static void in_dev_free_rcu(struct rcu_head *head)
2389d40c84cSEric Dumazet {
2399d40c84cSEric Dumazet 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2409d40c84cSEric Dumazet 
2419d40c84cSEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2429d40c84cSEric Dumazet 	kfree(idev);
2439d40c84cSEric Dumazet }
2449d40c84cSEric Dumazet 
2451da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2481da177e4SLinus Torvalds 
249547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
250547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2511da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
25291df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2531da177e4SLinus Torvalds #endif
254d62607c3SJakub Kicinski 	netdev_put(dev, &idev->dev_tracker);
2551da177e4SLinus Torvalds 	if (!idev->dead)
2569f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2579f9354b9SEric Dumazet 	else
2589d40c84cSEric Dumazet 		call_rcu(&idev->rcu_head, in_dev_free_rcu);
2591da177e4SLinus Torvalds }
2609f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2611da177e4SLinus Torvalds 
26271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	struct in_device *in_dev;
26520e61da7SWANG Cong 	int err = -ENOMEM;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	ASSERT_RTNL();
2681da177e4SLinus Torvalds 
2690da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2701da177e4SLinus Torvalds 	if (!in_dev)
2711da177e4SLinus Torvalds 		goto out;
272c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2739355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2741da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2751da177e4SLinus Torvalds 	in_dev->dev = dev;
2769f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2779f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2781da177e4SLinus Torvalds 		goto out_kfree;
2790187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2800187bdfbSBen Hutchings 		dev_disable_lro(dev);
2811da177e4SLinus Torvalds 	/* Reference in_dev->dev */
282d62607c3SJakub Kicinski 	netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL);
28330c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2847658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2851da177e4SLinus Torvalds 
28620e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
28720e61da7SWANG Cong 	if (err) {
28820e61da7SWANG Cong 		in_dev->dead = 1;
2891b49cd71SYang Yingliang 		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
29020e61da7SWANG Cong 		in_dev_put(in_dev);
29120e61da7SWANG Cong 		in_dev = NULL;
29220e61da7SWANG Cong 		goto out;
29320e61da7SWANG Cong 	}
2941da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2951da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2961da177e4SLinus Torvalds 		ip_mc_up(in_dev);
297483479ecSJarek Poplawski 
29830c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
299cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
300483479ecSJarek Poplawski out:
30120e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
3021da177e4SLinus Torvalds out_kfree:
3031da177e4SLinus Torvalds 	kfree(in_dev);
3041da177e4SLinus Torvalds 	in_dev = NULL;
3051da177e4SLinus Torvalds 	goto out;
3061da177e4SLinus Torvalds }
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds 	struct net_device *dev;
3112638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	ASSERT_RTNL();
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	dev = in_dev->dev;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	in_dev->dead = 1;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3201da177e4SLinus Torvalds 
3212638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3221da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3231da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3241da177e4SLinus Torvalds 	}
3251da177e4SLinus Torvalds 
326a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3271da177e4SLinus Torvalds 
32851602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3291da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3301da177e4SLinus Torvalds 	arp_ifdown(dev);
3311da177e4SLinus Torvalds 
3329d40c84cSEric Dumazet 	in_dev_put(in_dev);
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds 
335ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3361da177e4SLinus Torvalds {
337d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
338d519e870SFlorian Westphal 
3391da177e4SLinus Torvalds 	rcu_read_lock();
340d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3411da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3421da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3431da177e4SLinus Torvalds 				rcu_read_unlock();
3441da177e4SLinus Torvalds 				return 1;
3451da177e4SLinus Torvalds 			}
3461da177e4SLinus Torvalds 		}
347d519e870SFlorian Westphal 	}
3481da177e4SLinus Torvalds 	rcu_read_unlock();
3491da177e4SLinus Torvalds 	return 0;
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
3522638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3532638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
35415e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3551da177e4SLinus Torvalds {
3568f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3572638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
358ac28b1ecSLiu Jian 	struct in_ifaddr __rcu **last_prim;
3590ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3600ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	ASSERT_RTNL();
3631da177e4SLinus Torvalds 
3642638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
365ac28b1ecSLiu Jian 	last_prim = ifap;
366fbd40ea0SDavid S. Miller 	if (in_dev->dead)
367fbd40ea0SDavid S. Miller 		goto no_promotions;
368fbd40ea0SDavid S. Miller 
3698f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3708f937c60SHarald Welte 	 * unless alias promotion is set
3718f937c60SHarald Welte 	 **/
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3742638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3751da177e4SLinus Torvalds 
3762638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3770ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3780ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
379ac28b1ecSLiu Jian 				last_prim = &ifa->ifa_next;
3800ff60a45SJamal Hadi Salim 
3811da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3821da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3831da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3841da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3850ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3861da177e4SLinus Torvalds 				continue;
3871da177e4SLinus Torvalds 			}
3881da177e4SLinus Torvalds 
3890ff60a45SJamal Hadi Salim 			if (!do_promote) {
390fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3911da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3921da177e4SLinus Torvalds 
39315e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
394e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
395e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3961da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3978f937c60SHarald Welte 			} else {
3988f937c60SHarald Welte 				promote = ifa;
3998f937c60SHarald Welte 				break;
4008f937c60SHarald Welte 			}
4011da177e4SLinus Torvalds 		}
4021da177e4SLinus Torvalds 	}
4031da177e4SLinus Torvalds 
4042d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4052d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4062d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4072d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4082d230e2bSJulian Anastasov 	 */
4092638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4102d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4112d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4122d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4132d230e2bSJulian Anastasov 	}
4142d230e2bSJulian Anastasov 
415fbd40ea0SDavid S. Miller no_promotions:
4161da177e4SLinus Torvalds 	/* 2. Unlink it */
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
419fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4241da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4251da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4261da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4271da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4281da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4291da177e4SLinus Torvalds 	   So that, this order is correct.
4301da177e4SLinus Torvalds 	 */
43115e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
432e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4330ff60a45SJamal Hadi Salim 
4340ff60a45SJamal Hadi Salim 	if (promote) {
4352638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4360ff60a45SJamal Hadi Salim 
4372638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4380ff60a45SJamal Hadi Salim 		if (prev_prom) {
4392638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4402638eb8bSFlorian Westphal 
4412638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4426a9e9ceaSFlorian Westphal 
443ac28b1ecSLiu Jian 			last_sec = rtnl_dereference(*last_prim);
4442638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
445ac28b1ecSLiu Jian 			rcu_assign_pointer(*last_prim, promote);
4460ff60a45SJamal Hadi Salim 		}
4470ff60a45SJamal Hadi Salim 
4480ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
44915e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
450e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
451e041c683SAlan Stern 				NETDEV_UP, promote);
4522638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4532638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4540ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4550ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4560ff60a45SJamal Hadi Salim 					continue;
4570ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4580ff60a45SJamal Hadi Salim 		}
4590ff60a45SJamal Hadi Salim 
4600ff60a45SJamal Hadi Salim 	}
4616363097cSHerbert Xu 	if (destroy)
4621da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
4652638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4662638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
467d6062cbbSThomas Graf 			 int destroy)
468d6062cbbSThomas Graf {
469d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
470d6062cbbSThomas Graf }
471d6062cbbSThomas Graf 
4725c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4735c766d64SJiri Pirko 
4745c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4755c766d64SJiri Pirko 
476d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
477de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4781da177e4SLinus Torvalds {
4792638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4801da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4813ad7d246SKrister Johansen 	struct in_validator_info ivi;
4822638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4833ad7d246SKrister Johansen 	int ret;
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	ASSERT_RTNL();
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4881da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4891da177e4SLinus Torvalds 		return 0;
4901da177e4SLinus Torvalds 	}
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4931da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4941da177e4SLinus Torvalds 
4952e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
4962e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
4972e605463SMatteo Croce 
4982638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
4992638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
5002638eb8bSFlorian Westphal 
5012638eb8bSFlorian Westphal 	while (ifa1) {
5021da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
5031da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5041da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5051da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5061da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5071da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5081da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5091da177e4SLinus Torvalds 				return -EEXIST;
5101da177e4SLinus Torvalds 			}
5111da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
512b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value");
5131da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5141da177e4SLinus Torvalds 				return -EINVAL;
5151da177e4SLinus Torvalds 			}
5161da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5171da177e4SLinus Torvalds 		}
5182638eb8bSFlorian Westphal 
5192638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5202638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 
5233ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5243ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5253ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5263ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5273ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5283ad7d246SKrister Johansen 	 * not the flags.
5293ad7d246SKrister Johansen 	 */
5303ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5313ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
532de95e047SDavid Ahern 	ivi.extack = extack;
5333ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5343ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5353ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5363ad7d246SKrister Johansen 	if (ret) {
5373ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5383ad7d246SKrister Johansen 		return ret;
5393ad7d246SKrister Johansen 	}
5403ad7d246SKrister Johansen 
541d4150779SJason A. Donenfeld 	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
5421da177e4SLinus Torvalds 		ifap = last_primary;
5431da177e4SLinus Torvalds 
5442638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5452638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5461da177e4SLinus Torvalds 
547fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
548fd23c3b3SDavid S. Miller 
5495c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
550906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5515c766d64SJiri Pirko 
5521da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5531da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5541da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
55515e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
556e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	return 0;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds 
561d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
562d6062cbbSThomas Graf {
563de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
564d6062cbbSThomas Graf }
565d6062cbbSThomas Graf 
5661da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5671da177e4SLinus Torvalds {
568e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 	ASSERT_RTNL();
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds 	if (!in_dev) {
5731da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5741da177e4SLinus Torvalds 		return -ENOBUFS;
5751da177e4SLinus Torvalds 	}
57671e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5771d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5781da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
579547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5801da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5811da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5821da177e4SLinus Torvalds 	}
583f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5841da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5851da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5861da177e4SLinus Torvalds }
5871da177e4SLinus Torvalds 
5888723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5898723e1b4SEric Dumazet  * We dont take a reference on found in_device
5908723e1b4SEric Dumazet  */
5917fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5921da177e4SLinus Torvalds {
5931da177e4SLinus Torvalds 	struct net_device *dev;
5941da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
595c148fc2eSEric Dumazet 
596c148fc2eSEric Dumazet 	rcu_read_lock();
597c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5981da177e4SLinus Torvalds 	if (dev)
5998723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
600c148fc2eSEric Dumazet 	rcu_read_unlock();
6011da177e4SLinus Torvalds 	return in_dev;
6021da177e4SLinus Torvalds }
6039f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6061da177e4SLinus Torvalds 
60760cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
60860cad5daSAl Viro 				    __be32 mask)
6091da177e4SLinus Torvalds {
610d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
611d519e870SFlorian Westphal 
6121da177e4SLinus Torvalds 	ASSERT_RTNL();
6131da177e4SLinus Torvalds 
614d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6151da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6161da177e4SLinus Torvalds 			return ifa;
617d519e870SFlorian Westphal 	}
6181da177e4SLinus Torvalds 	return NULL;
6191da177e4SLinus Torvalds }
6201da177e4SLinus Torvalds 
621690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
622690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
62393a714d6SMadhu Challa {
624690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
62593a714d6SMadhu Challa 	struct ip_mreqn mreq = {
62693a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
62793a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
62893a714d6SMadhu Challa 	};
629690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
63093a714d6SMadhu Challa 	int ret;
63193a714d6SMadhu Challa 
63293a714d6SMadhu Challa 	ASSERT_RTNL();
63393a714d6SMadhu Challa 
63493a714d6SMadhu Challa 	lock_sock(sk);
63593a714d6SMadhu Challa 	if (join)
63654ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
63793a714d6SMadhu Challa 	else
63854ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
63993a714d6SMadhu Challa 	release_sock(sk);
64093a714d6SMadhu Challa 
64193a714d6SMadhu Challa 	return ret;
642690cc863STaras Chornyi #else
643690cc863STaras Chornyi 	return -EOPNOTSUPP;
644690cc863STaras Chornyi #endif
64593a714d6SMadhu Challa }
64693a714d6SMadhu Challa 
647c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
648c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6491da177e4SLinus Torvalds {
6503b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6512638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
652dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6531da177e4SLinus Torvalds 	struct in_device *in_dev;
654dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6552638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
65630e2379eSMenglong Dong 	int err;
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 	ASSERT_RTNL();
6591da177e4SLinus Torvalds 
6608cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6618cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
662dfdd5fd4SThomas Graf 	if (err < 0)
663dfdd5fd4SThomas Graf 		goto errout;
664dfdd5fd4SThomas Graf 
665dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6667fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
66751456b29SIan Morris 	if (!in_dev) {
668b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
669dfdd5fd4SThomas Graf 		err = -ENODEV;
670dfdd5fd4SThomas Graf 		goto errout;
671dfdd5fd4SThomas Graf 	}
672dfdd5fd4SThomas Graf 
6732638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6741da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
675dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
67667b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6771da177e4SLinus Torvalds 			continue;
678dfdd5fd4SThomas Graf 
679dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
680dfdd5fd4SThomas Graf 			continue;
681dfdd5fd4SThomas Graf 
682dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
683dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
68467b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
685dfdd5fd4SThomas Graf 			continue;
686dfdd5fd4SThomas Graf 
68793a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
688690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
68915e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6901da177e4SLinus Torvalds 		return 0;
6911da177e4SLinus Torvalds 	}
692dfdd5fd4SThomas Graf 
693b4672c73SHangbin Liu 	NL_SET_ERR_MSG(extack, "ipv4: Address not found");
694dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
695dfdd5fd4SThomas Graf errout:
696dfdd5fd4SThomas Graf 	return err;
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds 
6995c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
7005c766d64SJiri Pirko 
7015c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
7025c766d64SJiri Pirko {
7035c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
7045c766d64SJiri Pirko 	struct in_ifaddr *ifa;
705c988d1e8SJiri Pirko 	struct hlist_node *n;
7065c766d64SJiri Pirko 	int i;
7075c766d64SJiri Pirko 
7085c766d64SJiri Pirko 	now = jiffies;
7095c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
7105c766d64SJiri Pirko 
7115c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
712c988d1e8SJiri Pirko 		bool change_needed = false;
713c988d1e8SJiri Pirko 
714c988d1e8SJiri Pirko 		rcu_read_lock();
715b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
716*3cd3e72cSEric Dumazet 			unsigned long age, tstamp;
7175c766d64SJiri Pirko 
7185c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
7195c766d64SJiri Pirko 				continue;
7205c766d64SJiri Pirko 
721*3cd3e72cSEric Dumazet 			tstamp = READ_ONCE(ifa->ifa_tstamp);
7225c766d64SJiri Pirko 			/* We try to batch several events at once. */
723*3cd3e72cSEric Dumazet 			age = (now - tstamp +
7245c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7255c766d64SJiri Pirko 
7265c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
7275c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
728c988d1e8SJiri Pirko 				change_needed = true;
729c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
730c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
731c988d1e8SJiri Pirko 				continue;
732c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
733*3cd3e72cSEric Dumazet 				if (time_before(tstamp +
734c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
735*3cd3e72cSEric Dumazet 					next = tstamp +
736c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
737c988d1e8SJiri Pirko 
738c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
739c988d1e8SJiri Pirko 					change_needed = true;
740*3cd3e72cSEric Dumazet 			} else if (time_before(tstamp +
741c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
742c988d1e8SJiri Pirko 					       next)) {
743*3cd3e72cSEric Dumazet 				next = tstamp +
744c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
745c988d1e8SJiri Pirko 			}
746c988d1e8SJiri Pirko 		}
747c988d1e8SJiri Pirko 		rcu_read_unlock();
748c988d1e8SJiri Pirko 		if (!change_needed)
749c988d1e8SJiri Pirko 			continue;
750c988d1e8SJiri Pirko 		rtnl_lock();
751c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
752c988d1e8SJiri Pirko 			unsigned long age;
753c988d1e8SJiri Pirko 
754c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
755c988d1e8SJiri Pirko 				continue;
756c988d1e8SJiri Pirko 
757c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
758c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
759c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
760c988d1e8SJiri Pirko 
761c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
762c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7632638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7642638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7655c766d64SJiri Pirko 
7662638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7672638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7682638eb8bSFlorian Westphal 				while (tmp) {
76940008e92SFlorian Westphal 					if (tmp == ifa) {
7705c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7715c766d64SJiri Pirko 							     ifap, 1);
772c988d1e8SJiri Pirko 						break;
7735c766d64SJiri Pirko 					}
7742638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7752638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
776c988d1e8SJiri Pirko 				}
777c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
778c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
779c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
780c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7815c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7825c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7835c766d64SJiri Pirko 			}
7845c766d64SJiri Pirko 		}
785c988d1e8SJiri Pirko 		rtnl_unlock();
7865c766d64SJiri Pirko 	}
7875c766d64SJiri Pirko 
7885c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7895c766d64SJiri Pirko 	next_sched = next;
7905c766d64SJiri Pirko 
7915c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7925c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7935c766d64SJiri Pirko 		next_sched = next_sec;
7945c766d64SJiri Pirko 
7955c766d64SJiri Pirko 	now = jiffies;
7965c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7975c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7985c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7995c766d64SJiri Pirko 
800906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
801906e073fSviresh kumar 			next_sched - now);
8025c766d64SJiri Pirko }
8035c766d64SJiri Pirko 
8045c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
8055c766d64SJiri Pirko 			     __u32 prefered_lft)
8065c766d64SJiri Pirko {
8075c766d64SJiri Pirko 	unsigned long timeout;
8085c766d64SJiri Pirko 
8095c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
8105c766d64SJiri Pirko 
8115c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
8125c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
8135c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
8145c766d64SJiri Pirko 	else
8155c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
8165c766d64SJiri Pirko 
8175c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8185c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8195c766d64SJiri Pirko 		if (timeout == 0)
8205c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
8215c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
8225c766d64SJiri Pirko 	}
823*3cd3e72cSEric Dumazet 	WRITE_ONCE(ifa->ifa_tstamp, jiffies);
8245c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
825*3cd3e72cSEric Dumazet 		WRITE_ONCE(ifa->ifa_cstamp, ifa->ifa_tstamp);
8265c766d64SJiri Pirko }
8275c766d64SJiri Pirko 
8285c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
829dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
830dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8311da177e4SLinus Torvalds {
8325c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8335c753978SThomas Graf 	struct in_ifaddr *ifa;
8345c753978SThomas Graf 	struct ifaddrmsg *ifm;
8351da177e4SLinus Torvalds 	struct net_device *dev;
8361da177e4SLinus Torvalds 	struct in_device *in_dev;
8377b218574SDenis V. Lunev 	int err;
8381da177e4SLinus Torvalds 
8398cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8408cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8415c753978SThomas Graf 	if (err < 0)
8425c753978SThomas Graf 		goto errout;
8431da177e4SLinus Torvalds 
8445c753978SThomas Graf 	ifm = nlmsg_data(nlh);
845c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
846b4672c73SHangbin Liu 
847b4672c73SHangbin Liu 	if (ifm->ifa_prefixlen > 32) {
848b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length");
8495c753978SThomas Graf 		goto errout;
850b4672c73SHangbin Liu 	}
851b4672c73SHangbin Liu 
852b4672c73SHangbin Liu 	if (!tb[IFA_LOCAL]) {
853b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied");
854b4672c73SHangbin Liu 		goto errout;
855b4672c73SHangbin Liu 	}
8561da177e4SLinus Torvalds 
8574b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8585c753978SThomas Graf 	err = -ENODEV;
859b4672c73SHangbin Liu 	if (!dev) {
860b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
8615c753978SThomas Graf 		goto errout;
862b4672c73SHangbin Liu 	}
8631da177e4SLinus Torvalds 
8645c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8655c753978SThomas Graf 	err = -ENOBUFS;
86651456b29SIan Morris 	if (!in_dev)
8675c753978SThomas Graf 		goto errout;
86871e27da9SHerbert Xu 
8695c753978SThomas Graf 	ifa = inet_alloc_ifa();
87051456b29SIan Morris 	if (!ifa)
8715c753978SThomas Graf 		/*
8725c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8735c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8745c753978SThomas Graf 		 */
8755c753978SThomas Graf 		goto errout;
8765c753978SThomas Graf 
877a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8781d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8795c753978SThomas Graf 	in_dev_hold(in_dev);
8805c753978SThomas Graf 
88151456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8825c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8835c753978SThomas Graf 
884fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8851da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8861da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
887ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
888ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8891da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8901da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8915c753978SThomas Graf 
89267b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
89367b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8945c753978SThomas Graf 
8955c753978SThomas Graf 	if (tb[IFA_BROADCAST])
89667b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8975c753978SThomas Graf 
8985c753978SThomas Graf 	if (tb[IFA_LABEL])
899872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
9001da177e4SLinus Torvalds 	else
9011da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
9021da177e4SLinus Torvalds 
903af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
904af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
905af4d768aSDavid Ahern 
90647f0bd50SJacques de Laval 	if (tb[IFA_PROTO])
90747f0bd50SJacques de Laval 		ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
90847f0bd50SJacques de Laval 
9095c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
9105c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
9115c766d64SJiri Pirko 
9125c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
9135c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
914b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid");
9155c766d64SJiri Pirko 			err = -EINVAL;
916446266b0SDaniel Borkmann 			goto errout_free;
9175c766d64SJiri Pirko 		}
9185c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
9195c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9205c766d64SJiri Pirko 	}
9215c766d64SJiri Pirko 
9225c753978SThomas Graf 	return ifa;
9235c753978SThomas Graf 
924446266b0SDaniel Borkmann errout_free:
925446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9265c753978SThomas Graf errout:
9275c753978SThomas Graf 	return ERR_PTR(err);
9285c753978SThomas Graf }
9295c753978SThomas Graf 
9305c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9315c766d64SJiri Pirko {
9325c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
933ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9345c766d64SJiri Pirko 
9355c766d64SJiri Pirko 	if (!ifa->ifa_local)
9365c766d64SJiri Pirko 		return NULL;
9375c766d64SJiri Pirko 
938ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9395c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9405c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9415c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9425c766d64SJiri Pirko 			return ifa1;
9435c766d64SJiri Pirko 	}
9445c766d64SJiri Pirko 	return NULL;
9455c766d64SJiri Pirko }
9465c766d64SJiri Pirko 
947c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
948c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9495c753978SThomas Graf {
9503b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9515c753978SThomas Graf 	struct in_ifaddr *ifa;
9525c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9535c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9545c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9555c753978SThomas Graf 
9565c753978SThomas Graf 	ASSERT_RTNL();
9575c753978SThomas Graf 
958dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9595c753978SThomas Graf 	if (IS_ERR(ifa))
9605c753978SThomas Graf 		return PTR_ERR(ifa);
9615c753978SThomas Graf 
9625c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9635c766d64SJiri Pirko 	if (!ifa_existing) {
9645c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
965614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9665c766d64SJiri Pirko 		 */
9675c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
96893a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
969690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
97093a714d6SMadhu Challa 
97193a714d6SMadhu Challa 			if (ret < 0) {
972b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed");
97393a714d6SMadhu Challa 				inet_free_ifa(ifa);
97493a714d6SMadhu Challa 				return ret;
97593a714d6SMadhu Challa 			}
97693a714d6SMadhu Challa 		}
977de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
978de95e047SDavid Ahern 					 extack);
9795c766d64SJiri Pirko 	} else {
980af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
9815c4a9aa8SPetr Machata 		u8 new_proto = ifa->ifa_proto;
982af4d768aSDavid Ahern 
9835c766d64SJiri Pirko 		inet_free_ifa(ifa);
9845c766d64SJiri Pirko 
9855c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
986b4672c73SHangbin Liu 		    !(nlh->nlmsg_flags & NLM_F_REPLACE)) {
987b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: Address already assigned");
9885c766d64SJiri Pirko 			return -EEXIST;
989b4672c73SHangbin Liu 		}
99034e2ed34SJiri Pirko 		ifa = ifa_existing;
991af4d768aSDavid Ahern 
992af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
993af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
994af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
995af4d768aSDavid Ahern 		}
996af4d768aSDavid Ahern 
9975c4a9aa8SPetr Machata 		ifa->ifa_proto = new_proto;
9985c4a9aa8SPetr Machata 
99934e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
100005a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
1001906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
1002906e073fSviresh kumar 				&check_lifetime_work, 0);
100334e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
10045c766d64SJiri Pirko 	}
10055c766d64SJiri Pirko 	return 0;
10061da177e4SLinus Torvalds }
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds /*
10091da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
10101da177e4SLinus Torvalds  */
10111da177e4SLinus Torvalds 
101240384999SEric Dumazet static int inet_abc_len(__be32 addr)
10131da177e4SLinus Torvalds {
10141da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
10151da177e4SLinus Torvalds 
101665cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
10171da177e4SLinus Torvalds 		rc = 0;
10181da177e4SLinus Torvalds 	else {
1019714e85beSAl Viro 		__u32 haddr = ntohl(addr);
1020714e85beSAl Viro 		if (IN_CLASSA(haddr))
10211da177e4SLinus Torvalds 			rc = 8;
1022714e85beSAl Viro 		else if (IN_CLASSB(haddr))
10231da177e4SLinus Torvalds 			rc = 16;
1024714e85beSAl Viro 		else if (IN_CLASSC(haddr))
10251da177e4SLinus Torvalds 			rc = 24;
102665cab850SDave Taht 		else if (IN_CLASSE(haddr))
102765cab850SDave Taht 			rc = 32;
10281da177e4SLinus Torvalds 	}
10291da177e4SLinus Torvalds 
10301da177e4SLinus Torvalds 	return rc;
10311da177e4SLinus Torvalds }
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 
103403aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10351da177e4SLinus Torvalds {
10361da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
103703aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10382638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10391da177e4SLinus Torvalds 	struct in_device *in_dev;
10401da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10411da177e4SLinus Torvalds 	struct net_device *dev;
10421da177e4SLinus Torvalds 	char *colon;
10431da177e4SLinus Torvalds 	int ret = -EFAULT;
10441da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10451da177e4SLinus Torvalds 
104603aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10471da177e4SLinus Torvalds 
10481da177e4SLinus Torvalds 	/* save original address for comparison */
10491da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10501da177e4SLinus Torvalds 
105103aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10521da177e4SLinus Torvalds 	if (colon)
10531da177e4SLinus Torvalds 		*colon = 0;
10541da177e4SLinus Torvalds 
105503aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds 	switch (cmd) {
10581da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10591da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10601da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10611da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10621da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10631da177e4SLinus Torvalds 		   so that we do not impose a lock.
10641da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10651da177e4SLinus Torvalds 		 */
10661da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10671da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10681da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10691da177e4SLinus Torvalds 		break;
10701da177e4SLinus Torvalds 
10711da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1072bf5b30b8SZhao Hongjiang 		ret = -EPERM;
107352e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10741da177e4SLinus Torvalds 			goto out;
10751da177e4SLinus Torvalds 		break;
10761da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10771da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10781da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10791da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1080bf5b30b8SZhao Hongjiang 		ret = -EPERM;
108152e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10821da177e4SLinus Torvalds 			goto out;
10831da177e4SLinus Torvalds 		ret = -EINVAL;
10841da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10851da177e4SLinus Torvalds 			goto out;
10861da177e4SLinus Torvalds 		break;
10871da177e4SLinus Torvalds 	default:
10881da177e4SLinus Torvalds 		ret = -EINVAL;
10891da177e4SLinus Torvalds 		goto out;
10901da177e4SLinus Torvalds 	}
10911da177e4SLinus Torvalds 
10921da177e4SLinus Torvalds 	rtnl_lock();
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds 	ret = -ENODEV;
109503aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10969f9354b9SEric Dumazet 	if (!dev)
10971da177e4SLinus Torvalds 		goto done;
10981da177e4SLinus Torvalds 
10991da177e4SLinus Torvalds 	if (colon)
11001da177e4SLinus Torvalds 		*colon = ':';
11011da177e4SLinus Torvalds 
11029f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
11039f9354b9SEric Dumazet 	if (in_dev) {
11041da177e4SLinus Torvalds 		if (tryaddrmatch) {
11051da177e4SLinus Torvalds 			/* Matthias Andree */
11061da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
11071da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
11081da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
11091da177e4SLinus Torvalds 			   This is checked above. */
11102638eb8bSFlorian Westphal 
11112638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11122638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11131da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
111403aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
11151da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
11166c91afe1SDavid S. Miller 							ifa->ifa_local) {
11171da177e4SLinus Torvalds 					break; /* found */
11181da177e4SLinus Torvalds 				}
11191da177e4SLinus Torvalds 			}
11201da177e4SLinus Torvalds 		}
11211da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
11221da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
11231da177e4SLinus Torvalds 		   comparing just the label */
11241da177e4SLinus Torvalds 		if (!ifa) {
11252638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11262638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11271da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
112803aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11291da177e4SLinus Torvalds 					break;
11301da177e4SLinus Torvalds 		}
11311da177e4SLinus Torvalds 	}
11321da177e4SLinus Torvalds 
11331da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11341da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11351da177e4SLinus Torvalds 		goto done;
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 	switch (cmd) {
11381da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
113930e948a3STonghao Zhang 		ret = 0;
11401da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
114103aef17bSAl Viro 		break;
11421da177e4SLinus Torvalds 
11431da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
114430e948a3STonghao Zhang 		ret = 0;
11451da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
114603aef17bSAl Viro 		break;
11471da177e4SLinus Torvalds 
11481da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
114930e948a3STonghao Zhang 		ret = 0;
11501da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
115103aef17bSAl Viro 		break;
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
115430e948a3STonghao Zhang 		ret = 0;
11551da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
115603aef17bSAl Viro 		break;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11591da177e4SLinus Torvalds 		if (colon) {
11601da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11611da177e4SLinus Torvalds 			if (!ifa)
11621da177e4SLinus Torvalds 				break;
11631da177e4SLinus Torvalds 			ret = 0;
116403aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11651da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11661da177e4SLinus Torvalds 			break;
11671da177e4SLinus Torvalds 		}
1168567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11691da177e4SLinus Torvalds 		break;
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11721da177e4SLinus Torvalds 		ret = -EINVAL;
11731da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11741da177e4SLinus Torvalds 			break;
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 		if (!ifa) {
11771da177e4SLinus Torvalds 			ret = -ENOBUFS;
11789f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11799f9354b9SEric Dumazet 			if (!ifa)
11801da177e4SLinus Torvalds 				break;
1181c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11821da177e4SLinus Torvalds 			if (colon)
118303aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11841da177e4SLinus Torvalds 			else
11851da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11861da177e4SLinus Torvalds 		} else {
11871da177e4SLinus Torvalds 			ret = 0;
11881da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11891da177e4SLinus Torvalds 				break;
11901da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11911da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1192148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11931da177e4SLinus Torvalds 		}
11941da177e4SLinus Torvalds 
11951da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11961da177e4SLinus Torvalds 
11971da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11981da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11991da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
12001da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12011da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
12021da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
12031da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
12041da177e4SLinus Torvalds 		} else {
12051da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
12061da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
12071da177e4SLinus Torvalds 		}
12085c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
12091da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
12101da177e4SLinus Torvalds 		break;
12111da177e4SLinus Torvalds 
12121da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
12131da177e4SLinus Torvalds 		ret = 0;
12141da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
12151da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12161da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
12171da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12181da177e4SLinus Torvalds 		}
12191da177e4SLinus Torvalds 		break;
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
12221da177e4SLinus Torvalds 		ret = 0;
12231da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
12241da177e4SLinus Torvalds 			break;
12251da177e4SLinus Torvalds 		ret = -EINVAL;
12261da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12271da177e4SLinus Torvalds 			break;
12281da177e4SLinus Torvalds 		ret = 0;
12291da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12301da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12311da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12321da177e4SLinus Torvalds 		break;
12331da177e4SLinus Torvalds 
12341da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12351da177e4SLinus Torvalds 
12361da177e4SLinus Torvalds 		/*
12371da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12381da177e4SLinus Torvalds 		 */
12391da177e4SLinus Torvalds 		ret = -EINVAL;
12401da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12411da177e4SLinus Torvalds 			break;
12421da177e4SLinus Torvalds 		ret = 0;
12431da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1244a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12451da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12461da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12471da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12481da177e4SLinus Torvalds 
12491da177e4SLinus Torvalds 			/* See if current broadcast address matches
12501da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12511da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12521da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12531da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12541da177e4SLinus Torvalds 			 */
12551da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12561da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12571da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1258dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12591da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12601da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12611da177e4SLinus Torvalds 			}
12621da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12631da177e4SLinus Torvalds 		}
12641da177e4SLinus Torvalds 		break;
12651da177e4SLinus Torvalds 	}
12661da177e4SLinus Torvalds done:
12671da177e4SLinus Torvalds 	rtnl_unlock();
12681da177e4SLinus Torvalds out:
12691da177e4SLinus Torvalds 	return ret;
12701da177e4SLinus Torvalds }
12711da177e4SLinus Torvalds 
1272b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12731da177e4SLinus Torvalds {
1274e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1275ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12761da177e4SLinus Torvalds 	struct ifreq ifr;
12771da177e4SLinus Torvalds 	int done = 0;
12781da177e4SLinus Torvalds 
127936fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
128036fd633eSAl Viro 		goto out;
128136fd633eSAl Viro 
12829f9354b9SEric Dumazet 	if (!in_dev)
12831da177e4SLinus Torvalds 		goto out;
12841da177e4SLinus Torvalds 
1285ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12861da177e4SLinus Torvalds 		if (!buf) {
128736fd633eSAl Viro 			done += size;
12881da177e4SLinus Torvalds 			continue;
12891da177e4SLinus Torvalds 		}
129036fd633eSAl Viro 		if (len < size)
12911da177e4SLinus Torvalds 			break;
12921da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12931da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12941da177e4SLinus Torvalds 
12951da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12961da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12971da177e4SLinus Torvalds 								ifa->ifa_local;
12981da177e4SLinus Torvalds 
129936fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
13001da177e4SLinus Torvalds 			done = -EFAULT;
13011da177e4SLinus Torvalds 			break;
13021da177e4SLinus Torvalds 		}
130336fd633eSAl Viro 		len  -= size;
130436fd633eSAl Viro 		done += size;
13051da177e4SLinus Torvalds 	}
13061da177e4SLinus Torvalds out:
13071da177e4SLinus Torvalds 	return done;
13081da177e4SLinus Torvalds }
13091da177e4SLinus Torvalds 
13108b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
13118b57fd1eSGao Feng 				 int scope)
13128b57fd1eSGao Feng {
1313d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1314d519e870SFlorian Westphal 
1315d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1316d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1317d519e870SFlorian Westphal 			continue;
13188b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
13198b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
13208b57fd1eSGao Feng 			return ifa->ifa_local;
1321d519e870SFlorian Westphal 	}
13228b57fd1eSGao Feng 
13238b57fd1eSGao Feng 	return 0;
13248b57fd1eSGao Feng }
13258b57fd1eSGao Feng 
1326a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13271da177e4SLinus Torvalds {
1328d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1329a61ced5dSAl Viro 	__be32 addr = 0;
1330d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13311da177e4SLinus Torvalds 	struct in_device *in_dev;
1332c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13333f2fb9a8SDavid Ahern 	int master_idx;
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 	rcu_read_lock();
1336e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13371da177e4SLinus Torvalds 	if (!in_dev)
13381da177e4SLinus Torvalds 		goto no_in_dev;
13391da177e4SLinus Torvalds 
1340d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1341d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1342d8c444d5SShijie Luo 
1343d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1344d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1345d519e870SFlorian Westphal 			continue;
1346d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13471da177e4SLinus Torvalds 			continue;
13481da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13491da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13501da177e4SLinus Torvalds 			break;
13511da177e4SLinus Torvalds 		}
13521da177e4SLinus Torvalds 		if (!addr)
13531da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1354d519e870SFlorian Westphal 	}
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds 	if (addr)
1357c6d14c84SEric Dumazet 		goto out_unlock;
13589f9354b9SEric Dumazet no_in_dev:
13593f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13601da177e4SLinus Torvalds 
136117b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
136217b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
136317b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
136417b693cdSDavid Lamparter 	 * equality check in the loop below.
136517b693cdSDavid Lamparter 	 */
136617b693cdSDavid Lamparter 	if (master_idx &&
136717b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
136817b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13698b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13708b57fd1eSGao Feng 		if (addr)
137117b693cdSDavid Lamparter 			goto out_unlock;
137217b693cdSDavid Lamparter 	}
137317b693cdSDavid Lamparter 
13741da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1375ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13761da177e4SLinus Torvalds 	   in dev_base list.
13771da177e4SLinus Torvalds 	 */
1378c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13793f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13803f2fb9a8SDavid Ahern 			continue;
13813f2fb9a8SDavid Ahern 
13829f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13839f9354b9SEric Dumazet 		if (!in_dev)
13841da177e4SLinus Torvalds 			continue;
13851da177e4SLinus Torvalds 
13868b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13878b57fd1eSGao Feng 		if (addr)
1388c6d14c84SEric Dumazet 			goto out_unlock;
13891da177e4SLinus Torvalds 	}
1390c6d14c84SEric Dumazet out_unlock:
13911da177e4SLinus Torvalds 	rcu_read_unlock();
13921da177e4SLinus Torvalds 	return addr;
13931da177e4SLinus Torvalds }
13949f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13951da177e4SLinus Torvalds 
139660cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
139760cad5daSAl Viro 			      __be32 local, int scope)
13981da177e4SLinus Torvalds {
1399650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1400ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1401a144ea4bSAl Viro 	__be32 addr = 0;
1402ef11db33SFlorian Westphal 	int same = 0;
14031da177e4SLinus Torvalds 
1404650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1405650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1406650638a7SShijie Luo 
1407ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1408650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1409650638a7SShijie Luo 
14101da177e4SLinus Torvalds 		if (!addr &&
14111da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1412650638a7SShijie Luo 		    min_scope <= scope) {
14131da177e4SLinus Torvalds 			addr = ifa->ifa_local;
14141da177e4SLinus Torvalds 			if (same)
14151da177e4SLinus Torvalds 				break;
14161da177e4SLinus Torvalds 		}
14171da177e4SLinus Torvalds 		if (!same) {
14181da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
14191da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
14201da177e4SLinus Torvalds 			if (same && addr) {
14211da177e4SLinus Torvalds 				if (local || !dst)
14221da177e4SLinus Torvalds 					break;
14231da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
14241da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
14251da177e4SLinus Torvalds 					break;
14261da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1427650638a7SShijie Luo 				if (min_scope <= scope) {
14281da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14291da177e4SLinus Torvalds 					break;
14301da177e4SLinus Torvalds 				}
14311da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14321da177e4SLinus Torvalds 				same = 0;
14331da177e4SLinus Torvalds 			}
14341da177e4SLinus Torvalds 		}
1435ef11db33SFlorian Westphal 	}
14361da177e4SLinus Torvalds 
14371da177e4SLinus Torvalds 	return same ? addr : 0;
14381da177e4SLinus Torvalds }
14391da177e4SLinus Torvalds 
14401da177e4SLinus Torvalds /*
14411da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1442b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1443b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14441da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14451da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14461da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14471da177e4SLinus Torvalds  */
1448b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14499bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14501da177e4SLinus Torvalds {
145160cad5daSAl Viro 	__be32 addr = 0;
14529bd85e32SDenis V. Lunev 	struct net_device *dev;
14531da177e4SLinus Torvalds 
145400db4124SIan Morris 	if (in_dev)
14559bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14561da177e4SLinus Torvalds 
14571da177e4SLinus Torvalds 	rcu_read_lock();
1458c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14599f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14609f9354b9SEric Dumazet 		if (in_dev) {
14611da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14621da177e4SLinus Torvalds 			if (addr)
14631da177e4SLinus Torvalds 				break;
14641da177e4SLinus Torvalds 		}
14651da177e4SLinus Torvalds 	}
14661da177e4SLinus Torvalds 	rcu_read_unlock();
14671da177e4SLinus Torvalds 
14681da177e4SLinus Torvalds 	return addr;
14691da177e4SLinus Torvalds }
1470eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14711da177e4SLinus Torvalds 
14721da177e4SLinus Torvalds /*
14731da177e4SLinus Torvalds  *	Device notifier
14741da177e4SLinus Torvalds  */
14751da177e4SLinus Torvalds 
14761da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14771da177e4SLinus Torvalds {
1478e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14791da177e4SLinus Torvalds }
14809f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14811da177e4SLinus Torvalds 
14821da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14831da177e4SLinus Torvalds {
1484e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14851da177e4SLinus Torvalds }
14869f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14871da177e4SLinus Torvalds 
14883ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14893ad7d246SKrister Johansen {
14903ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14913ad7d246SKrister Johansen }
14923ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14933ad7d246SKrister Johansen 
14943ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14953ad7d246SKrister Johansen {
14963ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14973ad7d246SKrister Johansen 	    nb);
14983ad7d246SKrister Johansen }
14993ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
15003ad7d246SKrister Johansen 
15019f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
15029f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
15031da177e4SLinus Torvalds */
15041da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
15051da177e4SLinus Torvalds {
15061da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
15071da177e4SLinus Torvalds 	int named = 0;
15081da177e4SLinus Torvalds 
1509ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
15101da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
15111da177e4SLinus Torvalds 
15121da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
15131da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15141da177e4SLinus Torvalds 		if (named++ == 0)
1515573bf470SThomas Graf 			goto skip;
151644344b2aSMark McLoughlin 		dot = strchr(old, ':');
151751456b29SIan Morris 		if (!dot) {
15181da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
15191da177e4SLinus Torvalds 			dot = old;
15201da177e4SLinus Torvalds 		}
15219f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
15221da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
15239f9354b9SEric Dumazet 		else
15241da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1525573bf470SThomas Graf skip:
1526573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15271da177e4SLinus Torvalds 	}
15281da177e4SLinus Torvalds }
15291da177e4SLinus Torvalds 
1530d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1531d11327adSIan Campbell 					struct in_device *in_dev)
1532d11327adSIan Campbell 
1533d11327adSIan Campbell {
1534ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1535d11327adSIan Campbell 
1536ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1537d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15386c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15396c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1540d11327adSIan Campbell 			 dev->dev_addr, NULL);
1541d11327adSIan Campbell 	}
1542b76d0789SZoltan Kiss }
1543d11327adSIan Campbell 
15441da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15451da177e4SLinus Torvalds 
15461da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15471da177e4SLinus Torvalds 			 void *ptr)
15481da177e4SLinus Torvalds {
1549351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1550748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15511da177e4SLinus Torvalds 
15521da177e4SLinus Torvalds 	ASSERT_RTNL();
15531da177e4SLinus Torvalds 
15541da177e4SLinus Torvalds 	if (!in_dev) {
15558030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15561da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
155720e61da7SWANG Cong 			if (IS_ERR(in_dev))
155820e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15590cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
156042f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
156142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15621da177e4SLinus Torvalds 			}
156306770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
156406770843SBreno Leitao 			/* Re-enabling IP */
156506770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
156606770843SBreno Leitao 				in_dev = inetdev_init(dev);
15678030f544SHerbert Xu 		}
15681da177e4SLinus Torvalds 		goto out;
15691da177e4SLinus Torvalds 	}
15701da177e4SLinus Torvalds 
15711da177e4SLinus Torvalds 	switch (event) {
15721da177e4SLinus Torvalds 	case NETDEV_REGISTER:
157391df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1574a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15751da177e4SLinus Torvalds 		break;
15761da177e4SLinus Torvalds 	case NETDEV_UP:
157706770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15781da177e4SLinus Torvalds 			break;
15790cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15809f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15819f9354b9SEric Dumazet 
15829f9354b9SEric Dumazet 			if (ifa) {
1583fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15841da177e4SLinus Torvalds 				ifa->ifa_local =
15851da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15861da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15871da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15881da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15891da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15901da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15911da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15925c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15935c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1594dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1595dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15961da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15971da177e4SLinus Torvalds 			}
15981da177e4SLinus Torvalds 		}
15991da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1600a8eceea8SJoe Perches 		fallthrough;
1601eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1602d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1603d11327adSIan Campbell 			break;
1604a8eceea8SJoe Perches 		fallthrough;
1605d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1606a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1607d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
16081da177e4SLinus Torvalds 		break;
16091da177e4SLinus Torvalds 	case NETDEV_DOWN:
16101da177e4SLinus Torvalds 		ip_mc_down(in_dev);
16111da177e4SLinus Torvalds 		break;
161293d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
161375c78500SMoni Shoua 		ip_mc_unmap(in_dev);
161475c78500SMoni Shoua 		break;
161593d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
161675c78500SMoni Shoua 		ip_mc_remap(in_dev);
161775c78500SMoni Shoua 		break;
16181da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
161906770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
16201da177e4SLinus Torvalds 			break;
162106770843SBreno Leitao 		/* disable IP when MTU is not enough */
1622a8eceea8SJoe Perches 		fallthrough;
16231da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
16241da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
16251da177e4SLinus Torvalds 		break;
16261da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16271da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16281da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16291da177e4SLinus Torvalds 		 */
16301da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16311da177e4SLinus Torvalds 
163251602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
163366f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16341da177e4SLinus Torvalds 		break;
16351da177e4SLinus Torvalds 	}
16361da177e4SLinus Torvalds out:
16371da177e4SLinus Torvalds 	return NOTIFY_DONE;
16381da177e4SLinus Torvalds }
16391da177e4SLinus Torvalds 
16401da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16411da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16421da177e4SLinus Torvalds };
16431da177e4SLinus Torvalds 
164440384999SEric Dumazet static size_t inet_nlmsg_size(void)
1645339bf98fSThomas Graf {
1646339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1647339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1648339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1649339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1650ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
165163b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
165247f0bd50SJacques de Laval 	       + nla_total_size(1)  /* IFA_PROTO */
1653af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
165463b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1655339bf98fSThomas Graf }
1656339bf98fSThomas Graf 
16575c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16585c766d64SJiri Pirko {
16595c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16605c766d64SJiri Pirko }
16615c766d64SJiri Pirko 
16625c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16635c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16645c766d64SJiri Pirko {
16655c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16665c766d64SJiri Pirko 
16675c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16685c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16695c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16705c766d64SJiri Pirko 	ci.ifa_valid = valid;
16715c766d64SJiri Pirko 
16725c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16735c766d64SJiri Pirko }
16745c766d64SJiri Pirko 
16751da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1676978a46faSChristian Brauner 			    struct inet_fill_args *args)
16771da177e4SLinus Torvalds {
16781da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16791da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
1680*3cd3e72cSEric Dumazet 	unsigned long tstamp;
16815c766d64SJiri Pirko 	u32 preferred, valid;
16821da177e4SLinus Torvalds 
1683978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1684978a46faSChristian Brauner 			args->flags);
168551456b29SIan Morris 	if (!nlh)
168626932566SPatrick McHardy 		return -EMSGSIZE;
168747f68512SThomas Graf 
168847f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16891da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16901da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16915c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16921da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16931da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16941da177e4SLinus Torvalds 
1695978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1696978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1697d3807145SChristian Brauner 		goto nla_put_failure;
1698d3807145SChristian Brauner 
1699*3cd3e72cSEric Dumazet 	tstamp = READ_ONCE(ifa->ifa_tstamp);
17005c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
17015c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
17025c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
17035c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
1704*3cd3e72cSEric Dumazet 			long tval = (jiffies - tstamp) / HZ;
17055c766d64SJiri Pirko 
17065c766d64SJiri Pirko 			if (preferred > tval)
17075c766d64SJiri Pirko 				preferred -= tval;
17085c766d64SJiri Pirko 			else
17095c766d64SJiri Pirko 				preferred = 0;
17105c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
17115c766d64SJiri Pirko 				if (valid > tval)
17125c766d64SJiri Pirko 					valid -= tval;
17135c766d64SJiri Pirko 				else
17145c766d64SJiri Pirko 					valid = 0;
17155c766d64SJiri Pirko 			}
17165c766d64SJiri Pirko 		}
17175c766d64SJiri Pirko 	} else {
17185c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
17195c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
17205c766d64SJiri Pirko 	}
1721f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1722930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1723f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1724930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1725f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1726930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1727f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
17285c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
172947f0bd50SJacques de Laval 	    (ifa->ifa_proto &&
173047f0bd50SJacques de Laval 	     nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
1731ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1732af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1733af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
1734*3cd3e72cSEric Dumazet 	    put_cacheinfo(skb, READ_ONCE(ifa->ifa_cstamp), tstamp,
17355c766d64SJiri Pirko 			  preferred, valid))
1736f3756b79SDavid S. Miller 		goto nla_put_failure;
173747f68512SThomas Graf 
1738053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1739053c095aSJohannes Berg 	return 0;
174047f68512SThomas Graf 
174147f68512SThomas Graf nla_put_failure:
174226932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
174326932566SPatrick McHardy 	return -EMSGSIZE;
17441da177e4SLinus Torvalds }
17451da177e4SLinus Torvalds 
1746c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1747c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1748c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17495fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1750c33078e3SDavid Ahern {
17515fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1752c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1753c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1754c33078e3SDavid Ahern 	int err, i;
1755c33078e3SDavid Ahern 
1756c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1757c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1758c33078e3SDavid Ahern 		return -EINVAL;
1759c33078e3SDavid Ahern 	}
1760c33078e3SDavid Ahern 
1761c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1762c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1763c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1764c33078e3SDavid Ahern 		return -EINVAL;
1765c33078e3SDavid Ahern 	}
17665fcd266aSDavid Ahern 
17675fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17685fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17695fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17705fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1771c33078e3SDavid Ahern 	}
1772c33078e3SDavid Ahern 
17738cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1774c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1775c33078e3SDavid Ahern 	if (err < 0)
1776c33078e3SDavid Ahern 		return err;
1777c33078e3SDavid Ahern 
1778c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1779c33078e3SDavid Ahern 		if (!tb[i])
1780c33078e3SDavid Ahern 			continue;
1781c33078e3SDavid Ahern 
1782c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1783c33078e3SDavid Ahern 			struct net *net;
1784c33078e3SDavid Ahern 
1785c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1786c33078e3SDavid Ahern 
1787c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1788c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1789bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1790c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1791c33078e3SDavid Ahern 				return PTR_ERR(net);
1792c33078e3SDavid Ahern 			}
1793c33078e3SDavid Ahern 			*tgt_net = net;
1794c33078e3SDavid Ahern 		} else {
1795c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1796c33078e3SDavid Ahern 			return -EINVAL;
1797c33078e3SDavid Ahern 		}
1798c33078e3SDavid Ahern 	}
1799c33078e3SDavid Ahern 
1800c33078e3SDavid Ahern 	return 0;
1801c33078e3SDavid Ahern }
1802c33078e3SDavid Ahern 
18031c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
18041c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
18051c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
18061c98eca4SDavid Ahern {
18071c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
18081c98eca4SDavid Ahern 	int ip_idx = 0;
18091c98eca4SDavid Ahern 	int err;
18101c98eca4SDavid Ahern 
1811d3e6e285SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1812ef11db33SFlorian Westphal 		if (ip_idx < s_ip_idx) {
1813ef11db33SFlorian Westphal 			ip_idx++;
18141c98eca4SDavid Ahern 			continue;
1815ef11db33SFlorian Westphal 		}
18161c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
18171c98eca4SDavid Ahern 		if (err < 0)
18181c98eca4SDavid Ahern 			goto done;
18191c98eca4SDavid Ahern 
18201c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1821ef11db33SFlorian Westphal 		ip_idx++;
18221c98eca4SDavid Ahern 	}
18231c98eca4SDavid Ahern 	err = 0;
18241c98eca4SDavid Ahern 
18251c98eca4SDavid Ahern done:
18261c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
18271c98eca4SDavid Ahern 
18281c98eca4SDavid Ahern 	return err;
18291c98eca4SDavid Ahern }
18301c98eca4SDavid Ahern 
1831081a0e3bSEric Dumazet /* Combine dev_addr_genid and dev_base_seq to detect changes.
1832081a0e3bSEric Dumazet  */
1833081a0e3bSEric Dumazet static u32 inet_base_seq(const struct net *net)
1834081a0e3bSEric Dumazet {
1835081a0e3bSEric Dumazet 	u32 res = atomic_read(&net->ipv4.dev_addr_genid) +
1836081a0e3bSEric Dumazet 		  net->dev_base_seq;
1837081a0e3bSEric Dumazet 
1838081a0e3bSEric Dumazet 	/* Must not return 0 (see nl_dump_check_consistent()).
1839081a0e3bSEric Dumazet 	 * Chose a value far away from 0.
1840081a0e3bSEric Dumazet 	 */
1841081a0e3bSEric Dumazet 	if (!res)
1842081a0e3bSEric Dumazet 		res = 0x80000000;
1843081a0e3bSEric Dumazet 	return res;
1844081a0e3bSEric Dumazet }
1845081a0e3bSEric Dumazet 
18461da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18471da177e4SLinus Torvalds {
1848c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1849978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1850978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1851c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1852978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1853978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1854978a46faSChristian Brauner 		.netnsid = -1,
1855978a46faSChristian Brauner 	};
18563b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1857d3807145SChristian Brauner 	struct net *tgt_net = net;
1858eec4df98SEric Dumazet 	int h, s_h;
1859eec4df98SEric Dumazet 	int idx, s_idx;
18601c98eca4SDavid Ahern 	int s_ip_idx;
18611da177e4SLinus Torvalds 	struct net_device *dev;
18621da177e4SLinus Torvalds 	struct in_device *in_dev;
1863eec4df98SEric Dumazet 	struct hlist_head *head;
1864d7e38611SDavid Ahern 	int err = 0;
18651da177e4SLinus Torvalds 
1866eec4df98SEric Dumazet 	s_h = cb->args[0];
1867eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
18681c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1869eec4df98SEric Dumazet 
1870c33078e3SDavid Ahern 	if (cb->strict_check) {
1871c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18725fcd266aSDavid Ahern 						 skb->sk, cb);
1873c33078e3SDavid Ahern 		if (err < 0)
1874d7e38611SDavid Ahern 			goto put_tgt_net;
18755fcd266aSDavid Ahern 
1876d7e38611SDavid Ahern 		err = 0;
18775fcd266aSDavid Ahern 		if (fillargs.ifindex) {
18785fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1879d7e38611SDavid Ahern 			if (!dev) {
1880d7e38611SDavid Ahern 				err = -ENODEV;
1881d7e38611SDavid Ahern 				goto put_tgt_net;
1882d7e38611SDavid Ahern 			}
18835fcd266aSDavid Ahern 
18845fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
18855fcd266aSDavid Ahern 			if (in_dev) {
18865fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18875fcd266aSDavid Ahern 						       &fillargs);
18885fcd266aSDavid Ahern 			}
18895fcd266aSDavid Ahern 			goto put_tgt_net;
18905fcd266aSDavid Ahern 		}
1891d3807145SChristian Brauner 	}
1892d3807145SChristian Brauner 
1893eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18947562f876SPavel Emelianov 		idx = 0;
1895d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1896eec4df98SEric Dumazet 		rcu_read_lock();
1897081a0e3bSEric Dumazet 		cb->seq = inet_base_seq(tgt_net);
1898b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18991da177e4SLinus Torvalds 			if (idx < s_idx)
19007562f876SPavel Emelianov 				goto cont;
19014b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
19021da177e4SLinus Torvalds 				s_ip_idx = 0;
1903eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
19049f9354b9SEric Dumazet 			if (!in_dev)
19057562f876SPavel Emelianov 				goto cont;
19061da177e4SLinus Torvalds 
19071c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
19081c98eca4SDavid Ahern 					       &fillargs);
19091c98eca4SDavid Ahern 			if (err < 0) {
1910eec4df98SEric Dumazet 				rcu_read_unlock();
19111da177e4SLinus Torvalds 				goto done;
19121da177e4SLinus Torvalds 			}
19137562f876SPavel Emelianov cont:
19147562f876SPavel Emelianov 			idx++;
19151da177e4SLinus Torvalds 		}
1916eec4df98SEric Dumazet 		rcu_read_unlock();
1917eec4df98SEric Dumazet 	}
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds done:
1920eec4df98SEric Dumazet 	cb->args[0] = h;
1921eec4df98SEric Dumazet 	cb->args[1] = idx;
19225fcd266aSDavid Ahern put_tgt_net:
1923978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1924d3807145SChristian Brauner 		put_net(tgt_net);
19251da177e4SLinus Torvalds 
19267c1e8a38SArthur Gautier 	return skb->len ? : err;
19271da177e4SLinus Torvalds }
19281da177e4SLinus Torvalds 
1929d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
193015e47304SEric W. Biederman 		      u32 portid)
19311da177e4SLinus Torvalds {
1932978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1933978a46faSChristian Brauner 		.portid = portid,
1934978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1935978a46faSChristian Brauner 		.event = event,
1936978a46faSChristian Brauner 		.flags = 0,
1937978a46faSChristian Brauner 		.netnsid = -1,
1938978a46faSChristian Brauner 	};
193947f68512SThomas Graf 	struct sk_buff *skb;
1940d6062cbbSThomas Graf 	int err = -ENOBUFS;
19414b8aa9abSDenis V. Lunev 	struct net *net;
19421da177e4SLinus Torvalds 
1943c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1944339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
194551456b29SIan Morris 	if (!skb)
1946d6062cbbSThomas Graf 		goto errout;
1947d6062cbbSThomas Graf 
1948978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
194926932566SPatrick McHardy 	if (err < 0) {
195026932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
195126932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
195226932566SPatrick McHardy 		kfree_skb(skb);
195326932566SPatrick McHardy 		goto errout;
195426932566SPatrick McHardy 	}
195515e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19561ce85fe4SPablo Neira Ayuso 	return;
1957d6062cbbSThomas Graf errout:
1958d6062cbbSThomas Graf 	if (err < 0)
19594b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19601da177e4SLinus Torvalds }
19611da177e4SLinus Torvalds 
1962b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1963b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19649f0f7272SThomas Graf {
19651fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19669f0f7272SThomas Graf 
19679f0f7272SThomas Graf 	if (!in_dev)
19689f0f7272SThomas Graf 		return 0;
19699f0f7272SThomas Graf 
19709f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19719f0f7272SThomas Graf }
19729f0f7272SThomas Graf 
1973d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1974d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19759f0f7272SThomas Graf {
19761fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19779f0f7272SThomas Graf 	struct nlattr *nla;
19789f0f7272SThomas Graf 	int i;
19799f0f7272SThomas Graf 
19809f0f7272SThomas Graf 	if (!in_dev)
19819f0f7272SThomas Graf 		return -ENODATA;
19829f0f7272SThomas Graf 
19839f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
198451456b29SIan Morris 	if (!nla)
19859f0f7272SThomas Graf 		return -EMSGSIZE;
19869f0f7272SThomas Graf 
19879f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19880598f8f3SEric Dumazet 		((u32 *) nla_data(nla))[i] = READ_ONCE(in_dev->cnf.data[i]);
19899f0f7272SThomas Graf 
19909f0f7272SThomas Graf 	return 0;
19919f0f7272SThomas Graf }
19929f0f7272SThomas Graf 
19939f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19949f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19959f0f7272SThomas Graf };
19969f0f7272SThomas Graf 
1997cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
19988679c31eSRocco Yue 				 const struct nlattr *nla,
19998679c31eSRocco Yue 				 struct netlink_ext_ack *extack)
20009f0f7272SThomas Graf {
20019f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
20029f0f7272SThomas Graf 	int err, rem;
20039f0f7272SThomas Graf 
2004a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
2005cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
20069f0f7272SThomas Graf 
20078cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
20088679c31eSRocco Yue 					  inet_af_policy, extack);
20099f0f7272SThomas Graf 	if (err < 0)
20109f0f7272SThomas Graf 		return err;
20119f0f7272SThomas Graf 
20129f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20139f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
20149f0f7272SThomas Graf 			int cfgid = nla_type(a);
20159f0f7272SThomas Graf 
20169f0f7272SThomas Graf 			if (nla_len(a) < 4)
20179f0f7272SThomas Graf 				return -EINVAL;
20189f0f7272SThomas Graf 
20199f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
20209f0f7272SThomas Graf 				return -EINVAL;
20219f0f7272SThomas Graf 		}
20229f0f7272SThomas Graf 	}
20239f0f7272SThomas Graf 
2024cf7afbfeSThomas Graf 	return 0;
2025cf7afbfeSThomas Graf }
2026cf7afbfeSThomas Graf 
20273583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
20283583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
2029cf7afbfeSThomas Graf {
2030a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
2031cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
2032cf7afbfeSThomas Graf 	int rem;
2033cf7afbfeSThomas Graf 
2034cf7afbfeSThomas Graf 	if (!in_dev)
2035cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
2036cf7afbfeSThomas Graf 
20378cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
20385ac6b198SZheng Yongjun 		return -EINVAL;
2039cf7afbfeSThomas Graf 
20409f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20419f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
20429f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
20439f0f7272SThomas Graf 	}
20449f0f7272SThomas Graf 
20459f0f7272SThomas Graf 	return 0;
20469f0f7272SThomas Graf }
20479f0f7272SThomas Graf 
2048edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2049edc9e748SNicolas Dichtel {
2050edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2051edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2052136ba622SZhang Shengju 	bool all = false;
2053edc9e748SNicolas Dichtel 
2054136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2055136ba622SZhang Shengju 		all = true;
2056136ba622SZhang Shengju 
2057136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2058edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2059136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2060cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2061136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2062d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20635cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20645cbf777cSXin Long 		size += nla_total_size(4);
2065136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2066f085ff1cSstephen hemminger 		size += nla_total_size(4);
2067136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2068974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2069edc9e748SNicolas Dichtel 
2070edc9e748SNicolas Dichtel 	return size;
2071edc9e748SNicolas Dichtel }
2072edc9e748SNicolas Dichtel 
2073edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
20740598f8f3SEric Dumazet 				     const struct ipv4_devconf *devconf,
20750598f8f3SEric Dumazet 				     u32 portid, u32 seq, int event,
20760598f8f3SEric Dumazet 				     unsigned int flags, int type)
2077edc9e748SNicolas Dichtel {
2078edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2079edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2080136ba622SZhang Shengju 	bool all = false;
2081edc9e748SNicolas Dichtel 
2082edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2083edc9e748SNicolas Dichtel 			flags);
208451456b29SIan Morris 	if (!nlh)
2085edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2086edc9e748SNicolas Dichtel 
2087136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2088136ba622SZhang Shengju 		all = true;
2089136ba622SZhang Shengju 
2090edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2091edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2092edc9e748SNicolas Dichtel 
2093edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2094edc9e748SNicolas Dichtel 		goto nla_put_failure;
2095edc9e748SNicolas Dichtel 
2096b5c9641dSDavid Ahern 	if (!devconf)
2097b5c9641dSDavid Ahern 		goto out;
2098b5c9641dSDavid Ahern 
2099136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2100edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
21010598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, FORWARDING)) < 0)
2102edc9e748SNicolas Dichtel 		goto nla_put_failure;
2103136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2104cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
21050598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, RP_FILTER)) < 0)
2106cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2107136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2108d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
21090598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, MC_FORWARDING)) < 0)
2110d67b8c61SNicolas Dichtel 		goto nla_put_failure;
21115cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
21125cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
21130598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, BC_FORWARDING)) < 0)
21145cbf777cSXin Long 		goto nla_put_failure;
2115136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
211609aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
21170598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf, PROXY_ARP)) < 0)
2118f085ff1cSstephen hemminger 		goto nla_put_failure;
2119136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2120974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
21210598f8f3SEric Dumazet 			IPV4_DEVCONF_RO(*devconf,
21220598f8f3SEric Dumazet 					IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2123974d7af5SAndy Gospodarek 		goto nla_put_failure;
2124edc9e748SNicolas Dichtel 
2125b5c9641dSDavid Ahern out:
2126053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2127053c095aSJohannes Berg 	return 0;
2128edc9e748SNicolas Dichtel 
2129edc9e748SNicolas Dichtel nla_put_failure:
2130edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2131edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2132edc9e748SNicolas Dichtel }
2133edc9e748SNicolas Dichtel 
21343b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
21353b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2136edc9e748SNicolas Dichtel {
2137edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2138edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2139edc9e748SNicolas Dichtel 
2140fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
214151456b29SIan Morris 	if (!skb)
2142edc9e748SNicolas Dichtel 		goto errout;
2143edc9e748SNicolas Dichtel 
2144edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
21453b022865SDavid Ahern 					event, 0, type);
2146edc9e748SNicolas Dichtel 	if (err < 0) {
2147edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2148edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2149edc9e748SNicolas Dichtel 		kfree_skb(skb);
2150edc9e748SNicolas Dichtel 		goto errout;
2151edc9e748SNicolas Dichtel 	}
2152fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2153edc9e748SNicolas Dichtel 	return;
2154edc9e748SNicolas Dichtel errout:
2155edc9e748SNicolas Dichtel 	if (err < 0)
2156edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2157edc9e748SNicolas Dichtel }
2158edc9e748SNicolas Dichtel 
21599e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21609e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21619e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2162cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
216309aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2164974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21659e551110SNicolas Dichtel };
21669e551110SNicolas Dichtel 
2167eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2168eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2169eede370dSJakub Kicinski 				      struct nlattr **tb,
2170eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2171eede370dSJakub Kicinski {
2172eede370dSJakub Kicinski 	int i, err;
2173eede370dSJakub Kicinski 
2174eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2175eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2176eede370dSJakub Kicinski 		return -EINVAL;
2177eede370dSJakub Kicinski 	}
2178eede370dSJakub Kicinski 
2179eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21808cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21818cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21828cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2183eede370dSJakub Kicinski 
21848cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21858cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21868cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2187eede370dSJakub Kicinski 	if (err)
2188eede370dSJakub Kicinski 		return err;
2189eede370dSJakub Kicinski 
2190eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2191eede370dSJakub Kicinski 		if (!tb[i])
2192eede370dSJakub Kicinski 			continue;
2193eede370dSJakub Kicinski 
2194eede370dSJakub Kicinski 		switch (i) {
2195eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2196eede370dSJakub Kicinski 			break;
2197eede370dSJakub Kicinski 		default:
2198eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2199eede370dSJakub Kicinski 			return -EINVAL;
2200eede370dSJakub Kicinski 		}
2201eede370dSJakub Kicinski 	}
2202eede370dSJakub Kicinski 
2203eede370dSJakub Kicinski 	return 0;
2204eede370dSJakub Kicinski }
2205eede370dSJakub Kicinski 
22069e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2207c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2208c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
22099e551110SNicolas Dichtel {
22109e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
22119e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX + 1];
2212bbcf9105SEric Dumazet 	const struct ipv4_devconf *devconf;
2213bbcf9105SEric Dumazet 	struct in_device *in_dev = NULL;
2214bbcf9105SEric Dumazet 	struct net_device *dev = NULL;
22159e551110SNicolas Dichtel 	struct sk_buff *skb;
22169e551110SNicolas Dichtel 	int ifindex;
22179e551110SNicolas Dichtel 	int err;
22189e551110SNicolas Dichtel 
2219eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2220eede370dSJakub Kicinski 	if (err)
2221bbcf9105SEric Dumazet 		return err;
22229e551110SNicolas Dichtel 
22239e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
2224bbcf9105SEric Dumazet 		return -EINVAL;
22259e551110SNicolas Dichtel 
22269e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
22279e551110SNicolas Dichtel 	switch (ifindex) {
22289e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
22299e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
22309e551110SNicolas Dichtel 		break;
22319e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
22329e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
22339e551110SNicolas Dichtel 		break;
22349e551110SNicolas Dichtel 	default:
2235bbcf9105SEric Dumazet 		err = -ENODEV;
2236bbcf9105SEric Dumazet 		dev = dev_get_by_index(net, ifindex);
2237bbcf9105SEric Dumazet 		if (dev)
2238bbcf9105SEric Dumazet 			in_dev = in_dev_get(dev);
223951456b29SIan Morris 		if (!in_dev)
22409e551110SNicolas Dichtel 			goto errout;
22419e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
22429e551110SNicolas Dichtel 		break;
22439e551110SNicolas Dichtel 	}
22449e551110SNicolas Dichtel 
22459e551110SNicolas Dichtel 	err = -ENOBUFS;
2246fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
224751456b29SIan Morris 	if (!skb)
22489e551110SNicolas Dichtel 		goto errout;
22499e551110SNicolas Dichtel 
22509e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22519e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22529e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2253136ba622SZhang Shengju 					NETCONFA_ALL);
22549e551110SNicolas Dichtel 	if (err < 0) {
22559e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22569e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22579e551110SNicolas Dichtel 		kfree_skb(skb);
22589e551110SNicolas Dichtel 		goto errout;
22599e551110SNicolas Dichtel 	}
22609e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22619e551110SNicolas Dichtel errout:
2262bbcf9105SEric Dumazet 	if (in_dev)
2263bbcf9105SEric Dumazet 		in_dev_put(in_dev);
2264bbcf9105SEric Dumazet 	dev_put(dev);
22659e551110SNicolas Dichtel 	return err;
22669e551110SNicolas Dichtel }
22679e551110SNicolas Dichtel 
22687a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22697a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22707a674200SNicolas Dichtel {
2271addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22727a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
227316748707SEric Dumazet 	struct {
227416748707SEric Dumazet 		unsigned long ifindex;
227516748707SEric Dumazet 		unsigned int all_default;
227616748707SEric Dumazet 	} *ctx = (void *)cb->ctx;
227716748707SEric Dumazet 	const struct in_device *in_dev;
22787a674200SNicolas Dichtel 	struct net_device *dev;
227916748707SEric Dumazet 	int err = 0;
22807a674200SNicolas Dichtel 
2281addd383fSDavid Ahern 	if (cb->strict_check) {
2282addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2283addd383fSDavid Ahern 		struct netconfmsg *ncm;
2284addd383fSDavid Ahern 
2285addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2286addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2287addd383fSDavid Ahern 			return -EINVAL;
2288addd383fSDavid Ahern 		}
2289addd383fSDavid Ahern 
2290addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2291addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2292addd383fSDavid Ahern 			return -EINVAL;
2293addd383fSDavid Ahern 		}
2294addd383fSDavid Ahern 	}
2295addd383fSDavid Ahern 
22967a674200SNicolas Dichtel 	rcu_read_lock();
229716748707SEric Dumazet 	for_each_netdev_dump(net, dev, ctx->ifindex) {
22987a674200SNicolas Dichtel 		in_dev = __in_dev_get_rcu(dev);
22997a674200SNicolas Dichtel 		if (!in_dev)
230016748707SEric Dumazet 			continue;
230116748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, dev->ifindex,
23027a674200SNicolas Dichtel 						&in_dev->cnf,
23037a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2304addd383fSDavid Ahern 						nlh->nlmsg_seq,
230516748707SEric Dumazet 						RTM_NEWNETCONF, NLM_F_MULTI,
230616748707SEric Dumazet 						NETCONFA_ALL);
230716748707SEric Dumazet 		if (err < 0)
23087a674200SNicolas Dichtel 			goto done;
23097a674200SNicolas Dichtel 	}
231016748707SEric Dumazet 	if (ctx->all_default == 0) {
231116748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
23127a674200SNicolas Dichtel 						net->ipv4.devconf_all,
23137a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2314addd383fSDavid Ahern 						nlh->nlmsg_seq,
23157a674200SNicolas Dichtel 						RTM_NEWNETCONF, NLM_F_MULTI,
231616748707SEric Dumazet 						NETCONFA_ALL);
231716748707SEric Dumazet 		if (err < 0)
23187a674200SNicolas Dichtel 			goto done;
231916748707SEric Dumazet 		ctx->all_default++;
23207a674200SNicolas Dichtel 	}
232116748707SEric Dumazet 	if (ctx->all_default == 1) {
232216748707SEric Dumazet 		err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
23237a674200SNicolas Dichtel 						net->ipv4.devconf_dflt,
23247a674200SNicolas Dichtel 						NETLINK_CB(cb->skb).portid,
2325addd383fSDavid Ahern 						nlh->nlmsg_seq,
23267a674200SNicolas Dichtel 						RTM_NEWNETCONF, NLM_F_MULTI,
232716748707SEric Dumazet 						NETCONFA_ALL);
232816748707SEric Dumazet 		if (err < 0)
23297a674200SNicolas Dichtel 			goto done;
233016748707SEric Dumazet 		ctx->all_default++;
23317a674200SNicolas Dichtel 	}
23327a674200SNicolas Dichtel done:
233316748707SEric Dumazet 	if (err < 0 && likely(skb->len))
233416748707SEric Dumazet 		err = skb->len;
233516748707SEric Dumazet 	rcu_read_unlock();
233616748707SEric Dumazet 	return err;
23377a674200SNicolas Dichtel }
23387a674200SNicolas Dichtel 
23391da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23401da177e4SLinus Torvalds 
2341c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
234231be3085SHerbert Xu {
234331be3085SHerbert Xu 	struct net_device *dev;
234431be3085SHerbert Xu 
234531be3085SHerbert Xu 	rcu_read_lock();
2346c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2347c6d14c84SEric Dumazet 		struct in_device *in_dev;
2348c6d14c84SEric Dumazet 
234931be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
235031be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23519355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2352c6d14c84SEric Dumazet 	}
235331be3085SHerbert Xu 	rcu_read_unlock();
235431be3085SHerbert Xu }
235531be3085SHerbert Xu 
2356c6d14c84SEric Dumazet /* called with RTNL locked */
2357c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
235868dd299bSPavel Emelyanov {
235968dd299bSPavel Emelyanov 	struct net_device *dev;
2360586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
236168dd299bSPavel Emelyanov 
2362586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23639355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23643b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23653b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2366edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2367edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23683b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23693b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2370edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2371edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
237268dd299bSPavel Emelyanov 
2373c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
237468dd299bSPavel Emelyanov 		struct in_device *in_dev;
2375fa17806cSEric Dumazet 
23760187bdfbSBen Hutchings 		if (on)
23770187bdfbSBen Hutchings 			dev_disable_lro(dev);
2378fa17806cSEric Dumazet 
2379fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2380edc9e748SNicolas Dichtel 		if (in_dev) {
238168dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23823b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23833b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2384edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2385edc9e748SNicolas Dichtel 		}
238668dd299bSPavel Emelyanov 	}
238768dd299bSPavel Emelyanov }
238868dd299bSPavel Emelyanov 
2389f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2390f085ff1cSstephen hemminger {
2391f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2392f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2393f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2394f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2395f085ff1cSstephen hemminger 	else {
2396f085ff1cSstephen hemminger 		struct in_device *idev
2397f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2398f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2399f085ff1cSstephen hemminger 	}
2400f085ff1cSstephen hemminger }
2401f085ff1cSstephen hemminger 
2402fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
240332927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
240431be3085SHerbert Xu {
2405d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
24068d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2407d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
240831be3085SHerbert Xu 
240931be3085SHerbert Xu 	if (write) {
241031be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2411c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
241231be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2413f085ff1cSstephen hemminger 		int ifindex;
241431be3085SHerbert Xu 
241531be3085SHerbert Xu 		set_bit(i, cnf->state);
241631be3085SHerbert Xu 
24179355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2418c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2419d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2420d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2421d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
24224ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2423f085ff1cSstephen hemminger 
24245cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
24255cbf777cSXin Long 		    new_value != old_value)
24265cbf777cSXin Long 			rt_cache_flush(net);
24275cbf777cSXin Long 
2428cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2429cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2430f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24313b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24323b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2433cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2434cc535dfbSNicolas Dichtel 		}
2435f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2436f085ff1cSstephen hemminger 		    new_value != old_value) {
2437f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24383b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24393b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2440f085ff1cSstephen hemminger 						    ifindex, cnf);
2441f085ff1cSstephen hemminger 		}
2442974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2443974d7af5SAndy Gospodarek 		    new_value != old_value) {
2444974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24453b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24463b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2447974d7af5SAndy Gospodarek 						    ifindex, cnf);
2448974d7af5SAndy Gospodarek 		}
244931be3085SHerbert Xu 	}
245031be3085SHerbert Xu 
245131be3085SHerbert Xu 	return ret;
245231be3085SHerbert Xu }
245331be3085SHerbert Xu 
2454fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
245532927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24561da177e4SLinus Torvalds {
24571da177e4SLinus Torvalds 	int *valp = ctl->data;
24581da177e4SLinus Torvalds 	int val = *valp;
245988af182eSEric W. Biederman 	loff_t pos = *ppos;
24608292d7f6SYang Yang 	struct net *net = ctl->extra2;
24618292d7f6SYang Yang 	int ret;
24628292d7f6SYang Yang 
24638292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24648292d7f6SYang Yang 		return -EPERM;
24658292d7f6SYang Yang 
24668292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24671da177e4SLinus Torvalds 
24681da177e4SLinus Torvalds 	if (write && *valp != val) {
24690187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
247088af182eSEric W. Biederman 			if (!rtnl_trylock()) {
247188af182eSEric W. Biederman 				/* Restore the original values before restarting */
247288af182eSEric W. Biederman 				*valp = val;
247388af182eSEric W. Biederman 				*ppos = pos;
24749b8adb5eSEric W. Biederman 				return restart_syscall();
247588af182eSEric W. Biederman 			}
24760187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2477c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2478edc9e748SNicolas Dichtel 			} else {
24790187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24800187bdfbSBen Hutchings 				struct in_device *idev =
24810187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2482edc9e748SNicolas Dichtel 				if (*valp)
24830187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24843b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2485edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2486edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2487edc9e748SNicolas Dichtel 							    cnf);
24880187bdfbSBen Hutchings 			}
24890187bdfbSBen Hutchings 			rtnl_unlock();
24904ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2491edc9e748SNicolas Dichtel 		} else
24923b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24933b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2494edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2495edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24960187bdfbSBen Hutchings 	}
24971da177e4SLinus Torvalds 
24981da177e4SLinus Torvalds 	return ret;
24991da177e4SLinus Torvalds }
25001da177e4SLinus Torvalds 
2501fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
250232927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
25031da177e4SLinus Torvalds {
25041da177e4SLinus Torvalds 	int *valp = ctl->data;
25051da177e4SLinus Torvalds 	int val = *valp;
25068d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
250776e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
25081da177e4SLinus Torvalds 
25091da177e4SLinus Torvalds 	if (write && *valp != val)
25104ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
25111da177e4SLinus Torvalds 
25121da177e4SLinus Torvalds 	return ret;
25131da177e4SLinus Torvalds }
25141da177e4SLinus Torvalds 
2515f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
251642f811b8SHerbert Xu 	{ \
251742f811b8SHerbert Xu 		.procname	= name, \
251842f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
251902291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
252042f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
252142f811b8SHerbert Xu 		.mode		= mval, \
252242f811b8SHerbert Xu 		.proc_handler	= proc, \
252331be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
252442f811b8SHerbert Xu 	}
252542f811b8SHerbert Xu 
252642f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2527f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
252842f811b8SHerbert Xu 
252942f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2530f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
253142f811b8SHerbert Xu 
2532f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2533f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
253442f811b8SHerbert Xu 
253542f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2536f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
253742f811b8SHerbert Xu 
25381da177e4SLinus Torvalds static struct devinet_sysctl_table {
25391da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
254002291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
25411da177e4SLinus Torvalds } devinet_sysctl = {
25421da177e4SLinus Torvalds 	.devinet_vars = {
254342f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2544f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
254542f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25465cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
254742f811b8SHerbert Xu 
254842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
254942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
255042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
255142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
255242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
255342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
255442f811b8SHerbert Xu 					"accept_source_route"),
25558153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
255628f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
255742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
255842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
255942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
256042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
256142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
256242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
256342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
256442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
256542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2566eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
2567fcdb44d0SJames Prestwood 		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
2568fcdb44d0SJames Prestwood 					"arp_evict_nocarrier"),
256965324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25705c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25715c6fe01cSWilliam Manley 					"force_igmp_version"),
25722690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25732690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25742690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25752690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25760eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25770eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
257897daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
257997daf331SJohannes Berg 					"drop_gratuitous_arp"),
258042f811b8SHerbert Xu 
258142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
258242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
258342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
258442f811b8SHerbert Xu 					      "promote_secondaries"),
2585d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2586d0daebc3SThomas Graf 					      "route_localnet"),
258712b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
258812b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25891da177e4SLinus Torvalds 	},
25901da177e4SLinus Torvalds };
25911da177e4SLinus Torvalds 
2592ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
259329c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25941da177e4SLinus Torvalds {
25951da177e4SLinus Torvalds 	int i;
25969fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25978607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2598bfada697SPavel Emelyanov 
2599425b9c7fSVasily Averin 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT);
26001da177e4SLinus Torvalds 	if (!t)
26019fa89642SPavel Emelyanov 		goto out;
26029fa89642SPavel Emelyanov 
26031da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
26041da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
260531be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2606c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
26071da177e4SLinus Torvalds 	}
26081da177e4SLinus Torvalds 
26098607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
26101da177e4SLinus Torvalds 
26118607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
26121da177e4SLinus Torvalds 	if (!t->sysctl_header)
26138607ddb8SEric W. Biederman 		goto free;
26141da177e4SLinus Torvalds 
26151da177e4SLinus Torvalds 	p->sysctl = t;
261629c994e3SNicolas Dichtel 
26173b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
26183b022865SDavid Ahern 				    ifindex, p);
2619ea40b324SPavel Emelyanov 	return 0;
26201da177e4SLinus Torvalds 
26211da177e4SLinus Torvalds free:
26221da177e4SLinus Torvalds 	kfree(t);
26239fa89642SPavel Emelyanov out:
26246def4801Sliuguoqiang 	return -ENOMEM;
26251da177e4SLinus Torvalds }
26261da177e4SLinus Torvalds 
2627b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2628b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
262966f27a52SPavel Emelyanov {
263051602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
263166f27a52SPavel Emelyanov 
2632b5c9641dSDavid Ahern 	if (t) {
263351602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2634ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26351da177e4SLinus Torvalds 		kfree(t);
26361da177e4SLinus Torvalds 	}
263751602b2aSPavel Emelyanov 
2638b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2639b5c9641dSDavid Ahern }
2640b5c9641dSDavid Ahern 
264120e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
264251602b2aSPavel Emelyanov {
264320e61da7SWANG Cong 	int err;
264420e61da7SWANG Cong 
264520e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
264620e61da7SWANG Cong 		return -EINVAL;
264720e61da7SWANG Cong 
264820e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
264920e61da7SWANG Cong 	if (err)
265020e61da7SWANG Cong 		return err;
265120e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
265229c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
265320e61da7SWANG Cong 	if (err)
265420e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
265520e61da7SWANG Cong 	return err;
265651602b2aSPavel Emelyanov }
265751602b2aSPavel Emelyanov 
265851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
265951602b2aSPavel Emelyanov {
2660b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2661b5c9641dSDavid Ahern 
2662b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
266351602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26641da177e4SLinus Torvalds }
26651da177e4SLinus Torvalds 
266668dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
266768dd299bSPavel Emelyanov 	{
266868dd299bSPavel Emelyanov 		.procname	= "ip_forward",
266968dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
267002291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
267168dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
267268dd299bSPavel Emelyanov 		.mode		= 0644,
267368dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
267468dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2675c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
267668dd299bSPavel Emelyanov 	},
267768dd299bSPavel Emelyanov 	{ },
267868dd299bSPavel Emelyanov };
26792a75de0cSEric Dumazet #endif
268068dd299bSPavel Emelyanov 
2681752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2682752d14dcSPavel Emelyanov {
2683752d14dcSPavel Emelyanov 	int err;
2684752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26852a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2686856c395cSCong Wang 	struct ctl_table *tbl;
2687752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26882a75de0cSEric Dumazet #endif
2689752d14dcSPavel Emelyanov 
2690752d14dcSPavel Emelyanov 	err = -ENOMEM;
2691856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
269251456b29SIan Morris 	if (!all)
2693752d14dcSPavel Emelyanov 		goto err_alloc_all;
2694752d14dcSPavel Emelyanov 
2695856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
269651456b29SIan Morris 	if (!dflt)
2697752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2698752d14dcSPavel Emelyanov 
26992a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2700856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
270151456b29SIan Morris 	if (!tbl)
2702752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2703752d14dcSPavel Emelyanov 
270402291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2705752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2706752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
27072a75de0cSEric Dumazet #endif
2708856c395cSCong Wang 
27099efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
2710a5612ca1SKuniyuki Iwashima 		switch (net_inherit_devconf()) {
2711a5612ca1SKuniyuki Iwashima 		case 3:
27129efd6a3cSNicolas Dichtel 			/* copy from the current netns */
27139efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
27149efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27159efd6a3cSNicolas Dichtel 			memcpy(dflt,
27169efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
27179efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2718a5612ca1SKuniyuki Iwashima 			break;
2719a5612ca1SKuniyuki Iwashima 		case 0:
2720a5612ca1SKuniyuki Iwashima 		case 1:
2721a5612ca1SKuniyuki Iwashima 			/* copy from init_net */
27229efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
27239efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27249efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
27259efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2726a5612ca1SKuniyuki Iwashima 			break;
2727a5612ca1SKuniyuki Iwashima 		case 2:
2728a5612ca1SKuniyuki Iwashima 			/* use compiled values */
2729a5612ca1SKuniyuki Iwashima 			break;
27309efd6a3cSNicolas Dichtel 		}
2731752d14dcSPavel Emelyanov 	}
2732752d14dcSPavel Emelyanov 
2733752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
273429c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2735752d14dcSPavel Emelyanov 	if (err < 0)
2736752d14dcSPavel Emelyanov 		goto err_reg_all;
2737752d14dcSPavel Emelyanov 
273829c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
273929c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2740752d14dcSPavel Emelyanov 	if (err < 0)
2741752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2742752d14dcSPavel Emelyanov 
2743752d14dcSPavel Emelyanov 	err = -ENOMEM;
2744c899710fSJoel Granados 	forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl,
2745c899710fSJoel Granados 					  ARRAY_SIZE(ctl_forward_entry));
274651456b29SIan Morris 	if (!forw_hdr)
2747752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27482a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2749752d14dcSPavel Emelyanov #endif
2750752d14dcSPavel Emelyanov 
2751752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2752752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2753752d14dcSPavel Emelyanov 	return 0;
2754752d14dcSPavel Emelyanov 
2755752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2756752d14dcSPavel Emelyanov err_reg_ctl:
2757b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2758752d14dcSPavel Emelyanov err_reg_dflt:
2759b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2760752d14dcSPavel Emelyanov err_reg_all:
2761752d14dcSPavel Emelyanov 	kfree(tbl);
2762752d14dcSPavel Emelyanov err_alloc_ctl:
27632a75de0cSEric Dumazet #endif
2764752d14dcSPavel Emelyanov 	kfree(dflt);
2765752d14dcSPavel Emelyanov err_alloc_dflt:
2766752d14dcSPavel Emelyanov 	kfree(all);
2767752d14dcSPavel Emelyanov err_alloc_all:
2768752d14dcSPavel Emelyanov 	return err;
2769752d14dcSPavel Emelyanov }
2770752d14dcSPavel Emelyanov 
2771752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2772752d14dcSPavel Emelyanov {
27732a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2774752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2775752d14dcSPavel Emelyanov 
2776752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2777752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2778b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2779b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2780b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2781b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2782752d14dcSPavel Emelyanov 	kfree(tbl);
27832a75de0cSEric Dumazet #endif
2784752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2785752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2786752d14dcSPavel Emelyanov }
2787752d14dcSPavel Emelyanov 
2788752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2789752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2790752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2791752d14dcSPavel Emelyanov };
2792752d14dcSPavel Emelyanov 
2793207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
27949f0f7272SThomas Graf 	.family		  = AF_INET,
27959f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
27969f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2797cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2798cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
27999f0f7272SThomas Graf };
28009f0f7272SThomas Graf 
28011da177e4SLinus Torvalds void __init devinet_init(void)
28021da177e4SLinus Torvalds {
2803fd23c3b3SDavid S. Miller 	int i;
2804fd23c3b3SDavid S. Miller 
2805fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2806fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2807fd23c3b3SDavid S. Miller 
2808752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
28091da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
281063f3444fSThomas Graf 
2811906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
28125c766d64SJiri Pirko 
28139f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
28149f0f7272SThomas Graf 
2815b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2816b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2817b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
28189e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2819bbcf9105SEric Dumazet 		      inet_netconf_dump_devconf,
282016748707SEric Dumazet 		      RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED);
28211da177e4SLinus Torvalds }
2822