xref: /openbmc/linux/net/ipv4/devinet.c (revision b0e99d03)
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*/,
7842f811b8SHerbert Xu 	},
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8242f811b8SHerbert Xu 	.data = {
8302291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
882690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
9042f811b8SHerbert Xu 	},
911da177e4SLinus Torvalds };
921da177e4SLinus Torvalds 
939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
949355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9542f811b8SHerbert Xu 
96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
975c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
985c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
995c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1005176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1015c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
102ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
103af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
104d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
1055c753978SThomas Graf };
1065c753978SThomas Graf 
107978a46faSChristian Brauner struct inet_fill_args {
108978a46faSChristian Brauner 	u32 portid;
109978a46faSChristian Brauner 	u32 seq;
110978a46faSChristian Brauner 	int event;
111978a46faSChristian Brauner 	unsigned int flags;
112978a46faSChristian Brauner 	int netnsid;
1135fcd266aSDavid Ahern 	int ifindex;
114978a46faSChristian Brauner };
115978a46faSChristian Brauner 
11640384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
11740384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
11840384999SEric Dumazet 
119fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
120fd23c3b3SDavid S. Miller 
1216eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
122fd23c3b3SDavid S. Miller {
12340384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
124fd23c3b3SDavid S. Miller 
12540384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
126fd23c3b3SDavid S. Miller }
127fd23c3b3SDavid S. Miller 
128fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
129fd23c3b3SDavid S. Miller {
13040384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
131fd23c3b3SDavid S. Miller 
13232a4be48SWANG Cong 	ASSERT_RTNL();
133fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
134fd23c3b3SDavid S. Miller }
135fd23c3b3SDavid S. Miller 
136fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
137fd23c3b3SDavid S. Miller {
13832a4be48SWANG Cong 	ASSERT_RTNL();
139fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
140fd23c3b3SDavid S. Miller }
141fd23c3b3SDavid S. Miller 
1429435eb1cSDavid S. Miller /**
1439435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1449435eb1cSDavid S. Miller  * @net: the net namespace
1459435eb1cSDavid S. Miller  * @addr: the source address
1469435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1479435eb1cSDavid S. Miller  *
1489435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1499435eb1cSDavid S. Miller  */
1509435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1519435eb1cSDavid S. Miller {
1529435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1539435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1549435eb1cSDavid S. Miller 
1559435eb1cSDavid S. Miller 	rcu_read_lock();
1566e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1576e617de8SPaolo Abeni 	if (!ifa) {
158406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
159406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
160406b6f97SDavid S. Miller 		struct fib_table *local;
161406b6f97SDavid S. Miller 
162406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
163406b6f97SDavid S. Miller 		 * over loopback subnets work.
164406b6f97SDavid S. Miller 		 */
165406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
166406b6f97SDavid S. Miller 		if (local &&
167406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
168406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
169406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1706e617de8SPaolo Abeni 	} else {
1716e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
172406b6f97SDavid S. Miller 	}
1739435eb1cSDavid S. Miller 	if (result && devref)
1749435eb1cSDavid S. Miller 		dev_hold(result);
1759435eb1cSDavid S. Miller 	rcu_read_unlock();
1769435eb1cSDavid S. Miller 	return result;
1779435eb1cSDavid S. Miller }
1789435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1799435eb1cSDavid S. Miller 
1806e617de8SPaolo Abeni /* called under RCU lock */
1816e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1826e617de8SPaolo Abeni {
1836e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1846e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1856e617de8SPaolo Abeni 
1866e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1876e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1886e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1896e617de8SPaolo Abeni 			return ifa;
1906e617de8SPaolo Abeni 
1916e617de8SPaolo Abeni 	return NULL;
1926e617de8SPaolo Abeni }
1936e617de8SPaolo Abeni 
194d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1951da177e4SLinus Torvalds 
196e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1973ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
1982638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
1992638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
2001da177e4SLinus Torvalds 			 int destroy);
2011da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20451602b2aSPavel Emelyanov #else
20520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20651602b2aSPavel Emelyanov {
20720e61da7SWANG Cong 	return 0;
20851602b2aSPavel Emelyanov }
20940384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
21051602b2aSPavel Emelyanov {
21151602b2aSPavel Emelyanov }
2121da177e4SLinus Torvalds #endif
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds /* Locks all the inet devices. */
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2171da177e4SLinus Torvalds {
2186126891cSVasily Averin 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT);
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2241da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2251da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2261da177e4SLinus Torvalds 	kfree(ifa);
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
22940384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2351da177e4SLinus Torvalds {
2361da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2371da177e4SLinus Torvalds 
238547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
239547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
240e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2411da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
24291df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2431da177e4SLinus Torvalds #endif
2441da177e4SLinus Torvalds 	dev_put(dev);
2451da177e4SLinus Torvalds 	if (!idev->dead)
2469f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2479f9354b9SEric Dumazet 	else
2481da177e4SLinus Torvalds 		kfree(idev);
2491da177e4SLinus Torvalds }
2509f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2511da177e4SLinus Torvalds 
25271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2531da177e4SLinus Torvalds {
2541da177e4SLinus Torvalds 	struct in_device *in_dev;
25520e61da7SWANG Cong 	int err = -ENOMEM;
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds 	ASSERT_RTNL();
2581da177e4SLinus Torvalds 
2590da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2601da177e4SLinus Torvalds 	if (!in_dev)
2611da177e4SLinus Torvalds 		goto out;
262c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2639355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2641da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2651da177e4SLinus Torvalds 	in_dev->dev = dev;
2669f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2679f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2681da177e4SLinus Torvalds 		goto out_kfree;
2690187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2700187bdfbSBen Hutchings 		dev_disable_lro(dev);
2711da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2721da177e4SLinus Torvalds 	dev_hold(dev);
27330c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2747658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2751da177e4SLinus Torvalds 
27620e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
27720e61da7SWANG Cong 	if (err) {
27820e61da7SWANG Cong 		in_dev->dead = 1;
2791b49cd71SYang Yingliang 		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
28020e61da7SWANG Cong 		in_dev_put(in_dev);
28120e61da7SWANG Cong 		in_dev = NULL;
28220e61da7SWANG Cong 		goto out;
28320e61da7SWANG Cong 	}
2841da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2851da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2861da177e4SLinus Torvalds 		ip_mc_up(in_dev);
287483479ecSJarek Poplawski 
28830c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
289cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
290483479ecSJarek Poplawski out:
29120e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2921da177e4SLinus Torvalds out_kfree:
2931da177e4SLinus Torvalds 	kfree(in_dev);
2941da177e4SLinus Torvalds 	in_dev = NULL;
2951da177e4SLinus Torvalds 	goto out;
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
3011da177e4SLinus Torvalds 	in_dev_put(idev);
3021da177e4SLinus Torvalds }
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3051da177e4SLinus Torvalds {
3061da177e4SLinus Torvalds 	struct net_device *dev;
3072638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	ASSERT_RTNL();
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	dev = in_dev->dev;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	in_dev->dead = 1;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3161da177e4SLinus Torvalds 
3172638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3181da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3191da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3201da177e4SLinus Torvalds 	}
3211da177e4SLinus Torvalds 
322a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3231da177e4SLinus Torvalds 
32451602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3251da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3261da177e4SLinus Torvalds 	arp_ifdown(dev);
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
331ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3321da177e4SLinus Torvalds {
333d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
334d519e870SFlorian Westphal 
3351da177e4SLinus Torvalds 	rcu_read_lock();
336d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3371da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3381da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3391da177e4SLinus Torvalds 				rcu_read_unlock();
3401da177e4SLinus Torvalds 				return 1;
3411da177e4SLinus Torvalds 			}
3421da177e4SLinus Torvalds 		}
343d519e870SFlorian Westphal 	}
3441da177e4SLinus Torvalds 	rcu_read_unlock();
3451da177e4SLinus Torvalds 	return 0;
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds 
3482638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3492638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
35015e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3511da177e4SLinus Torvalds {
3528f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3532638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
3542638eb8bSFlorian Westphal 	struct in_ifaddr *last_prim;
3550ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3560ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 	ASSERT_RTNL();
3591da177e4SLinus Torvalds 
3602638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
3612638eb8bSFlorian Westphal 	last_prim = rtnl_dereference(in_dev->ifa_list);
362fbd40ea0SDavid S. Miller 	if (in_dev->dead)
363fbd40ea0SDavid S. Miller 		goto no_promotions;
364fbd40ea0SDavid S. Miller 
3658f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3668f937c60SHarald Welte 	 * unless alias promotion is set
3678f937c60SHarald Welte 	 **/
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3702638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3711da177e4SLinus Torvalds 
3722638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3730ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3740ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3750ff60a45SJamal Hadi Salim 				last_prim = ifa;
3760ff60a45SJamal Hadi Salim 
3771da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3781da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3791da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3801da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3810ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3821da177e4SLinus Torvalds 				continue;
3831da177e4SLinus Torvalds 			}
3841da177e4SLinus Torvalds 
3850ff60a45SJamal Hadi Salim 			if (!do_promote) {
386fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3871da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3881da177e4SLinus Torvalds 
38915e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
390e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
391e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3921da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3938f937c60SHarald Welte 			} else {
3948f937c60SHarald Welte 				promote = ifa;
3958f937c60SHarald Welte 				break;
3968f937c60SHarald Welte 			}
3971da177e4SLinus Torvalds 		}
3981da177e4SLinus Torvalds 	}
3991da177e4SLinus Torvalds 
4002d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4012d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4022d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4032d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4042d230e2bSJulian Anastasov 	 */
4052638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4062d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4072d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4082d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4092d230e2bSJulian Anastasov 	}
4102d230e2bSJulian Anastasov 
411fbd40ea0SDavid S. Miller no_promotions:
4121da177e4SLinus Torvalds 	/* 2. Unlink it */
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
415fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4161da177e4SLinus Torvalds 
4171da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4201da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4211da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4221da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4231da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4241da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4251da177e4SLinus Torvalds 	   So that, this order is correct.
4261da177e4SLinus Torvalds 	 */
42715e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
428e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4290ff60a45SJamal Hadi Salim 
4300ff60a45SJamal Hadi Salim 	if (promote) {
4312638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4320ff60a45SJamal Hadi Salim 
4332638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4340ff60a45SJamal Hadi Salim 		if (prev_prom) {
4352638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4362638eb8bSFlorian Westphal 
4372638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4386a9e9ceaSFlorian Westphal 
4396a9e9ceaSFlorian Westphal 			last_sec = rtnl_dereference(last_prim->ifa_next);
4402638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
4412638eb8bSFlorian Westphal 			rcu_assign_pointer(last_prim->ifa_next, promote);
4420ff60a45SJamal Hadi Salim 		}
4430ff60a45SJamal Hadi Salim 
4440ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
44515e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
446e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
447e041c683SAlan Stern 				NETDEV_UP, promote);
4482638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4492638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4500ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4510ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4520ff60a45SJamal Hadi Salim 					continue;
4530ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4540ff60a45SJamal Hadi Salim 		}
4550ff60a45SJamal Hadi Salim 
4560ff60a45SJamal Hadi Salim 	}
4576363097cSHerbert Xu 	if (destroy)
4581da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4591da177e4SLinus Torvalds }
4601da177e4SLinus Torvalds 
4612638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4622638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
463d6062cbbSThomas Graf 			 int destroy)
464d6062cbbSThomas Graf {
465d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
466d6062cbbSThomas Graf }
467d6062cbbSThomas Graf 
4685c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4695c766d64SJiri Pirko 
4705c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4715c766d64SJiri Pirko 
472d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
473de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4741da177e4SLinus Torvalds {
4752638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4761da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4773ad7d246SKrister Johansen 	struct in_validator_info ivi;
4782638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4793ad7d246SKrister Johansen 	int ret;
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds 	ASSERT_RTNL();
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4841da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4851da177e4SLinus Torvalds 		return 0;
4861da177e4SLinus Torvalds 	}
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4891da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4901da177e4SLinus Torvalds 
4912e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
4922e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
4932e605463SMatteo Croce 
4942638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
4952638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
4962638eb8bSFlorian Westphal 
4972638eb8bSFlorian Westphal 	while (ifa1) {
4981da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4991da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5001da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5011da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5021da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5031da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5041da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5051da177e4SLinus Torvalds 				return -EEXIST;
5061da177e4SLinus Torvalds 			}
5071da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
5081da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5091da177e4SLinus Torvalds 				return -EINVAL;
5101da177e4SLinus Torvalds 			}
5111da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5121da177e4SLinus Torvalds 		}
5132638eb8bSFlorian Westphal 
5142638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5152638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5161da177e4SLinus Torvalds 	}
5171da177e4SLinus Torvalds 
5183ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5193ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5203ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5213ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5223ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5233ad7d246SKrister Johansen 	 * not the flags.
5243ad7d246SKrister Johansen 	 */
5253ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5263ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
527de95e047SDavid Ahern 	ivi.extack = extack;
5283ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5293ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5303ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5313ad7d246SKrister Johansen 	if (ret) {
5323ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5333ad7d246SKrister Johansen 		return ret;
5343ad7d246SKrister Johansen 	}
5353ad7d246SKrister Johansen 
5361da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
53763862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
5381da177e4SLinus Torvalds 		ifap = last_primary;
5391da177e4SLinus Torvalds 	}
5401da177e4SLinus Torvalds 
5412638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5422638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5431da177e4SLinus Torvalds 
544fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
545fd23c3b3SDavid S. Miller 
5465c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
547906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5485c766d64SJiri Pirko 
5491da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5501da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5511da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
55215e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
553e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	return 0;
5561da177e4SLinus Torvalds }
5571da177e4SLinus Torvalds 
558d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
559d6062cbbSThomas Graf {
560de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
561d6062cbbSThomas Graf }
562d6062cbbSThomas Graf 
5631da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5641da177e4SLinus Torvalds {
565e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 	ASSERT_RTNL();
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 	if (!in_dev) {
5701da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5711da177e4SLinus Torvalds 		return -ENOBUFS;
5721da177e4SLinus Torvalds 	}
57371e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5741d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5751da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
576547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5771da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5781da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5791da177e4SLinus Torvalds 	}
580f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5811da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5821da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds 
5858723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5868723e1b4SEric Dumazet  * We dont take a reference on found in_device
5878723e1b4SEric Dumazet  */
5887fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5891da177e4SLinus Torvalds {
5901da177e4SLinus Torvalds 	struct net_device *dev;
5911da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
592c148fc2eSEric Dumazet 
593c148fc2eSEric Dumazet 	rcu_read_lock();
594c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5951da177e4SLinus Torvalds 	if (dev)
5968723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
597c148fc2eSEric Dumazet 	rcu_read_unlock();
5981da177e4SLinus Torvalds 	return in_dev;
5991da177e4SLinus Torvalds }
6009f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6031da177e4SLinus Torvalds 
60460cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
60560cad5daSAl Viro 				    __be32 mask)
6061da177e4SLinus Torvalds {
607d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
608d519e870SFlorian Westphal 
6091da177e4SLinus Torvalds 	ASSERT_RTNL();
6101da177e4SLinus Torvalds 
611d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6121da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6131da177e4SLinus Torvalds 			return ifa;
614d519e870SFlorian Westphal 	}
6151da177e4SLinus Torvalds 	return NULL;
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds 
618690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
619690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
62093a714d6SMadhu Challa {
621690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
62293a714d6SMadhu Challa 	struct ip_mreqn mreq = {
62393a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
62493a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
62593a714d6SMadhu Challa 	};
626690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
62793a714d6SMadhu Challa 	int ret;
62893a714d6SMadhu Challa 
62993a714d6SMadhu Challa 	ASSERT_RTNL();
63093a714d6SMadhu Challa 
63193a714d6SMadhu Challa 	lock_sock(sk);
63293a714d6SMadhu Challa 	if (join)
63354ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
63493a714d6SMadhu Challa 	else
63554ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
63693a714d6SMadhu Challa 	release_sock(sk);
63793a714d6SMadhu Challa 
63893a714d6SMadhu Challa 	return ret;
639690cc863STaras Chornyi #else
640690cc863STaras Chornyi 	return -EOPNOTSUPP;
641690cc863STaras Chornyi #endif
64293a714d6SMadhu Challa }
64393a714d6SMadhu Challa 
644c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
645c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6461da177e4SLinus Torvalds {
6473b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6482638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
649dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6501da177e4SLinus Torvalds 	struct in_device *in_dev;
651dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6522638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
65330e2379eSMenglong Dong 	int err;
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	ASSERT_RTNL();
6561da177e4SLinus Torvalds 
6578cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6588cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
659dfdd5fd4SThomas Graf 	if (err < 0)
660dfdd5fd4SThomas Graf 		goto errout;
661dfdd5fd4SThomas Graf 
662dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6637fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
66451456b29SIan Morris 	if (!in_dev) {
665dfdd5fd4SThomas Graf 		err = -ENODEV;
666dfdd5fd4SThomas Graf 		goto errout;
667dfdd5fd4SThomas Graf 	}
668dfdd5fd4SThomas Graf 
6692638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6701da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
671dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
67267b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6731da177e4SLinus Torvalds 			continue;
674dfdd5fd4SThomas Graf 
675dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
676dfdd5fd4SThomas Graf 			continue;
677dfdd5fd4SThomas Graf 
678dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
679dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
68067b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
681dfdd5fd4SThomas Graf 			continue;
682dfdd5fd4SThomas Graf 
68393a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
684690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
68515e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6861da177e4SLinus Torvalds 		return 0;
6871da177e4SLinus Torvalds 	}
688dfdd5fd4SThomas Graf 
689dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
690dfdd5fd4SThomas Graf errout:
691dfdd5fd4SThomas Graf 	return err;
6921da177e4SLinus Torvalds }
6931da177e4SLinus Torvalds 
6945c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6955c766d64SJiri Pirko 
6965c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6975c766d64SJiri Pirko {
6985c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6995c766d64SJiri Pirko 	struct in_ifaddr *ifa;
700c988d1e8SJiri Pirko 	struct hlist_node *n;
7015c766d64SJiri Pirko 	int i;
7025c766d64SJiri Pirko 
7035c766d64SJiri Pirko 	now = jiffies;
7045c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
7055c766d64SJiri Pirko 
7065c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
707c988d1e8SJiri Pirko 		bool change_needed = false;
708c988d1e8SJiri Pirko 
709c988d1e8SJiri Pirko 		rcu_read_lock();
710b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
7115c766d64SJiri Pirko 			unsigned long age;
7125c766d64SJiri Pirko 
7135c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
7145c766d64SJiri Pirko 				continue;
7155c766d64SJiri Pirko 
7165c766d64SJiri Pirko 			/* We try to batch several events at once. */
7175c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
7185c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7195c766d64SJiri Pirko 
7205c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
7215c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
722c988d1e8SJiri Pirko 				change_needed = true;
723c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
724c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
725c988d1e8SJiri Pirko 				continue;
726c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
727c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
728c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
729c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
730c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
731c988d1e8SJiri Pirko 
732c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
733c988d1e8SJiri Pirko 					change_needed = true;
734c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
735c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
736c988d1e8SJiri Pirko 					       next)) {
737c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
738c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
739c988d1e8SJiri Pirko 			}
740c988d1e8SJiri Pirko 		}
741c988d1e8SJiri Pirko 		rcu_read_unlock();
742c988d1e8SJiri Pirko 		if (!change_needed)
743c988d1e8SJiri Pirko 			continue;
744c988d1e8SJiri Pirko 		rtnl_lock();
745c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
746c988d1e8SJiri Pirko 			unsigned long age;
747c988d1e8SJiri Pirko 
748c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
749c988d1e8SJiri Pirko 				continue;
750c988d1e8SJiri Pirko 
751c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
752c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
753c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
754c988d1e8SJiri Pirko 
755c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
756c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7572638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7582638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7595c766d64SJiri Pirko 
7602638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7612638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7622638eb8bSFlorian Westphal 				while (tmp) {
76340008e92SFlorian Westphal 					if (tmp == ifa) {
7645c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7655c766d64SJiri Pirko 							     ifap, 1);
766c988d1e8SJiri Pirko 						break;
7675c766d64SJiri Pirko 					}
7682638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7692638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
770c988d1e8SJiri Pirko 				}
771c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
772c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
773c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
774c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7755c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7765c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7775c766d64SJiri Pirko 			}
7785c766d64SJiri Pirko 		}
779c988d1e8SJiri Pirko 		rtnl_unlock();
7805c766d64SJiri Pirko 	}
7815c766d64SJiri Pirko 
7825c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7835c766d64SJiri Pirko 	next_sched = next;
7845c766d64SJiri Pirko 
7855c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7865c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7875c766d64SJiri Pirko 		next_sched = next_sec;
7885c766d64SJiri Pirko 
7895c766d64SJiri Pirko 	now = jiffies;
7905c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7915c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7925c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7935c766d64SJiri Pirko 
794906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
795906e073fSviresh kumar 			next_sched - now);
7965c766d64SJiri Pirko }
7975c766d64SJiri Pirko 
7985c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7995c766d64SJiri Pirko 			     __u32 prefered_lft)
8005c766d64SJiri Pirko {
8015c766d64SJiri Pirko 	unsigned long timeout;
8025c766d64SJiri Pirko 
8035c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
8045c766d64SJiri Pirko 
8055c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
8065c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
8075c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
8085c766d64SJiri Pirko 	else
8095c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
8105c766d64SJiri Pirko 
8115c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8125c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8135c766d64SJiri Pirko 		if (timeout == 0)
8145c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
8155c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
8165c766d64SJiri Pirko 	}
8175c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
8185c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
8195c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
8205c766d64SJiri Pirko }
8215c766d64SJiri Pirko 
8225c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
823dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
824dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8251da177e4SLinus Torvalds {
8265c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8275c753978SThomas Graf 	struct in_ifaddr *ifa;
8285c753978SThomas Graf 	struct ifaddrmsg *ifm;
8291da177e4SLinus Torvalds 	struct net_device *dev;
8301da177e4SLinus Torvalds 	struct in_device *in_dev;
8317b218574SDenis V. Lunev 	int err;
8321da177e4SLinus Torvalds 
8338cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8348cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8355c753978SThomas Graf 	if (err < 0)
8365c753978SThomas Graf 		goto errout;
8371da177e4SLinus Torvalds 
8385c753978SThomas Graf 	ifm = nlmsg_data(nlh);
839c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
84051456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
8415c753978SThomas Graf 		goto errout;
8421da177e4SLinus Torvalds 
8434b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8445c753978SThomas Graf 	err = -ENODEV;
84551456b29SIan Morris 	if (!dev)
8465c753978SThomas Graf 		goto errout;
8471da177e4SLinus Torvalds 
8485c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8495c753978SThomas Graf 	err = -ENOBUFS;
85051456b29SIan Morris 	if (!in_dev)
8515c753978SThomas Graf 		goto errout;
85271e27da9SHerbert Xu 
8535c753978SThomas Graf 	ifa = inet_alloc_ifa();
85451456b29SIan Morris 	if (!ifa)
8555c753978SThomas Graf 		/*
8565c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8575c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8585c753978SThomas Graf 		 */
8595c753978SThomas Graf 		goto errout;
8605c753978SThomas Graf 
861a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8621d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8635c753978SThomas Graf 	in_dev_hold(in_dev);
8645c753978SThomas Graf 
86551456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8665c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8675c753978SThomas Graf 
868fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8691da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8701da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
871ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
872ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8731da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8741da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8755c753978SThomas Graf 
87667b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
87767b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8785c753978SThomas Graf 
8795c753978SThomas Graf 	if (tb[IFA_BROADCAST])
88067b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8815c753978SThomas Graf 
8825c753978SThomas Graf 	if (tb[IFA_LABEL])
883872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8841da177e4SLinus Torvalds 	else
8851da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8861da177e4SLinus Torvalds 
887af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
888af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
889af4d768aSDavid Ahern 
8905c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8915c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8925c766d64SJiri Pirko 
8935c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8945c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8955c766d64SJiri Pirko 			err = -EINVAL;
896446266b0SDaniel Borkmann 			goto errout_free;
8975c766d64SJiri Pirko 		}
8985c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8995c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9005c766d64SJiri Pirko 	}
9015c766d64SJiri Pirko 
9025c753978SThomas Graf 	return ifa;
9035c753978SThomas Graf 
904446266b0SDaniel Borkmann errout_free:
905446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9065c753978SThomas Graf errout:
9075c753978SThomas Graf 	return ERR_PTR(err);
9085c753978SThomas Graf }
9095c753978SThomas Graf 
9105c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9115c766d64SJiri Pirko {
9125c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
913ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9145c766d64SJiri Pirko 
9155c766d64SJiri Pirko 	if (!ifa->ifa_local)
9165c766d64SJiri Pirko 		return NULL;
9175c766d64SJiri Pirko 
918ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9195c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9205c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9215c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9225c766d64SJiri Pirko 			return ifa1;
9235c766d64SJiri Pirko 	}
9245c766d64SJiri Pirko 	return NULL;
9255c766d64SJiri Pirko }
9265c766d64SJiri Pirko 
927c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
928c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9295c753978SThomas Graf {
9303b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9315c753978SThomas Graf 	struct in_ifaddr *ifa;
9325c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9335c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9345c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9355c753978SThomas Graf 
9365c753978SThomas Graf 	ASSERT_RTNL();
9375c753978SThomas Graf 
938dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9395c753978SThomas Graf 	if (IS_ERR(ifa))
9405c753978SThomas Graf 		return PTR_ERR(ifa);
9415c753978SThomas Graf 
9425c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9435c766d64SJiri Pirko 	if (!ifa_existing) {
9445c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
945614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9465c766d64SJiri Pirko 		 */
9475c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
94893a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
949690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
95093a714d6SMadhu Challa 
95193a714d6SMadhu Challa 			if (ret < 0) {
95293a714d6SMadhu Challa 				inet_free_ifa(ifa);
95393a714d6SMadhu Challa 				return ret;
95493a714d6SMadhu Challa 			}
95593a714d6SMadhu Challa 		}
956de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
957de95e047SDavid Ahern 					 extack);
9585c766d64SJiri Pirko 	} else {
959af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
960af4d768aSDavid Ahern 
9615c766d64SJiri Pirko 		inet_free_ifa(ifa);
9625c766d64SJiri Pirko 
9635c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
9645c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
9655c766d64SJiri Pirko 			return -EEXIST;
96634e2ed34SJiri Pirko 		ifa = ifa_existing;
967af4d768aSDavid Ahern 
968af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
969af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
970af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
971af4d768aSDavid Ahern 		}
972af4d768aSDavid Ahern 
97334e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
97405a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
975906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
976906e073fSviresh kumar 				&check_lifetime_work, 0);
97734e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
9785c766d64SJiri Pirko 	}
9795c766d64SJiri Pirko 	return 0;
9801da177e4SLinus Torvalds }
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds /*
9831da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
9841da177e4SLinus Torvalds  */
9851da177e4SLinus Torvalds 
98640384999SEric Dumazet static int inet_abc_len(__be32 addr)
9871da177e4SLinus Torvalds {
9881da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
9891da177e4SLinus Torvalds 
99065cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
9911da177e4SLinus Torvalds 		rc = 0;
9921da177e4SLinus Torvalds 	else {
993714e85beSAl Viro 		__u32 haddr = ntohl(addr);
994714e85beSAl Viro 		if (IN_CLASSA(haddr))
9951da177e4SLinus Torvalds 			rc = 8;
996714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9971da177e4SLinus Torvalds 			rc = 16;
998714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9991da177e4SLinus Torvalds 			rc = 24;
100065cab850SDave Taht 		else if (IN_CLASSE(haddr))
100165cab850SDave Taht 			rc = 32;
10021da177e4SLinus Torvalds 	}
10031da177e4SLinus Torvalds 
10041da177e4SLinus Torvalds 	return rc;
10051da177e4SLinus Torvalds }
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds 
100803aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10091da177e4SLinus Torvalds {
10101da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
101103aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10122638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10131da177e4SLinus Torvalds 	struct in_device *in_dev;
10141da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10151da177e4SLinus Torvalds 	struct net_device *dev;
10161da177e4SLinus Torvalds 	char *colon;
10171da177e4SLinus Torvalds 	int ret = -EFAULT;
10181da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10191da177e4SLinus Torvalds 
102003aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds 	/* save original address for comparison */
10231da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10241da177e4SLinus Torvalds 
102503aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10261da177e4SLinus Torvalds 	if (colon)
10271da177e4SLinus Torvalds 		*colon = 0;
10281da177e4SLinus Torvalds 
102903aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds 	switch (cmd) {
10321da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10331da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10341da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10351da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10361da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10371da177e4SLinus Torvalds 		   so that we do not impose a lock.
10381da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10391da177e4SLinus Torvalds 		 */
10401da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10411da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10421da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10431da177e4SLinus Torvalds 		break;
10441da177e4SLinus Torvalds 
10451da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1046bf5b30b8SZhao Hongjiang 		ret = -EPERM;
104752e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10481da177e4SLinus Torvalds 			goto out;
10491da177e4SLinus Torvalds 		break;
10501da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10511da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10521da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10531da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1054bf5b30b8SZhao Hongjiang 		ret = -EPERM;
105552e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10561da177e4SLinus Torvalds 			goto out;
10571da177e4SLinus Torvalds 		ret = -EINVAL;
10581da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10591da177e4SLinus Torvalds 			goto out;
10601da177e4SLinus Torvalds 		break;
10611da177e4SLinus Torvalds 	default:
10621da177e4SLinus Torvalds 		ret = -EINVAL;
10631da177e4SLinus Torvalds 		goto out;
10641da177e4SLinus Torvalds 	}
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	rtnl_lock();
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds 	ret = -ENODEV;
106903aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10709f9354b9SEric Dumazet 	if (!dev)
10711da177e4SLinus Torvalds 		goto done;
10721da177e4SLinus Torvalds 
10731da177e4SLinus Torvalds 	if (colon)
10741da177e4SLinus Torvalds 		*colon = ':';
10751da177e4SLinus Torvalds 
10769f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
10779f9354b9SEric Dumazet 	if (in_dev) {
10781da177e4SLinus Torvalds 		if (tryaddrmatch) {
10791da177e4SLinus Torvalds 			/* Matthias Andree */
10801da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
10811da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
10821da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
10831da177e4SLinus Torvalds 			   This is checked above. */
10842638eb8bSFlorian Westphal 
10852638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
10862638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
10871da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
108803aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
10891da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10906c91afe1SDavid S. Miller 							ifa->ifa_local) {
10911da177e4SLinus Torvalds 					break; /* found */
10921da177e4SLinus Torvalds 				}
10931da177e4SLinus Torvalds 			}
10941da177e4SLinus Torvalds 		}
10951da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10961da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10971da177e4SLinus Torvalds 		   comparing just the label */
10981da177e4SLinus Torvalds 		if (!ifa) {
10992638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11002638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11011da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
110203aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11031da177e4SLinus Torvalds 					break;
11041da177e4SLinus Torvalds 		}
11051da177e4SLinus Torvalds 	}
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11081da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11091da177e4SLinus Torvalds 		goto done;
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds 	switch (cmd) {
11121da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
111330e948a3STonghao Zhang 		ret = 0;
11141da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
111503aef17bSAl Viro 		break;
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
111830e948a3STonghao Zhang 		ret = 0;
11191da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
112003aef17bSAl Viro 		break;
11211da177e4SLinus Torvalds 
11221da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
112330e948a3STonghao Zhang 		ret = 0;
11241da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
112503aef17bSAl Viro 		break;
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
112830e948a3STonghao Zhang 		ret = 0;
11291da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
113003aef17bSAl Viro 		break;
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11331da177e4SLinus Torvalds 		if (colon) {
11341da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11351da177e4SLinus Torvalds 			if (!ifa)
11361da177e4SLinus Torvalds 				break;
11371da177e4SLinus Torvalds 			ret = 0;
113803aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11391da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11401da177e4SLinus Torvalds 			break;
11411da177e4SLinus Torvalds 		}
1142567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11431da177e4SLinus Torvalds 		break;
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11461da177e4SLinus Torvalds 		ret = -EINVAL;
11471da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11481da177e4SLinus Torvalds 			break;
11491da177e4SLinus Torvalds 
11501da177e4SLinus Torvalds 		if (!ifa) {
11511da177e4SLinus Torvalds 			ret = -ENOBUFS;
11529f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11539f9354b9SEric Dumazet 			if (!ifa)
11541da177e4SLinus Torvalds 				break;
1155c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11561da177e4SLinus Torvalds 			if (colon)
115703aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11581da177e4SLinus Torvalds 			else
11591da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11601da177e4SLinus Torvalds 		} else {
11611da177e4SLinus Torvalds 			ret = 0;
11621da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11631da177e4SLinus Torvalds 				break;
11641da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11651da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1166148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11671da177e4SLinus Torvalds 		}
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11721da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11731da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11741da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11751da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
11761da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
11771da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
11781da177e4SLinus Torvalds 		} else {
11791da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
11801da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
11811da177e4SLinus Torvalds 		}
11825c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
11831da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
11841da177e4SLinus Torvalds 		break;
11851da177e4SLinus Torvalds 
11861da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
11871da177e4SLinus Torvalds 		ret = 0;
11881da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
11891da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11901da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
11911da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11921da177e4SLinus Torvalds 		}
11931da177e4SLinus Torvalds 		break;
11941da177e4SLinus Torvalds 
11951da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11961da177e4SLinus Torvalds 		ret = 0;
11971da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11981da177e4SLinus Torvalds 			break;
11991da177e4SLinus Torvalds 		ret = -EINVAL;
12001da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12011da177e4SLinus Torvalds 			break;
12021da177e4SLinus Torvalds 		ret = 0;
12031da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12041da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12051da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12061da177e4SLinus Torvalds 		break;
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds 		/*
12111da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12121da177e4SLinus Torvalds 		 */
12131da177e4SLinus Torvalds 		ret = -EINVAL;
12141da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12151da177e4SLinus Torvalds 			break;
12161da177e4SLinus Torvalds 		ret = 0;
12171da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1218a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12191da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12201da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12211da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12221da177e4SLinus Torvalds 
12231da177e4SLinus Torvalds 			/* See if current broadcast address matches
12241da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12251da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12261da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12271da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12281da177e4SLinus Torvalds 			 */
12291da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12301da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12311da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1232dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12331da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12341da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12351da177e4SLinus Torvalds 			}
12361da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12371da177e4SLinus Torvalds 		}
12381da177e4SLinus Torvalds 		break;
12391da177e4SLinus Torvalds 	}
12401da177e4SLinus Torvalds done:
12411da177e4SLinus Torvalds 	rtnl_unlock();
12421da177e4SLinus Torvalds out:
12431da177e4SLinus Torvalds 	return ret;
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds 
1246*b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12471da177e4SLinus Torvalds {
1248e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1249ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12501da177e4SLinus Torvalds 	struct ifreq ifr;
12511da177e4SLinus Torvalds 	int done = 0;
12521da177e4SLinus Torvalds 
125336fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
125436fd633eSAl Viro 		goto out;
125536fd633eSAl Viro 
12569f9354b9SEric Dumazet 	if (!in_dev)
12571da177e4SLinus Torvalds 		goto out;
12581da177e4SLinus Torvalds 
1259ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12601da177e4SLinus Torvalds 		if (!buf) {
126136fd633eSAl Viro 			done += size;
12621da177e4SLinus Torvalds 			continue;
12631da177e4SLinus Torvalds 		}
126436fd633eSAl Viro 		if (len < size)
12651da177e4SLinus Torvalds 			break;
12661da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12671da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12701da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12711da177e4SLinus Torvalds 								ifa->ifa_local;
12721da177e4SLinus Torvalds 
127336fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12741da177e4SLinus Torvalds 			done = -EFAULT;
12751da177e4SLinus Torvalds 			break;
12761da177e4SLinus Torvalds 		}
127736fd633eSAl Viro 		len  -= size;
127836fd633eSAl Viro 		done += size;
12791da177e4SLinus Torvalds 	}
12801da177e4SLinus Torvalds out:
12811da177e4SLinus Torvalds 	return done;
12821da177e4SLinus Torvalds }
12831da177e4SLinus Torvalds 
12848b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
12858b57fd1eSGao Feng 				 int scope)
12868b57fd1eSGao Feng {
1287d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1288d519e870SFlorian Westphal 
1289d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1290d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1291d519e870SFlorian Westphal 			continue;
12928b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
12938b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
12948b57fd1eSGao Feng 			return ifa->ifa_local;
1295d519e870SFlorian Westphal 	}
12968b57fd1eSGao Feng 
12978b57fd1eSGao Feng 	return 0;
12988b57fd1eSGao Feng }
12998b57fd1eSGao Feng 
1300a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13011da177e4SLinus Torvalds {
1302d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1303a61ced5dSAl Viro 	__be32 addr = 0;
1304d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13051da177e4SLinus Torvalds 	struct in_device *in_dev;
1306c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13073f2fb9a8SDavid Ahern 	int master_idx;
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds 	rcu_read_lock();
1310e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13111da177e4SLinus Torvalds 	if (!in_dev)
13121da177e4SLinus Torvalds 		goto no_in_dev;
13131da177e4SLinus Torvalds 
1314d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1315d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1316d8c444d5SShijie Luo 
1317d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1318d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1319d519e870SFlorian Westphal 			continue;
1320d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13211da177e4SLinus Torvalds 			continue;
13221da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13231da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13241da177e4SLinus Torvalds 			break;
13251da177e4SLinus Torvalds 		}
13261da177e4SLinus Torvalds 		if (!addr)
13271da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1328d519e870SFlorian Westphal 	}
13291da177e4SLinus Torvalds 
13301da177e4SLinus Torvalds 	if (addr)
1331c6d14c84SEric Dumazet 		goto out_unlock;
13329f9354b9SEric Dumazet no_in_dev:
13333f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13341da177e4SLinus Torvalds 
133517b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
133617b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
133717b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
133817b693cdSDavid Lamparter 	 * equality check in the loop below.
133917b693cdSDavid Lamparter 	 */
134017b693cdSDavid Lamparter 	if (master_idx &&
134117b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
134217b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13438b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13448b57fd1eSGao Feng 		if (addr)
134517b693cdSDavid Lamparter 			goto out_unlock;
134617b693cdSDavid Lamparter 	}
134717b693cdSDavid Lamparter 
13481da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1349ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13501da177e4SLinus Torvalds 	   in dev_base list.
13511da177e4SLinus Torvalds 	 */
1352c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13533f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13543f2fb9a8SDavid Ahern 			continue;
13553f2fb9a8SDavid Ahern 
13569f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13579f9354b9SEric Dumazet 		if (!in_dev)
13581da177e4SLinus Torvalds 			continue;
13591da177e4SLinus Torvalds 
13608b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13618b57fd1eSGao Feng 		if (addr)
1362c6d14c84SEric Dumazet 			goto out_unlock;
13631da177e4SLinus Torvalds 	}
1364c6d14c84SEric Dumazet out_unlock:
13651da177e4SLinus Torvalds 	rcu_read_unlock();
13661da177e4SLinus Torvalds 	return addr;
13671da177e4SLinus Torvalds }
13689f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13691da177e4SLinus Torvalds 
137060cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
137160cad5daSAl Viro 			      __be32 local, int scope)
13721da177e4SLinus Torvalds {
1373650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1374ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1375a144ea4bSAl Viro 	__be32 addr = 0;
1376ef11db33SFlorian Westphal 	int same = 0;
13771da177e4SLinus Torvalds 
1378650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1379650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1380650638a7SShijie Luo 
1381ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1382650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1383650638a7SShijie Luo 
13841da177e4SLinus Torvalds 		if (!addr &&
13851da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1386650638a7SShijie Luo 		    min_scope <= scope) {
13871da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13881da177e4SLinus Torvalds 			if (same)
13891da177e4SLinus Torvalds 				break;
13901da177e4SLinus Torvalds 		}
13911da177e4SLinus Torvalds 		if (!same) {
13921da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
13931da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
13941da177e4SLinus Torvalds 			if (same && addr) {
13951da177e4SLinus Torvalds 				if (local || !dst)
13961da177e4SLinus Torvalds 					break;
13971da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
13981da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
13991da177e4SLinus Torvalds 					break;
14001da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1401650638a7SShijie Luo 				if (min_scope <= scope) {
14021da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14031da177e4SLinus Torvalds 					break;
14041da177e4SLinus Torvalds 				}
14051da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14061da177e4SLinus Torvalds 				same = 0;
14071da177e4SLinus Torvalds 			}
14081da177e4SLinus Torvalds 		}
1409ef11db33SFlorian Westphal 	}
14101da177e4SLinus Torvalds 
14111da177e4SLinus Torvalds 	return same ? addr : 0;
14121da177e4SLinus Torvalds }
14131da177e4SLinus Torvalds 
14141da177e4SLinus Torvalds /*
14151da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1416b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1417b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14181da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14191da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14201da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14211da177e4SLinus Torvalds  */
1422b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14239bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14241da177e4SLinus Torvalds {
142560cad5daSAl Viro 	__be32 addr = 0;
14269bd85e32SDenis V. Lunev 	struct net_device *dev;
14271da177e4SLinus Torvalds 
142800db4124SIan Morris 	if (in_dev)
14299bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds 	rcu_read_lock();
1432c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14339f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14349f9354b9SEric Dumazet 		if (in_dev) {
14351da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14361da177e4SLinus Torvalds 			if (addr)
14371da177e4SLinus Torvalds 				break;
14381da177e4SLinus Torvalds 		}
14391da177e4SLinus Torvalds 	}
14401da177e4SLinus Torvalds 	rcu_read_unlock();
14411da177e4SLinus Torvalds 
14421da177e4SLinus Torvalds 	return addr;
14431da177e4SLinus Torvalds }
1444eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14451da177e4SLinus Torvalds 
14461da177e4SLinus Torvalds /*
14471da177e4SLinus Torvalds  *	Device notifier
14481da177e4SLinus Torvalds  */
14491da177e4SLinus Torvalds 
14501da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14511da177e4SLinus Torvalds {
1452e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14531da177e4SLinus Torvalds }
14549f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14551da177e4SLinus Torvalds 
14561da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14571da177e4SLinus Torvalds {
1458e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14591da177e4SLinus Torvalds }
14609f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14611da177e4SLinus Torvalds 
14623ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14633ad7d246SKrister Johansen {
14643ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14653ad7d246SKrister Johansen }
14663ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14673ad7d246SKrister Johansen 
14683ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14693ad7d246SKrister Johansen {
14703ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14713ad7d246SKrister Johansen 	    nb);
14723ad7d246SKrister Johansen }
14733ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14743ad7d246SKrister Johansen 
14759f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
14769f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
14771da177e4SLinus Torvalds */
14781da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
14791da177e4SLinus Torvalds {
14801da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
14811da177e4SLinus Torvalds 	int named = 0;
14821da177e4SLinus Torvalds 
1483ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
14841da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
14851da177e4SLinus Torvalds 
14861da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
14871da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14881da177e4SLinus Torvalds 		if (named++ == 0)
1489573bf470SThomas Graf 			goto skip;
149044344b2aSMark McLoughlin 		dot = strchr(old, ':');
149151456b29SIan Morris 		if (!dot) {
14921da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
14931da177e4SLinus Torvalds 			dot = old;
14941da177e4SLinus Torvalds 		}
14959f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
14961da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
14979f9354b9SEric Dumazet 		else
14981da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1499573bf470SThomas Graf skip:
1500573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15011da177e4SLinus Torvalds 	}
15021da177e4SLinus Torvalds }
15031da177e4SLinus Torvalds 
1504d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1505d11327adSIan Campbell 					struct in_device *in_dev)
1506d11327adSIan Campbell 
1507d11327adSIan Campbell {
1508ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1509d11327adSIan Campbell 
1510ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1511d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15126c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15136c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1514d11327adSIan Campbell 			 dev->dev_addr, NULL);
1515d11327adSIan Campbell 	}
1516b76d0789SZoltan Kiss }
1517d11327adSIan Campbell 
15181da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15191da177e4SLinus Torvalds 
15201da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15211da177e4SLinus Torvalds 			 void *ptr)
15221da177e4SLinus Torvalds {
1523351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1524748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15251da177e4SLinus Torvalds 
15261da177e4SLinus Torvalds 	ASSERT_RTNL();
15271da177e4SLinus Torvalds 
15281da177e4SLinus Torvalds 	if (!in_dev) {
15298030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15301da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
153120e61da7SWANG Cong 			if (IS_ERR(in_dev))
153220e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15330cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
153442f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
153542f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15361da177e4SLinus Torvalds 			}
153706770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
153806770843SBreno Leitao 			/* Re-enabling IP */
153906770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
154006770843SBreno Leitao 				in_dev = inetdev_init(dev);
15418030f544SHerbert Xu 		}
15421da177e4SLinus Torvalds 		goto out;
15431da177e4SLinus Torvalds 	}
15441da177e4SLinus Torvalds 
15451da177e4SLinus Torvalds 	switch (event) {
15461da177e4SLinus Torvalds 	case NETDEV_REGISTER:
154791df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1548a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15491da177e4SLinus Torvalds 		break;
15501da177e4SLinus Torvalds 	case NETDEV_UP:
155106770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15521da177e4SLinus Torvalds 			break;
15530cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15549f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15559f9354b9SEric Dumazet 
15569f9354b9SEric Dumazet 			if (ifa) {
1557fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15581da177e4SLinus Torvalds 				ifa->ifa_local =
15591da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15601da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15611da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15621da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15631da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15641da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15651da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15665c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15675c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1568dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1569dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15701da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15711da177e4SLinus Torvalds 			}
15721da177e4SLinus Torvalds 		}
15731da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1574a8eceea8SJoe Perches 		fallthrough;
1575eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1576d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1577d11327adSIan Campbell 			break;
1578a8eceea8SJoe Perches 		fallthrough;
1579d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1580a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1581d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
15821da177e4SLinus Torvalds 		break;
15831da177e4SLinus Torvalds 	case NETDEV_DOWN:
15841da177e4SLinus Torvalds 		ip_mc_down(in_dev);
15851da177e4SLinus Torvalds 		break;
158693d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
158775c78500SMoni Shoua 		ip_mc_unmap(in_dev);
158875c78500SMoni Shoua 		break;
158993d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
159075c78500SMoni Shoua 		ip_mc_remap(in_dev);
159175c78500SMoni Shoua 		break;
15921da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
159306770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
15941da177e4SLinus Torvalds 			break;
159506770843SBreno Leitao 		/* disable IP when MTU is not enough */
1596a8eceea8SJoe Perches 		fallthrough;
15971da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
15981da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
15991da177e4SLinus Torvalds 		break;
16001da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16011da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16021da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16031da177e4SLinus Torvalds 		 */
16041da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16051da177e4SLinus Torvalds 
160651602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
160766f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16081da177e4SLinus Torvalds 		break;
16091da177e4SLinus Torvalds 	}
16101da177e4SLinus Torvalds out:
16111da177e4SLinus Torvalds 	return NOTIFY_DONE;
16121da177e4SLinus Torvalds }
16131da177e4SLinus Torvalds 
16141da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16151da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16161da177e4SLinus Torvalds };
16171da177e4SLinus Torvalds 
161840384999SEric Dumazet static size_t inet_nlmsg_size(void)
1619339bf98fSThomas Graf {
1620339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1621339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1622339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1623339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1624ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
162563b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
1626af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
162763b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1628339bf98fSThomas Graf }
1629339bf98fSThomas Graf 
16305c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16315c766d64SJiri Pirko {
16325c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16335c766d64SJiri Pirko }
16345c766d64SJiri Pirko 
16355c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16365c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16375c766d64SJiri Pirko {
16385c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16395c766d64SJiri Pirko 
16405c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16415c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16425c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16435c766d64SJiri Pirko 	ci.ifa_valid = valid;
16445c766d64SJiri Pirko 
16455c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16465c766d64SJiri Pirko }
16475c766d64SJiri Pirko 
16481da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1649978a46faSChristian Brauner 			    struct inet_fill_args *args)
16501da177e4SLinus Torvalds {
16511da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16521da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16535c766d64SJiri Pirko 	u32 preferred, valid;
16541da177e4SLinus Torvalds 
1655978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1656978a46faSChristian Brauner 			args->flags);
165751456b29SIan Morris 	if (!nlh)
165826932566SPatrick McHardy 		return -EMSGSIZE;
165947f68512SThomas Graf 
166047f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16611da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16621da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16635c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16641da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16651da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16661da177e4SLinus Torvalds 
1667978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1668978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1669d3807145SChristian Brauner 		goto nla_put_failure;
1670d3807145SChristian Brauner 
16715c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16725c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16735c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
16745c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
16755c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
16765c766d64SJiri Pirko 
16775c766d64SJiri Pirko 			if (preferred > tval)
16785c766d64SJiri Pirko 				preferred -= tval;
16795c766d64SJiri Pirko 			else
16805c766d64SJiri Pirko 				preferred = 0;
16815c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
16825c766d64SJiri Pirko 				if (valid > tval)
16835c766d64SJiri Pirko 					valid -= tval;
16845c766d64SJiri Pirko 				else
16855c766d64SJiri Pirko 					valid = 0;
16865c766d64SJiri Pirko 			}
16875c766d64SJiri Pirko 		}
16885c766d64SJiri Pirko 	} else {
16895c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
16905c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
16915c766d64SJiri Pirko 	}
1692f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1693930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1694f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1695930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1696f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1697930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1698f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
16995c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1700ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1701af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1702af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
17035c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
17045c766d64SJiri Pirko 			  preferred, valid))
1705f3756b79SDavid S. Miller 		goto nla_put_failure;
170647f68512SThomas Graf 
1707053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1708053c095aSJohannes Berg 	return 0;
170947f68512SThomas Graf 
171047f68512SThomas Graf nla_put_failure:
171126932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
171226932566SPatrick McHardy 	return -EMSGSIZE;
17131da177e4SLinus Torvalds }
17141da177e4SLinus Torvalds 
1715c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1716c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1717c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17185fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1719c33078e3SDavid Ahern {
17205fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1721c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1722c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1723c33078e3SDavid Ahern 	int err, i;
1724c33078e3SDavid Ahern 
1725c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1726c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1727c33078e3SDavid Ahern 		return -EINVAL;
1728c33078e3SDavid Ahern 	}
1729c33078e3SDavid Ahern 
1730c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1731c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1732c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1733c33078e3SDavid Ahern 		return -EINVAL;
1734c33078e3SDavid Ahern 	}
17355fcd266aSDavid Ahern 
17365fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17375fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17385fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17395fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1740c33078e3SDavid Ahern 	}
1741c33078e3SDavid Ahern 
17428cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1743c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1744c33078e3SDavid Ahern 	if (err < 0)
1745c33078e3SDavid Ahern 		return err;
1746c33078e3SDavid Ahern 
1747c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1748c33078e3SDavid Ahern 		if (!tb[i])
1749c33078e3SDavid Ahern 			continue;
1750c33078e3SDavid Ahern 
1751c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1752c33078e3SDavid Ahern 			struct net *net;
1753c33078e3SDavid Ahern 
1754c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1755c33078e3SDavid Ahern 
1756c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1757c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1758bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1759c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1760c33078e3SDavid Ahern 				return PTR_ERR(net);
1761c33078e3SDavid Ahern 			}
1762c33078e3SDavid Ahern 			*tgt_net = net;
1763c33078e3SDavid Ahern 		} else {
1764c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1765c33078e3SDavid Ahern 			return -EINVAL;
1766c33078e3SDavid Ahern 		}
1767c33078e3SDavid Ahern 	}
1768c33078e3SDavid Ahern 
1769c33078e3SDavid Ahern 	return 0;
1770c33078e3SDavid Ahern }
1771c33078e3SDavid Ahern 
17721c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
17731c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
17741c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
17751c98eca4SDavid Ahern {
17761c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
17771c98eca4SDavid Ahern 	int ip_idx = 0;
17781c98eca4SDavid Ahern 	int err;
17791c98eca4SDavid Ahern 
1780d3e6e285SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1781ef11db33SFlorian Westphal 		if (ip_idx < s_ip_idx) {
1782ef11db33SFlorian Westphal 			ip_idx++;
17831c98eca4SDavid Ahern 			continue;
1784ef11db33SFlorian Westphal 		}
17851c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
17861c98eca4SDavid Ahern 		if (err < 0)
17871c98eca4SDavid Ahern 			goto done;
17881c98eca4SDavid Ahern 
17891c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1790ef11db33SFlorian Westphal 		ip_idx++;
17911c98eca4SDavid Ahern 	}
17921c98eca4SDavid Ahern 	err = 0;
17931c98eca4SDavid Ahern 
17941c98eca4SDavid Ahern done:
17951c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
17961c98eca4SDavid Ahern 
17971c98eca4SDavid Ahern 	return err;
17981c98eca4SDavid Ahern }
17991c98eca4SDavid Ahern 
18001da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18011da177e4SLinus Torvalds {
1802c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1803978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1804978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1805c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1806978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1807978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1808978a46faSChristian Brauner 		.netnsid = -1,
1809978a46faSChristian Brauner 	};
18103b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1811d3807145SChristian Brauner 	struct net *tgt_net = net;
1812eec4df98SEric Dumazet 	int h, s_h;
1813eec4df98SEric Dumazet 	int idx, s_idx;
18141c98eca4SDavid Ahern 	int s_ip_idx;
18151da177e4SLinus Torvalds 	struct net_device *dev;
18161da177e4SLinus Torvalds 	struct in_device *in_dev;
1817eec4df98SEric Dumazet 	struct hlist_head *head;
1818d7e38611SDavid Ahern 	int err = 0;
18191da177e4SLinus Torvalds 
1820eec4df98SEric Dumazet 	s_h = cb->args[0];
1821eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
18221c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1823eec4df98SEric Dumazet 
1824c33078e3SDavid Ahern 	if (cb->strict_check) {
1825c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18265fcd266aSDavid Ahern 						 skb->sk, cb);
1827c33078e3SDavid Ahern 		if (err < 0)
1828d7e38611SDavid Ahern 			goto put_tgt_net;
18295fcd266aSDavid Ahern 
1830d7e38611SDavid Ahern 		err = 0;
18315fcd266aSDavid Ahern 		if (fillargs.ifindex) {
18325fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1833d7e38611SDavid Ahern 			if (!dev) {
1834d7e38611SDavid Ahern 				err = -ENODEV;
1835d7e38611SDavid Ahern 				goto put_tgt_net;
1836d7e38611SDavid Ahern 			}
18375fcd266aSDavid Ahern 
18385fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
18395fcd266aSDavid Ahern 			if (in_dev) {
18405fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18415fcd266aSDavid Ahern 						       &fillargs);
18425fcd266aSDavid Ahern 			}
18435fcd266aSDavid Ahern 			goto put_tgt_net;
18445fcd266aSDavid Ahern 		}
1845d3807145SChristian Brauner 	}
1846d3807145SChristian Brauner 
1847eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18487562f876SPavel Emelianov 		idx = 0;
1849d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1850eec4df98SEric Dumazet 		rcu_read_lock();
1851d3807145SChristian Brauner 		cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^
1852d3807145SChristian Brauner 			  tgt_net->dev_base_seq;
1853b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18541da177e4SLinus Torvalds 			if (idx < s_idx)
18557562f876SPavel Emelianov 				goto cont;
18564b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
18571da177e4SLinus Torvalds 				s_ip_idx = 0;
1858eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
18599f9354b9SEric Dumazet 			if (!in_dev)
18607562f876SPavel Emelianov 				goto cont;
18611da177e4SLinus Torvalds 
18621c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18631c98eca4SDavid Ahern 					       &fillargs);
18641c98eca4SDavid Ahern 			if (err < 0) {
1865eec4df98SEric Dumazet 				rcu_read_unlock();
18661da177e4SLinus Torvalds 				goto done;
18671da177e4SLinus Torvalds 			}
18687562f876SPavel Emelianov cont:
18697562f876SPavel Emelianov 			idx++;
18701da177e4SLinus Torvalds 		}
1871eec4df98SEric Dumazet 		rcu_read_unlock();
1872eec4df98SEric Dumazet 	}
18731da177e4SLinus Torvalds 
18741da177e4SLinus Torvalds done:
1875eec4df98SEric Dumazet 	cb->args[0] = h;
1876eec4df98SEric Dumazet 	cb->args[1] = idx;
18775fcd266aSDavid Ahern put_tgt_net:
1878978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1879d3807145SChristian Brauner 		put_net(tgt_net);
18801da177e4SLinus Torvalds 
18817c1e8a38SArthur Gautier 	return skb->len ? : err;
18821da177e4SLinus Torvalds }
18831da177e4SLinus Torvalds 
1884d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
188515e47304SEric W. Biederman 		      u32 portid)
18861da177e4SLinus Torvalds {
1887978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1888978a46faSChristian Brauner 		.portid = portid,
1889978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1890978a46faSChristian Brauner 		.event = event,
1891978a46faSChristian Brauner 		.flags = 0,
1892978a46faSChristian Brauner 		.netnsid = -1,
1893978a46faSChristian Brauner 	};
189447f68512SThomas Graf 	struct sk_buff *skb;
1895d6062cbbSThomas Graf 	int err = -ENOBUFS;
18964b8aa9abSDenis V. Lunev 	struct net *net;
18971da177e4SLinus Torvalds 
1898c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1899339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
190051456b29SIan Morris 	if (!skb)
1901d6062cbbSThomas Graf 		goto errout;
1902d6062cbbSThomas Graf 
1903978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
190426932566SPatrick McHardy 	if (err < 0) {
190526932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
190626932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
190726932566SPatrick McHardy 		kfree_skb(skb);
190826932566SPatrick McHardy 		goto errout;
190926932566SPatrick McHardy 	}
191015e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19111ce85fe4SPablo Neira Ayuso 	return;
1912d6062cbbSThomas Graf errout:
1913d6062cbbSThomas Graf 	if (err < 0)
19144b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19151da177e4SLinus Torvalds }
19161da177e4SLinus Torvalds 
1917b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1918b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19199f0f7272SThomas Graf {
19201fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19219f0f7272SThomas Graf 
19229f0f7272SThomas Graf 	if (!in_dev)
19239f0f7272SThomas Graf 		return 0;
19249f0f7272SThomas Graf 
19259f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19269f0f7272SThomas Graf }
19279f0f7272SThomas Graf 
1928d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1929d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19309f0f7272SThomas Graf {
19311fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19329f0f7272SThomas Graf 	struct nlattr *nla;
19339f0f7272SThomas Graf 	int i;
19349f0f7272SThomas Graf 
19359f0f7272SThomas Graf 	if (!in_dev)
19369f0f7272SThomas Graf 		return -ENODATA;
19379f0f7272SThomas Graf 
19389f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
193951456b29SIan Morris 	if (!nla)
19409f0f7272SThomas Graf 		return -EMSGSIZE;
19419f0f7272SThomas Graf 
19429f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19439f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
19449f0f7272SThomas Graf 
19459f0f7272SThomas Graf 	return 0;
19469f0f7272SThomas Graf }
19479f0f7272SThomas Graf 
19489f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19499f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19509f0f7272SThomas Graf };
19519f0f7272SThomas Graf 
1952cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1953cf7afbfeSThomas Graf 				 const struct nlattr *nla)
19549f0f7272SThomas Graf {
19559f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19569f0f7272SThomas Graf 	int err, rem;
19579f0f7272SThomas Graf 
1958a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
1959cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
19609f0f7272SThomas Graf 
19618cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
19628cb08174SJohannes Berg 					  inet_af_policy, NULL);
19639f0f7272SThomas Graf 	if (err < 0)
19649f0f7272SThomas Graf 		return err;
19659f0f7272SThomas Graf 
19669f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19679f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
19689f0f7272SThomas Graf 			int cfgid = nla_type(a);
19699f0f7272SThomas Graf 
19709f0f7272SThomas Graf 			if (nla_len(a) < 4)
19719f0f7272SThomas Graf 				return -EINVAL;
19729f0f7272SThomas Graf 
19739f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
19749f0f7272SThomas Graf 				return -EINVAL;
19759f0f7272SThomas Graf 		}
19769f0f7272SThomas Graf 	}
19779f0f7272SThomas Graf 
1978cf7afbfeSThomas Graf 	return 0;
1979cf7afbfeSThomas Graf }
1980cf7afbfeSThomas Graf 
19813583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
19823583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
1983cf7afbfeSThomas Graf {
1984a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1985cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1986cf7afbfeSThomas Graf 	int rem;
1987cf7afbfeSThomas Graf 
1988cf7afbfeSThomas Graf 	if (!in_dev)
1989cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1990cf7afbfeSThomas Graf 
19918cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
19925ac6b198SZheng Yongjun 		return -EINVAL;
1993cf7afbfeSThomas Graf 
19949f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
19959f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
19969f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
19979f0f7272SThomas Graf 	}
19989f0f7272SThomas Graf 
19999f0f7272SThomas Graf 	return 0;
20009f0f7272SThomas Graf }
20019f0f7272SThomas Graf 
2002edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2003edc9e748SNicolas Dichtel {
2004edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2005edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2006136ba622SZhang Shengju 	bool all = false;
2007edc9e748SNicolas Dichtel 
2008136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2009136ba622SZhang Shengju 		all = true;
2010136ba622SZhang Shengju 
2011136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2012edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2013136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2014cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2015136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2016d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20175cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20185cbf777cSXin Long 		size += nla_total_size(4);
2019136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2020f085ff1cSstephen hemminger 		size += nla_total_size(4);
2021136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2022974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2023edc9e748SNicolas Dichtel 
2024edc9e748SNicolas Dichtel 	return size;
2025edc9e748SNicolas Dichtel }
2026edc9e748SNicolas Dichtel 
2027edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
2028edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
2029edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
2030edc9e748SNicolas Dichtel 				     int type)
2031edc9e748SNicolas Dichtel {
2032edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2033edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2034136ba622SZhang Shengju 	bool all = false;
2035edc9e748SNicolas Dichtel 
2036edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2037edc9e748SNicolas Dichtel 			flags);
203851456b29SIan Morris 	if (!nlh)
2039edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2040edc9e748SNicolas Dichtel 
2041136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2042136ba622SZhang Shengju 		all = true;
2043136ba622SZhang Shengju 
2044edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2045edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2046edc9e748SNicolas Dichtel 
2047edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2048edc9e748SNicolas Dichtel 		goto nla_put_failure;
2049edc9e748SNicolas Dichtel 
2050b5c9641dSDavid Ahern 	if (!devconf)
2051b5c9641dSDavid Ahern 		goto out;
2052b5c9641dSDavid Ahern 
2053136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2054edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2055edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2056edc9e748SNicolas Dichtel 		goto nla_put_failure;
2057136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2058cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2059cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2060cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2061136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2062d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2063d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2064d67b8c61SNicolas Dichtel 		goto nla_put_failure;
20655cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
20665cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
20675cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
20685cbf777cSXin Long 		goto nla_put_failure;
2069136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
207009aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2071f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2072f085ff1cSstephen hemminger 		goto nla_put_failure;
2073136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2074974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2075974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2076974d7af5SAndy Gospodarek 		goto nla_put_failure;
2077edc9e748SNicolas Dichtel 
2078b5c9641dSDavid Ahern out:
2079053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2080053c095aSJohannes Berg 	return 0;
2081edc9e748SNicolas Dichtel 
2082edc9e748SNicolas Dichtel nla_put_failure:
2083edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2084edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2085edc9e748SNicolas Dichtel }
2086edc9e748SNicolas Dichtel 
20873b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
20883b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2089edc9e748SNicolas Dichtel {
2090edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2091edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2092edc9e748SNicolas Dichtel 
2093fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
209451456b29SIan Morris 	if (!skb)
2095edc9e748SNicolas Dichtel 		goto errout;
2096edc9e748SNicolas Dichtel 
2097edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
20983b022865SDavid Ahern 					event, 0, type);
2099edc9e748SNicolas Dichtel 	if (err < 0) {
2100edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2101edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2102edc9e748SNicolas Dichtel 		kfree_skb(skb);
2103edc9e748SNicolas Dichtel 		goto errout;
2104edc9e748SNicolas Dichtel 	}
2105fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2106edc9e748SNicolas Dichtel 	return;
2107edc9e748SNicolas Dichtel errout:
2108edc9e748SNicolas Dichtel 	if (err < 0)
2109edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2110edc9e748SNicolas Dichtel }
2111edc9e748SNicolas Dichtel 
21129e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21139e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21149e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2115cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
211609aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2117974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21189e551110SNicolas Dichtel };
21199e551110SNicolas Dichtel 
2120eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2121eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2122eede370dSJakub Kicinski 				      struct nlattr **tb,
2123eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2124eede370dSJakub Kicinski {
2125eede370dSJakub Kicinski 	int i, err;
2126eede370dSJakub Kicinski 
2127eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2128eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2129eede370dSJakub Kicinski 		return -EINVAL;
2130eede370dSJakub Kicinski 	}
2131eede370dSJakub Kicinski 
2132eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21338cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21348cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21358cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2136eede370dSJakub Kicinski 
21378cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21388cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21398cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2140eede370dSJakub Kicinski 	if (err)
2141eede370dSJakub Kicinski 		return err;
2142eede370dSJakub Kicinski 
2143eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2144eede370dSJakub Kicinski 		if (!tb[i])
2145eede370dSJakub Kicinski 			continue;
2146eede370dSJakub Kicinski 
2147eede370dSJakub Kicinski 		switch (i) {
2148eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2149eede370dSJakub Kicinski 			break;
2150eede370dSJakub Kicinski 		default:
2151eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2152eede370dSJakub Kicinski 			return -EINVAL;
2153eede370dSJakub Kicinski 		}
2154eede370dSJakub Kicinski 	}
2155eede370dSJakub Kicinski 
2156eede370dSJakub Kicinski 	return 0;
2157eede370dSJakub Kicinski }
2158eede370dSJakub Kicinski 
21599e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2160c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2161c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
21629e551110SNicolas Dichtel {
21639e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
21649e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
21659e551110SNicolas Dichtel 	struct sk_buff *skb;
21669e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
21679e551110SNicolas Dichtel 	struct in_device *in_dev;
21689e551110SNicolas Dichtel 	struct net_device *dev;
21699e551110SNicolas Dichtel 	int ifindex;
21709e551110SNicolas Dichtel 	int err;
21719e551110SNicolas Dichtel 
2172eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2173eede370dSJakub Kicinski 	if (err)
21749e551110SNicolas Dichtel 		goto errout;
21759e551110SNicolas Dichtel 
2176a97eb33fSAnton Protopopov 	err = -EINVAL;
21779e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
21789e551110SNicolas Dichtel 		goto errout;
21799e551110SNicolas Dichtel 
21809e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
21819e551110SNicolas Dichtel 	switch (ifindex) {
21829e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
21839e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
21849e551110SNicolas Dichtel 		break;
21859e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
21869e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
21879e551110SNicolas Dichtel 		break;
21889e551110SNicolas Dichtel 	default:
21899e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
219051456b29SIan Morris 		if (!dev)
21919e551110SNicolas Dichtel 			goto errout;
21929e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
219351456b29SIan Morris 		if (!in_dev)
21949e551110SNicolas Dichtel 			goto errout;
21959e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
21969e551110SNicolas Dichtel 		break;
21979e551110SNicolas Dichtel 	}
21989e551110SNicolas Dichtel 
21999e551110SNicolas Dichtel 	err = -ENOBUFS;
2200fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
220151456b29SIan Morris 	if (!skb)
22029e551110SNicolas Dichtel 		goto errout;
22039e551110SNicolas Dichtel 
22049e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22059e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22069e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2207136ba622SZhang Shengju 					NETCONFA_ALL);
22089e551110SNicolas Dichtel 	if (err < 0) {
22099e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22109e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22119e551110SNicolas Dichtel 		kfree_skb(skb);
22129e551110SNicolas Dichtel 		goto errout;
22139e551110SNicolas Dichtel 	}
22149e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22159e551110SNicolas Dichtel errout:
22169e551110SNicolas Dichtel 	return err;
22179e551110SNicolas Dichtel }
22189e551110SNicolas Dichtel 
22197a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22207a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22217a674200SNicolas Dichtel {
2222addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22237a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
22247a674200SNicolas Dichtel 	int h, s_h;
22257a674200SNicolas Dichtel 	int idx, s_idx;
22267a674200SNicolas Dichtel 	struct net_device *dev;
22277a674200SNicolas Dichtel 	struct in_device *in_dev;
22287a674200SNicolas Dichtel 	struct hlist_head *head;
22297a674200SNicolas Dichtel 
2230addd383fSDavid Ahern 	if (cb->strict_check) {
2231addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2232addd383fSDavid Ahern 		struct netconfmsg *ncm;
2233addd383fSDavid Ahern 
2234addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2235addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2236addd383fSDavid Ahern 			return -EINVAL;
2237addd383fSDavid Ahern 		}
2238addd383fSDavid Ahern 
2239addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2240addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2241addd383fSDavid Ahern 			return -EINVAL;
2242addd383fSDavid Ahern 		}
2243addd383fSDavid Ahern 	}
2244addd383fSDavid Ahern 
22457a674200SNicolas Dichtel 	s_h = cb->args[0];
22467a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
22477a674200SNicolas Dichtel 
22487a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
22497a674200SNicolas Dichtel 		idx = 0;
22507a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
22517a674200SNicolas Dichtel 		rcu_read_lock();
22520465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
22530465277fSNicolas Dichtel 			  net->dev_base_seq;
22547a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
22557a674200SNicolas Dichtel 			if (idx < s_idx)
22567a674200SNicolas Dichtel 				goto cont;
22577a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
22587a674200SNicolas Dichtel 			if (!in_dev)
22597a674200SNicolas Dichtel 				goto cont;
22607a674200SNicolas Dichtel 
22617a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
22627a674200SNicolas Dichtel 						      &in_dev->cnf,
22637a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2264addd383fSDavid Ahern 						      nlh->nlmsg_seq,
22657a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
22667a674200SNicolas Dichtel 						      NLM_F_MULTI,
2267136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
22687a674200SNicolas Dichtel 				rcu_read_unlock();
22697a674200SNicolas Dichtel 				goto done;
22707a674200SNicolas Dichtel 			}
22710465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
22727a674200SNicolas Dichtel cont:
22737a674200SNicolas Dichtel 			idx++;
22747a674200SNicolas Dichtel 		}
22757a674200SNicolas Dichtel 		rcu_read_unlock();
22767a674200SNicolas Dichtel 	}
22777a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
22787a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
22797a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
22807a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2281addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22827a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2283136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22847a674200SNicolas Dichtel 			goto done;
22857a674200SNicolas Dichtel 		else
22867a674200SNicolas Dichtel 			h++;
22877a674200SNicolas Dichtel 	}
22887a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
22897a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
22907a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
22917a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2292addd383fSDavid Ahern 					      nlh->nlmsg_seq,
22937a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2294136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
22957a674200SNicolas Dichtel 			goto done;
22967a674200SNicolas Dichtel 		else
22977a674200SNicolas Dichtel 			h++;
22987a674200SNicolas Dichtel 	}
22997a674200SNicolas Dichtel done:
23007a674200SNicolas Dichtel 	cb->args[0] = h;
23017a674200SNicolas Dichtel 	cb->args[1] = idx;
23027a674200SNicolas Dichtel 
23037a674200SNicolas Dichtel 	return skb->len;
23047a674200SNicolas Dichtel }
23057a674200SNicolas Dichtel 
23061da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23071da177e4SLinus Torvalds 
2308c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
230931be3085SHerbert Xu {
231031be3085SHerbert Xu 	struct net_device *dev;
231131be3085SHerbert Xu 
231231be3085SHerbert Xu 	rcu_read_lock();
2313c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2314c6d14c84SEric Dumazet 		struct in_device *in_dev;
2315c6d14c84SEric Dumazet 
231631be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
231731be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23189355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2319c6d14c84SEric Dumazet 	}
232031be3085SHerbert Xu 	rcu_read_unlock();
232131be3085SHerbert Xu }
232231be3085SHerbert Xu 
2323c6d14c84SEric Dumazet /* called with RTNL locked */
2324c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
232568dd299bSPavel Emelyanov {
232668dd299bSPavel Emelyanov 	struct net_device *dev;
2327586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
232868dd299bSPavel Emelyanov 
2329586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23309355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23313b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23323b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2333edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2334edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23353b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23363b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2337edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2338edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
233968dd299bSPavel Emelyanov 
2340c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
234168dd299bSPavel Emelyanov 		struct in_device *in_dev;
2342fa17806cSEric Dumazet 
23430187bdfbSBen Hutchings 		if (on)
23440187bdfbSBen Hutchings 			dev_disable_lro(dev);
2345fa17806cSEric Dumazet 
2346fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2347edc9e748SNicolas Dichtel 		if (in_dev) {
234868dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23493b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23503b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2351edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2352edc9e748SNicolas Dichtel 		}
235368dd299bSPavel Emelyanov 	}
235468dd299bSPavel Emelyanov }
235568dd299bSPavel Emelyanov 
2356f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2357f085ff1cSstephen hemminger {
2358f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2359f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2360f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2361f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2362f085ff1cSstephen hemminger 	else {
2363f085ff1cSstephen hemminger 		struct in_device *idev
2364f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2365f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2366f085ff1cSstephen hemminger 	}
2367f085ff1cSstephen hemminger }
2368f085ff1cSstephen hemminger 
2369fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
237032927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
237131be3085SHerbert Xu {
2372d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
23738d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2374d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
237531be3085SHerbert Xu 
237631be3085SHerbert Xu 	if (write) {
237731be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2378c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
237931be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2380f085ff1cSstephen hemminger 		int ifindex;
238131be3085SHerbert Xu 
238231be3085SHerbert Xu 		set_bit(i, cnf->state);
238331be3085SHerbert Xu 
23849355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2385c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2386d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2387d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2388d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
23894ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2390f085ff1cSstephen hemminger 
23915cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
23925cbf777cSXin Long 		    new_value != old_value)
23935cbf777cSXin Long 			rt_cache_flush(net);
23945cbf777cSXin Long 
2395cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2396cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2397f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
23983b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23993b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2400cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2401cc535dfbSNicolas Dichtel 		}
2402f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2403f085ff1cSstephen hemminger 		    new_value != old_value) {
2404f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24053b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24063b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2407f085ff1cSstephen hemminger 						    ifindex, cnf);
2408f085ff1cSstephen hemminger 		}
2409974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2410974d7af5SAndy Gospodarek 		    new_value != old_value) {
2411974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24123b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24133b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2414974d7af5SAndy Gospodarek 						    ifindex, cnf);
2415974d7af5SAndy Gospodarek 		}
241631be3085SHerbert Xu 	}
241731be3085SHerbert Xu 
241831be3085SHerbert Xu 	return ret;
241931be3085SHerbert Xu }
242031be3085SHerbert Xu 
2421fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
242232927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24231da177e4SLinus Torvalds {
24241da177e4SLinus Torvalds 	int *valp = ctl->data;
24251da177e4SLinus Torvalds 	int val = *valp;
242688af182eSEric W. Biederman 	loff_t pos = *ppos;
24278292d7f6SYang Yang 	struct net *net = ctl->extra2;
24288292d7f6SYang Yang 	int ret;
24298292d7f6SYang Yang 
24308292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24318292d7f6SYang Yang 		return -EPERM;
24328292d7f6SYang Yang 
24338292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24341da177e4SLinus Torvalds 
24351da177e4SLinus Torvalds 	if (write && *valp != val) {
24360187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
243788af182eSEric W. Biederman 			if (!rtnl_trylock()) {
243888af182eSEric W. Biederman 				/* Restore the original values before restarting */
243988af182eSEric W. Biederman 				*valp = val;
244088af182eSEric W. Biederman 				*ppos = pos;
24419b8adb5eSEric W. Biederman 				return restart_syscall();
244288af182eSEric W. Biederman 			}
24430187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2444c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2445edc9e748SNicolas Dichtel 			} else {
24460187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24470187bdfbSBen Hutchings 				struct in_device *idev =
24480187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2449edc9e748SNicolas Dichtel 				if (*valp)
24500187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24513b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2452edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2453edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2454edc9e748SNicolas Dichtel 							    cnf);
24550187bdfbSBen Hutchings 			}
24560187bdfbSBen Hutchings 			rtnl_unlock();
24574ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2458edc9e748SNicolas Dichtel 		} else
24593b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24603b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2461edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2462edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
24630187bdfbSBen Hutchings 	}
24641da177e4SLinus Torvalds 
24651da177e4SLinus Torvalds 	return ret;
24661da177e4SLinus Torvalds }
24671da177e4SLinus Torvalds 
2468fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
246932927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
24701da177e4SLinus Torvalds {
24711da177e4SLinus Torvalds 	int *valp = ctl->data;
24721da177e4SLinus Torvalds 	int val = *valp;
24738d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
247476e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
24751da177e4SLinus Torvalds 
24761da177e4SLinus Torvalds 	if (write && *valp != val)
24774ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
24781da177e4SLinus Torvalds 
24791da177e4SLinus Torvalds 	return ret;
24801da177e4SLinus Torvalds }
24811da177e4SLinus Torvalds 
2482f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
248342f811b8SHerbert Xu 	{ \
248442f811b8SHerbert Xu 		.procname	= name, \
248542f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
248602291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
248742f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
248842f811b8SHerbert Xu 		.mode		= mval, \
248942f811b8SHerbert Xu 		.proc_handler	= proc, \
249031be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
249142f811b8SHerbert Xu 	}
249242f811b8SHerbert Xu 
249342f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2494f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
249542f811b8SHerbert Xu 
249642f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2497f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
249842f811b8SHerbert Xu 
2499f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2500f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
250142f811b8SHerbert Xu 
250242f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2503f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
250442f811b8SHerbert Xu 
25051da177e4SLinus Torvalds static struct devinet_sysctl_table {
25061da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
250702291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
25081da177e4SLinus Torvalds } devinet_sysctl = {
25091da177e4SLinus Torvalds 	.devinet_vars = {
251042f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2511f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
251242f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25135cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
251442f811b8SHerbert Xu 
251542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
251642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
251742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
251842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
251942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
252042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
252142f811b8SHerbert Xu 					"accept_source_route"),
25228153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
252328f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
252442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
252542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
252642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
252742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
252842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
252942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
253042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
253142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
253242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2533eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
253465324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25355c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25365c6fe01cSWilliam Manley 					"force_igmp_version"),
25372690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25382690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25392690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25402690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25410eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25420eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
254397daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
254497daf331SJohannes Berg 					"drop_gratuitous_arp"),
254542f811b8SHerbert Xu 
254642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
254742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
254842f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
254942f811b8SHerbert Xu 					      "promote_secondaries"),
2550d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2551d0daebc3SThomas Graf 					      "route_localnet"),
255212b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
255312b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25541da177e4SLinus Torvalds 	},
25551da177e4SLinus Torvalds };
25561da177e4SLinus Torvalds 
2557ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
255829c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
25591da177e4SLinus Torvalds {
25601da177e4SLinus Torvalds 	int i;
25619fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
25628607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2563bfada697SPavel Emelyanov 
25649fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
25651da177e4SLinus Torvalds 	if (!t)
25669fa89642SPavel Emelyanov 		goto out;
25679fa89642SPavel Emelyanov 
25681da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
25691da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
257031be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2571c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
25721da177e4SLinus Torvalds 	}
25731da177e4SLinus Torvalds 
25748607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
25751da177e4SLinus Torvalds 
25768607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
25771da177e4SLinus Torvalds 	if (!t->sysctl_header)
25788607ddb8SEric W. Biederman 		goto free;
25791da177e4SLinus Torvalds 
25801da177e4SLinus Torvalds 	p->sysctl = t;
258129c994e3SNicolas Dichtel 
25823b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
25833b022865SDavid Ahern 				    ifindex, p);
2584ea40b324SPavel Emelyanov 	return 0;
25851da177e4SLinus Torvalds 
25861da177e4SLinus Torvalds free:
25871da177e4SLinus Torvalds 	kfree(t);
25889fa89642SPavel Emelyanov out:
2589ea40b324SPavel Emelyanov 	return -ENOBUFS;
25901da177e4SLinus Torvalds }
25911da177e4SLinus Torvalds 
2592b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2593b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
259466f27a52SPavel Emelyanov {
259551602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
259666f27a52SPavel Emelyanov 
2597b5c9641dSDavid Ahern 	if (t) {
259851602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2599ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26001da177e4SLinus Torvalds 		kfree(t);
26011da177e4SLinus Torvalds 	}
260251602b2aSPavel Emelyanov 
2603b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2604b5c9641dSDavid Ahern }
2605b5c9641dSDavid Ahern 
260620e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
260751602b2aSPavel Emelyanov {
260820e61da7SWANG Cong 	int err;
260920e61da7SWANG Cong 
261020e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
261120e61da7SWANG Cong 		return -EINVAL;
261220e61da7SWANG Cong 
261320e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
261420e61da7SWANG Cong 	if (err)
261520e61da7SWANG Cong 		return err;
261620e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
261729c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
261820e61da7SWANG Cong 	if (err)
261920e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
262020e61da7SWANG Cong 	return err;
262151602b2aSPavel Emelyanov }
262251602b2aSPavel Emelyanov 
262351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
262451602b2aSPavel Emelyanov {
2625b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2626b5c9641dSDavid Ahern 
2627b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
262851602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26291da177e4SLinus Torvalds }
26301da177e4SLinus Torvalds 
263168dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
263268dd299bSPavel Emelyanov 	{
263368dd299bSPavel Emelyanov 		.procname	= "ip_forward",
263468dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
263502291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
263668dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
263768dd299bSPavel Emelyanov 		.mode		= 0644,
263868dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
263968dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2640c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
264168dd299bSPavel Emelyanov 	},
264268dd299bSPavel Emelyanov 	{ },
264368dd299bSPavel Emelyanov };
26442a75de0cSEric Dumazet #endif
264568dd299bSPavel Emelyanov 
2646752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2647752d14dcSPavel Emelyanov {
2648752d14dcSPavel Emelyanov 	int err;
2649752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26502a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2651856c395cSCong Wang 	struct ctl_table *tbl;
2652752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26532a75de0cSEric Dumazet #endif
2654752d14dcSPavel Emelyanov 
2655752d14dcSPavel Emelyanov 	err = -ENOMEM;
2656856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
265751456b29SIan Morris 	if (!all)
2658752d14dcSPavel Emelyanov 		goto err_alloc_all;
2659752d14dcSPavel Emelyanov 
2660856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
266151456b29SIan Morris 	if (!dflt)
2662752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2663752d14dcSPavel Emelyanov 
26642a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2665856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
266651456b29SIan Morris 	if (!tbl)
2667752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2668752d14dcSPavel Emelyanov 
266902291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2670752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2671752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
26722a75de0cSEric Dumazet #endif
2673856c395cSCong Wang 
26749efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
26759efd6a3cSNicolas Dichtel 		if (IS_ENABLED(CONFIG_SYSCTL) &&
26769efd6a3cSNicolas Dichtel 		    sysctl_devconf_inherit_init_net == 3) {
26779efd6a3cSNicolas Dichtel 			/* copy from the current netns */
26789efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
26799efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
26809efd6a3cSNicolas Dichtel 			memcpy(dflt,
26819efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
26829efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
26839efd6a3cSNicolas Dichtel 		} else if (!IS_ENABLED(CONFIG_SYSCTL) ||
26849efd6a3cSNicolas Dichtel 			   sysctl_devconf_inherit_init_net != 2) {
26859efd6a3cSNicolas Dichtel 			/* inherit == 0 or 1: copy from init_net */
26869efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
26879efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
26889efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
26899efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
26909efd6a3cSNicolas Dichtel 		}
26919efd6a3cSNicolas Dichtel 		/* else inherit == 2: use compiled values */
2692752d14dcSPavel Emelyanov 	}
2693752d14dcSPavel Emelyanov 
2694752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
269529c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2696752d14dcSPavel Emelyanov 	if (err < 0)
2697752d14dcSPavel Emelyanov 		goto err_reg_all;
2698752d14dcSPavel Emelyanov 
269929c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
270029c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2701752d14dcSPavel Emelyanov 	if (err < 0)
2702752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2703752d14dcSPavel Emelyanov 
2704752d14dcSPavel Emelyanov 	err = -ENOMEM;
27058607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
270651456b29SIan Morris 	if (!forw_hdr)
2707752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27082a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2709752d14dcSPavel Emelyanov #endif
2710752d14dcSPavel Emelyanov 
2711752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2712752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2713752d14dcSPavel Emelyanov 	return 0;
2714752d14dcSPavel Emelyanov 
2715752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2716752d14dcSPavel Emelyanov err_reg_ctl:
2717b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2718752d14dcSPavel Emelyanov err_reg_dflt:
2719b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2720752d14dcSPavel Emelyanov err_reg_all:
2721752d14dcSPavel Emelyanov 	kfree(tbl);
2722752d14dcSPavel Emelyanov err_alloc_ctl:
27232a75de0cSEric Dumazet #endif
2724752d14dcSPavel Emelyanov 	kfree(dflt);
2725752d14dcSPavel Emelyanov err_alloc_dflt:
2726752d14dcSPavel Emelyanov 	kfree(all);
2727752d14dcSPavel Emelyanov err_alloc_all:
2728752d14dcSPavel Emelyanov 	return err;
2729752d14dcSPavel Emelyanov }
2730752d14dcSPavel Emelyanov 
2731752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2732752d14dcSPavel Emelyanov {
27332a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2734752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2735752d14dcSPavel Emelyanov 
2736752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2737752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2738b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2739b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2740b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2741b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2742752d14dcSPavel Emelyanov 	kfree(tbl);
27432a75de0cSEric Dumazet #endif
2744752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2745752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2746752d14dcSPavel Emelyanov }
2747752d14dcSPavel Emelyanov 
2748752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2749752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2750752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2751752d14dcSPavel Emelyanov };
2752752d14dcSPavel Emelyanov 
2753207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
27549f0f7272SThomas Graf 	.family		  = AF_INET,
27559f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
27569f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2757cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2758cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
27599f0f7272SThomas Graf };
27609f0f7272SThomas Graf 
27611da177e4SLinus Torvalds void __init devinet_init(void)
27621da177e4SLinus Torvalds {
2763fd23c3b3SDavid S. Miller 	int i;
2764fd23c3b3SDavid S. Miller 
2765fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2766fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2767fd23c3b3SDavid S. Miller 
2768752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
27691da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
277063f3444fSThomas Graf 
2771906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
27725c766d64SJiri Pirko 
27739f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
27749f0f7272SThomas Graf 
2775b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2776b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2777b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
27789e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2779b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
27801da177e4SLinus Torvalds }
2781