xref: /openbmc/linux/net/ipv4/devinet.c (revision 6634a8ec)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	NET3	IP device support routines.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
602c30a84SJesper Juhl  * 		Authors:	Ross Biro
71da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
81da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *	Additional Authors:
111da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
121da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Changes:
151da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
161da177e4SLinus Torvalds  *					lists.
171da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
181da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
191da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
201da177e4SLinus Torvalds  *					fall back to comparing just the label
211da177e4SLinus Torvalds  *					if no match found.
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
261da177e4SLinus Torvalds #include <linux/bitops.h>
274fc268d2SRandy Dunlap #include <linux/capability.h>
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds #include <linux/types.h>
301da177e4SLinus Torvalds #include <linux/kernel.h>
31174cd4b1SIngo Molnar #include <linux/sched/signal.h>
321da177e4SLinus Torvalds #include <linux/string.h>
331da177e4SLinus Torvalds #include <linux/mm.h>
341da177e4SLinus Torvalds #include <linux/socket.h>
351da177e4SLinus Torvalds #include <linux/sockios.h>
361da177e4SLinus Torvalds #include <linux/in.h>
371da177e4SLinus Torvalds #include <linux/errno.h>
381da177e4SLinus Torvalds #include <linux/interrupt.h>
391823730fSThomas Graf #include <linux/if_addr.h>
401da177e4SLinus Torvalds #include <linux/if_ether.h>
411da177e4SLinus Torvalds #include <linux/inet.h>
421da177e4SLinus Torvalds #include <linux/netdevice.h>
431da177e4SLinus Torvalds #include <linux/etherdevice.h>
441da177e4SLinus Torvalds #include <linux/skbuff.h>
451da177e4SLinus Torvalds #include <linux/init.h>
461da177e4SLinus Torvalds #include <linux/notifier.h>
471da177e4SLinus Torvalds #include <linux/inetdevice.h>
481da177e4SLinus Torvalds #include <linux/igmp.h>
495a0e3ad6STejun Heo #include <linux/slab.h>
50fd23c3b3SDavid S. Miller #include <linux/hash.h>
511da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
521da177e4SLinus Torvalds #include <linux/sysctl.h>
531da177e4SLinus Torvalds #endif
541da177e4SLinus Torvalds #include <linux/kmod.h>
55edc9e748SNicolas Dichtel #include <linux/netconf.h>
561da177e4SLinus Torvalds 
5714c85021SArnaldo Carvalho de Melo #include <net/arp.h>
581da177e4SLinus Torvalds #include <net/ip.h>
591da177e4SLinus Torvalds #include <net/route.h>
601da177e4SLinus Torvalds #include <net/ip_fib.h>
6163f3444fSThomas Graf #include <net/rtnetlink.h>
62752d14dcSPavel Emelyanov #include <net/net_namespace.h>
635c766d64SJiri Pirko #include <net/addrconf.h>
641da177e4SLinus Torvalds 
652e605463SMatteo Croce #define IPV6ONLY_FLAGS	\
662e605463SMatteo Croce 		(IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \
672e605463SMatteo Croce 		 IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \
682e605463SMatteo Croce 		 IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY)
692e605463SMatteo Croce 
700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7142f811b8SHerbert Xu 	.data = {
7202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
772690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
78fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
7942f811b8SHerbert Xu 	},
801da177e4SLinus Torvalds };
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8342f811b8SHerbert Xu 	.data = {
8402291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8802291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
902690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
91fcdb44d0SJames Prestwood 		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
9242f811b8SHerbert Xu 	},
931da177e4SLinus Torvalds };
941da177e4SLinus Torvalds 
959355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
969355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9742f811b8SHerbert Xu 
98ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
995c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
1005c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
1015c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1025176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1035c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
104ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
105af4d768aSDavid Ahern 	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
106d3807145SChristian Brauner 	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
10747f0bd50SJacques de Laval 	[IFA_PROTO]		= { .type = NLA_U8 },
1085c753978SThomas Graf };
1095c753978SThomas Graf 
110978a46faSChristian Brauner struct inet_fill_args {
111978a46faSChristian Brauner 	u32 portid;
112978a46faSChristian Brauner 	u32 seq;
113978a46faSChristian Brauner 	int event;
114978a46faSChristian Brauner 	unsigned int flags;
115978a46faSChristian Brauner 	int netnsid;
1165fcd266aSDavid Ahern 	int ifindex;
117978a46faSChristian Brauner };
118978a46faSChristian Brauner 
11940384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
12040384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
12140384999SEric Dumazet 
122fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
123fd23c3b3SDavid S. Miller 
inet_addr_hash(const struct net * net,__be32 addr)1246eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
125fd23c3b3SDavid S. Miller {
12640384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
127fd23c3b3SDavid S. Miller 
12840384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
129fd23c3b3SDavid S. Miller }
130fd23c3b3SDavid S. Miller 
inet_hash_insert(struct net * net,struct in_ifaddr * ifa)131fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
132fd23c3b3SDavid S. Miller {
13340384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
134fd23c3b3SDavid S. Miller 
13532a4be48SWANG Cong 	ASSERT_RTNL();
136fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
137fd23c3b3SDavid S. Miller }
138fd23c3b3SDavid S. Miller 
inet_hash_remove(struct in_ifaddr * ifa)139fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
140fd23c3b3SDavid S. Miller {
14132a4be48SWANG Cong 	ASSERT_RTNL();
142fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
143fd23c3b3SDavid S. Miller }
144fd23c3b3SDavid S. Miller 
1459435eb1cSDavid S. Miller /**
1469435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1479435eb1cSDavid S. Miller  * @net: the net namespace
1489435eb1cSDavid S. Miller  * @addr: the source address
1499435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1509435eb1cSDavid S. Miller  *
1519435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1529435eb1cSDavid S. Miller  */
__ip_dev_find(struct net * net,__be32 addr,bool devref)1539435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1549435eb1cSDavid S. Miller {
1559435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1569435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1579435eb1cSDavid S. Miller 
1589435eb1cSDavid S. Miller 	rcu_read_lock();
1596e617de8SPaolo Abeni 	ifa = inet_lookup_ifaddr_rcu(net, addr);
1606e617de8SPaolo Abeni 	if (!ifa) {
161406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
162406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
163406b6f97SDavid S. Miller 		struct fib_table *local;
164406b6f97SDavid S. Miller 
165406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
166406b6f97SDavid S. Miller 		 * over loopback subnets work.
167406b6f97SDavid S. Miller 		 */
168406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
169406b6f97SDavid S. Miller 		if (local &&
170406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
171406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
172406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
1736e617de8SPaolo Abeni 	} else {
1746e617de8SPaolo Abeni 		result = ifa->ifa_dev->dev;
175406b6f97SDavid S. Miller 	}
1769435eb1cSDavid S. Miller 	if (result && devref)
1779435eb1cSDavid S. Miller 		dev_hold(result);
1789435eb1cSDavid S. Miller 	rcu_read_unlock();
1799435eb1cSDavid S. Miller 	return result;
1809435eb1cSDavid S. Miller }
1819435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1829435eb1cSDavid S. Miller 
1836e617de8SPaolo Abeni /* called under RCU lock */
inet_lookup_ifaddr_rcu(struct net * net,__be32 addr)1846e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
1856e617de8SPaolo Abeni {
1866e617de8SPaolo Abeni 	u32 hash = inet_addr_hash(net, addr);
1876e617de8SPaolo Abeni 	struct in_ifaddr *ifa;
1886e617de8SPaolo Abeni 
1896e617de8SPaolo Abeni 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
1906e617de8SPaolo Abeni 		if (ifa->ifa_local == addr &&
1916e617de8SPaolo Abeni 		    net_eq(dev_net(ifa->ifa_dev->dev), net))
1926e617de8SPaolo Abeni 			return ifa;
1936e617de8SPaolo Abeni 
1946e617de8SPaolo Abeni 	return NULL;
1956e617de8SPaolo Abeni }
1966e617de8SPaolo Abeni 
197d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1981da177e4SLinus Torvalds 
199e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
2003ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
2012638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
2022638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
2031da177e4SLinus Torvalds 			 int destroy);
2041da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
20651602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
20751602b2aSPavel Emelyanov #else
devinet_sysctl_register(struct in_device * idev)20820e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
20951602b2aSPavel Emelyanov {
21020e61da7SWANG Cong 	return 0;
21151602b2aSPavel Emelyanov }
devinet_sysctl_unregister(struct in_device * idev)21240384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
21351602b2aSPavel Emelyanov {
21451602b2aSPavel Emelyanov }
2151da177e4SLinus Torvalds #endif
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds /* Locks all the inet devices. */
2181da177e4SLinus Torvalds 
inet_alloc_ifa(void)2191da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2201da177e4SLinus Torvalds {
2216126891cSVasily Averin 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT);
2221da177e4SLinus Torvalds }
2231da177e4SLinus Torvalds 
inet_rcu_free_ifa(struct rcu_head * head)2241da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2271da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2281da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2291da177e4SLinus Torvalds 	kfree(ifa);
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
inet_free_ifa(struct in_ifaddr * ifa)23240384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
in_dev_free_rcu(struct rcu_head * head)2379d40c84cSEric Dumazet static void in_dev_free_rcu(struct rcu_head *head)
2389d40c84cSEric Dumazet {
2399d40c84cSEric Dumazet 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2409d40c84cSEric Dumazet 
2419d40c84cSEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2429d40c84cSEric Dumazet 	kfree(idev);
2439d40c84cSEric Dumazet }
2449d40c84cSEric Dumazet 
in_dev_finish_destroy(struct in_device * idev)2451da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2481da177e4SLinus Torvalds 
249547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
250547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2511da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
25291df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2531da177e4SLinus Torvalds #endif
254d62607c3SJakub Kicinski 	netdev_put(dev, &idev->dev_tracker);
2551da177e4SLinus Torvalds 	if (!idev->dead)
2569f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2579f9354b9SEric Dumazet 	else
2589d40c84cSEric Dumazet 		call_rcu(&idev->rcu_head, in_dev_free_rcu);
2591da177e4SLinus Torvalds }
2609f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2611da177e4SLinus Torvalds 
inetdev_init(struct net_device * dev)26271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	struct in_device *in_dev;
26520e61da7SWANG Cong 	int err = -ENOMEM;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	ASSERT_RTNL();
2681da177e4SLinus Torvalds 
2690da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2701da177e4SLinus Torvalds 	if (!in_dev)
2711da177e4SLinus Torvalds 		goto out;
272c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2739355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2741da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2751da177e4SLinus Torvalds 	in_dev->dev = dev;
2769f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2779f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2781da177e4SLinus Torvalds 		goto out_kfree;
2790187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2800187bdfbSBen Hutchings 		dev_disable_lro(dev);
2811da177e4SLinus Torvalds 	/* Reference in_dev->dev */
282d62607c3SJakub Kicinski 	netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL);
28330c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2847658b36fSReshetova, Elena 	refcount_set(&in_dev->refcnt, 1);
2851da177e4SLinus Torvalds 
28620e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
28720e61da7SWANG Cong 	if (err) {
28820e61da7SWANG Cong 		in_dev->dead = 1;
2891b49cd71SYang Yingliang 		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
29020e61da7SWANG Cong 		in_dev_put(in_dev);
29120e61da7SWANG Cong 		in_dev = NULL;
29220e61da7SWANG Cong 		goto out;
29320e61da7SWANG Cong 	}
2941da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2951da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2961da177e4SLinus Torvalds 		ip_mc_up(in_dev);
297483479ecSJarek Poplawski 
29830c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
299cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
300483479ecSJarek Poplawski out:
30120e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
3021da177e4SLinus Torvalds out_kfree:
3031da177e4SLinus Torvalds 	kfree(in_dev);
3041da177e4SLinus Torvalds 	in_dev = NULL;
3051da177e4SLinus Torvalds 	goto out;
3061da177e4SLinus Torvalds }
3071da177e4SLinus Torvalds 
inetdev_destroy(struct in_device * in_dev)3081da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds 	struct net_device *dev;
3112638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	ASSERT_RTNL();
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	dev = in_dev->dev;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	in_dev->dead = 1;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
3201da177e4SLinus Torvalds 
3212638eb8bSFlorian Westphal 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
3221da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
3231da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3241da177e4SLinus Torvalds 	}
3251da177e4SLinus Torvalds 
326a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3271da177e4SLinus Torvalds 
32851602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3291da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3301da177e4SLinus Torvalds 	arp_ifdown(dev);
3311da177e4SLinus Torvalds 
3329d40c84cSEric Dumazet 	in_dev_put(in_dev);
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds 
inet_addr_onlink(struct in_device * in_dev,__be32 a,__be32 b)335ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3361da177e4SLinus Torvalds {
337d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
338d519e870SFlorian Westphal 
3391da177e4SLinus Torvalds 	rcu_read_lock();
340d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
3411da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3421da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3431da177e4SLinus Torvalds 				rcu_read_unlock();
3441da177e4SLinus Torvalds 				return 1;
3451da177e4SLinus Torvalds 			}
3461da177e4SLinus Torvalds 		}
347d519e870SFlorian Westphal 	}
3481da177e4SLinus Torvalds 	rcu_read_unlock();
3491da177e4SLinus Torvalds 	return 0;
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
__inet_del_ifa(struct in_device * in_dev,struct in_ifaddr __rcu ** ifap,int destroy,struct nlmsghdr * nlh,u32 portid)3522638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev,
3532638eb8bSFlorian Westphal 			   struct in_ifaddr __rcu **ifap,
35415e47304SEric W. Biederman 			   int destroy, struct nlmsghdr *nlh, u32 portid)
3551da177e4SLinus Torvalds {
3568f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3572638eb8bSFlorian Westphal 	struct in_ifaddr *ifa, *ifa1;
358ac28b1ecSLiu Jian 	struct in_ifaddr __rcu **last_prim;
3590ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3600ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	ASSERT_RTNL();
3631da177e4SLinus Torvalds 
3642638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
365ac28b1ecSLiu Jian 	last_prim = ifap;
366fbd40ea0SDavid S. Miller 	if (in_dev->dead)
367fbd40ea0SDavid S. Miller 		goto no_promotions;
368fbd40ea0SDavid S. Miller 
3698f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3708f937c60SHarald Welte 	 * unless alias promotion is set
3718f937c60SHarald Welte 	 **/
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3742638eb8bSFlorian Westphal 		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
3751da177e4SLinus Torvalds 
3762638eb8bSFlorian Westphal 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
3770ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3780ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
379ac28b1ecSLiu Jian 				last_prim = &ifa->ifa_next;
3800ff60a45SJamal Hadi Salim 
3811da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3821da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3831da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3841da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3850ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3861da177e4SLinus Torvalds 				continue;
3871da177e4SLinus Torvalds 			}
3881da177e4SLinus Torvalds 
3890ff60a45SJamal Hadi Salim 			if (!do_promote) {
390fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3911da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3921da177e4SLinus Torvalds 
39315e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
394e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
395e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3961da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3978f937c60SHarald Welte 			} else {
3988f937c60SHarald Welte 				promote = ifa;
3998f937c60SHarald Welte 				break;
4008f937c60SHarald Welte 			}
4011da177e4SLinus Torvalds 		}
4021da177e4SLinus Torvalds 	}
4031da177e4SLinus Torvalds 
4042d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
4052d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
4062d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
4072d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
4082d230e2bSJulian Anastasov 	 */
4092638eb8bSFlorian Westphal 	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
4102d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4112d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
4122d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
4132d230e2bSJulian Anastasov 	}
4142d230e2bSJulian Anastasov 
415fbd40ea0SDavid S. Miller no_promotions:
4161da177e4SLinus Torvalds 	/* 2. Unlink it */
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
419fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 	/* 3. Announce address deletion */
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4241da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
4251da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
4261da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
4271da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
4281da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
4291da177e4SLinus Torvalds 	   So that, this order is correct.
4301da177e4SLinus Torvalds 	 */
43115e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
432e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4330ff60a45SJamal Hadi Salim 
4340ff60a45SJamal Hadi Salim 	if (promote) {
4352638eb8bSFlorian Westphal 		struct in_ifaddr *next_sec;
4360ff60a45SJamal Hadi Salim 
4372638eb8bSFlorian Westphal 		next_sec = rtnl_dereference(promote->ifa_next);
4380ff60a45SJamal Hadi Salim 		if (prev_prom) {
4392638eb8bSFlorian Westphal 			struct in_ifaddr *last_sec;
4402638eb8bSFlorian Westphal 
4412638eb8bSFlorian Westphal 			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
4426a9e9ceaSFlorian Westphal 
443ac28b1ecSLiu Jian 			last_sec = rtnl_dereference(*last_prim);
4442638eb8bSFlorian Westphal 			rcu_assign_pointer(promote->ifa_next, last_sec);
445ac28b1ecSLiu Jian 			rcu_assign_pointer(*last_prim, promote);
4460ff60a45SJamal Hadi Salim 		}
4470ff60a45SJamal Hadi Salim 
4480ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
44915e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
450e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
451e041c683SAlan Stern 				NETDEV_UP, promote);
4522638eb8bSFlorian Westphal 		for (ifa = next_sec; ifa;
4532638eb8bSFlorian Westphal 		     ifa = rtnl_dereference(ifa->ifa_next)) {
4540ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4550ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4560ff60a45SJamal Hadi Salim 					continue;
4570ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4580ff60a45SJamal Hadi Salim 		}
4590ff60a45SJamal Hadi Salim 
4600ff60a45SJamal Hadi Salim 	}
4616363097cSHerbert Xu 	if (destroy)
4621da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
inet_del_ifa(struct in_device * in_dev,struct in_ifaddr __rcu ** ifap,int destroy)4652638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev,
4662638eb8bSFlorian Westphal 			 struct in_ifaddr __rcu **ifap,
467d6062cbbSThomas Graf 			 int destroy)
468d6062cbbSThomas Graf {
469d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
470d6062cbbSThomas Graf }
471d6062cbbSThomas Graf 
4725c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4735c766d64SJiri Pirko 
4745c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4755c766d64SJiri Pirko 
__inet_insert_ifa(struct in_ifaddr * ifa,struct nlmsghdr * nlh,u32 portid,struct netlink_ext_ack * extack)476d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
477de95e047SDavid Ahern 			     u32 portid, struct netlink_ext_ack *extack)
4781da177e4SLinus Torvalds {
4792638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **last_primary, **ifap;
4801da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4813ad7d246SKrister Johansen 	struct in_validator_info ivi;
4822638eb8bSFlorian Westphal 	struct in_ifaddr *ifa1;
4833ad7d246SKrister Johansen 	int ret;
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	ASSERT_RTNL();
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4881da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4891da177e4SLinus Torvalds 		return 0;
4901da177e4SLinus Torvalds 	}
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4931da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4941da177e4SLinus Torvalds 
4952e605463SMatteo Croce 	/* Don't set IPv6 only flags to IPv4 addresses */
4962e605463SMatteo Croce 	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
4972e605463SMatteo Croce 
4982638eb8bSFlorian Westphal 	ifap = &in_dev->ifa_list;
4992638eb8bSFlorian Westphal 	ifa1 = rtnl_dereference(*ifap);
5002638eb8bSFlorian Westphal 
5012638eb8bSFlorian Westphal 	while (ifa1) {
5021da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
5031da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
5041da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
5051da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
5061da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
5071da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
5081da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5091da177e4SLinus Torvalds 				return -EEXIST;
5101da177e4SLinus Torvalds 			}
5111da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
512b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value");
5131da177e4SLinus Torvalds 				inet_free_ifa(ifa);
5141da177e4SLinus Torvalds 				return -EINVAL;
5151da177e4SLinus Torvalds 			}
5161da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
5171da177e4SLinus Torvalds 		}
5182638eb8bSFlorian Westphal 
5192638eb8bSFlorian Westphal 		ifap = &ifa1->ifa_next;
5202638eb8bSFlorian Westphal 		ifa1 = rtnl_dereference(*ifap);
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 
5233ad7d246SKrister Johansen 	/* Allow any devices that wish to register ifaddr validtors to weigh
5243ad7d246SKrister Johansen 	 * in now, before changes are committed.  The rntl lock is serializing
5253ad7d246SKrister Johansen 	 * access here, so the state should not change between a validator call
5263ad7d246SKrister Johansen 	 * and a final notify on commit.  This isn't invoked on promotion under
5273ad7d246SKrister Johansen 	 * the assumption that validators are checking the address itself, and
5283ad7d246SKrister Johansen 	 * not the flags.
5293ad7d246SKrister Johansen 	 */
5303ad7d246SKrister Johansen 	ivi.ivi_addr = ifa->ifa_address;
5313ad7d246SKrister Johansen 	ivi.ivi_dev = ifa->ifa_dev;
532de95e047SDavid Ahern 	ivi.extack = extack;
5333ad7d246SKrister Johansen 	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
5343ad7d246SKrister Johansen 					   NETDEV_UP, &ivi);
5353ad7d246SKrister Johansen 	ret = notifier_to_errno(ret);
5363ad7d246SKrister Johansen 	if (ret) {
5373ad7d246SKrister Johansen 		inet_free_ifa(ifa);
5383ad7d246SKrister Johansen 		return ret;
5393ad7d246SKrister Johansen 	}
5403ad7d246SKrister Johansen 
541d4150779SJason A. Donenfeld 	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
5421da177e4SLinus Torvalds 		ifap = last_primary;
5431da177e4SLinus Torvalds 
5442638eb8bSFlorian Westphal 	rcu_assign_pointer(ifa->ifa_next, *ifap);
5452638eb8bSFlorian Westphal 	rcu_assign_pointer(*ifap, ifa);
5461da177e4SLinus Torvalds 
547fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
548fd23c3b3SDavid S. Miller 
5495c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
550906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
5515c766d64SJiri Pirko 
5521da177e4SLinus Torvalds 	/* Send message first, then call notifier.
5531da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
5541da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
55515e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
556e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	return 0;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds 
inet_insert_ifa(struct in_ifaddr * ifa)561d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
562d6062cbbSThomas Graf {
563de95e047SDavid Ahern 	return __inet_insert_ifa(ifa, NULL, 0, NULL);
564d6062cbbSThomas Graf }
565d6062cbbSThomas Graf 
inet_set_ifa(struct net_device * dev,struct in_ifaddr * ifa)5661da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
5671da177e4SLinus Torvalds {
568e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 	ASSERT_RTNL();
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds 	if (!in_dev) {
5731da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5741da177e4SLinus Torvalds 		return -ENOBUFS;
5751da177e4SLinus Torvalds 	}
57671e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5771d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5781da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
579547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5801da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5811da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5821da177e4SLinus Torvalds 	}
583f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5841da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5851da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5861da177e4SLinus Torvalds }
5871da177e4SLinus Torvalds 
5888723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5898723e1b4SEric Dumazet  * We dont take a reference on found in_device
5908723e1b4SEric Dumazet  */
inetdev_by_index(struct net * net,int ifindex)5917fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5921da177e4SLinus Torvalds {
5931da177e4SLinus Torvalds 	struct net_device *dev;
5941da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
595c148fc2eSEric Dumazet 
596c148fc2eSEric Dumazet 	rcu_read_lock();
597c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5981da177e4SLinus Torvalds 	if (dev)
5998723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
600c148fc2eSEric Dumazet 	rcu_read_unlock();
6011da177e4SLinus Torvalds 	return in_dev;
6021da177e4SLinus Torvalds }
6039f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
6061da177e4SLinus Torvalds 
inet_ifa_byprefix(struct in_device * in_dev,__be32 prefix,__be32 mask)60760cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
60860cad5daSAl Viro 				    __be32 mask)
6091da177e4SLinus Torvalds {
610d519e870SFlorian Westphal 	struct in_ifaddr *ifa;
611d519e870SFlorian Westphal 
6121da177e4SLinus Torvalds 	ASSERT_RTNL();
6131da177e4SLinus Torvalds 
614d519e870SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
6151da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
6161da177e4SLinus Torvalds 			return ifa;
617d519e870SFlorian Westphal 	}
6181da177e4SLinus Torvalds 	return NULL;
6191da177e4SLinus Torvalds }
6201da177e4SLinus Torvalds 
ip_mc_autojoin_config(struct net * net,bool join,const struct in_ifaddr * ifa)621690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join,
622690cc863STaras Chornyi 				 const struct in_ifaddr *ifa)
62393a714d6SMadhu Challa {
624690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST)
62593a714d6SMadhu Challa 	struct ip_mreqn mreq = {
62693a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
62793a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
62893a714d6SMadhu Challa 	};
629690cc863STaras Chornyi 	struct sock *sk = net->ipv4.mc_autojoin_sk;
63093a714d6SMadhu Challa 	int ret;
63193a714d6SMadhu Challa 
63293a714d6SMadhu Challa 	ASSERT_RTNL();
63393a714d6SMadhu Challa 
63493a714d6SMadhu Challa 	lock_sock(sk);
63593a714d6SMadhu Challa 	if (join)
63654ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
63793a714d6SMadhu Challa 	else
63854ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
63993a714d6SMadhu Challa 	release_sock(sk);
64093a714d6SMadhu Challa 
64193a714d6SMadhu Challa 	return ret;
642690cc863STaras Chornyi #else
643690cc863STaras Chornyi 	return -EOPNOTSUPP;
644690cc863STaras Chornyi #endif
64593a714d6SMadhu Challa }
64693a714d6SMadhu Challa 
inet_rtm_deladdr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)647c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
648c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
6491da177e4SLinus Torvalds {
6503b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6512638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap;
652dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6531da177e4SLinus Torvalds 	struct in_device *in_dev;
654dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
6552638eb8bSFlorian Westphal 	struct in_ifaddr *ifa;
65630e2379eSMenglong Dong 	int err;
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 	ASSERT_RTNL();
6591da177e4SLinus Torvalds 
6608cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
6618cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
662dfdd5fd4SThomas Graf 	if (err < 0)
663dfdd5fd4SThomas Graf 		goto errout;
664dfdd5fd4SThomas Graf 
665dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
6667fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
66751456b29SIan Morris 	if (!in_dev) {
668b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
669dfdd5fd4SThomas Graf 		err = -ENODEV;
670dfdd5fd4SThomas Graf 		goto errout;
671dfdd5fd4SThomas Graf 	}
672dfdd5fd4SThomas Graf 
6732638eb8bSFlorian Westphal 	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
6741da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
675dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
67667b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
6771da177e4SLinus Torvalds 			continue;
678dfdd5fd4SThomas Graf 
679dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
680dfdd5fd4SThomas Graf 			continue;
681dfdd5fd4SThomas Graf 
682dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
683dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
68467b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
685dfdd5fd4SThomas Graf 			continue;
686dfdd5fd4SThomas Graf 
68793a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
688690cc863STaras Chornyi 			ip_mc_autojoin_config(net, false, ifa);
68915e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6901da177e4SLinus Torvalds 		return 0;
6911da177e4SLinus Torvalds 	}
692dfdd5fd4SThomas Graf 
693b4672c73SHangbin Liu 	NL_SET_ERR_MSG(extack, "ipv4: Address not found");
694dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
695dfdd5fd4SThomas Graf errout:
696dfdd5fd4SThomas Graf 	return err;
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds 
6995c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
7005c766d64SJiri Pirko 
check_lifetime(struct work_struct * work)7015c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
7025c766d64SJiri Pirko {
7035c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
7045c766d64SJiri Pirko 	struct in_ifaddr *ifa;
705c988d1e8SJiri Pirko 	struct hlist_node *n;
7065c766d64SJiri Pirko 	int i;
7075c766d64SJiri Pirko 
7085c766d64SJiri Pirko 	now = jiffies;
7095c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
7105c766d64SJiri Pirko 
7115c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
712c988d1e8SJiri Pirko 		bool change_needed = false;
713c988d1e8SJiri Pirko 
714c988d1e8SJiri Pirko 		rcu_read_lock();
715b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
7165c766d64SJiri Pirko 			unsigned long age;
7175c766d64SJiri Pirko 
7185c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
7195c766d64SJiri Pirko 				continue;
7205c766d64SJiri Pirko 
7215c766d64SJiri Pirko 			/* We try to batch several events at once. */
7225c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
7235c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
7245c766d64SJiri Pirko 
7255c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
7265c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
727c988d1e8SJiri Pirko 				change_needed = true;
728c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
729c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
730c988d1e8SJiri Pirko 				continue;
731c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
732c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
733c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
734c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
735c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
736c988d1e8SJiri Pirko 
737c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
738c988d1e8SJiri Pirko 					change_needed = true;
739c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
740c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
741c988d1e8SJiri Pirko 					       next)) {
742c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
743c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
744c988d1e8SJiri Pirko 			}
745c988d1e8SJiri Pirko 		}
746c988d1e8SJiri Pirko 		rcu_read_unlock();
747c988d1e8SJiri Pirko 		if (!change_needed)
748c988d1e8SJiri Pirko 			continue;
749c988d1e8SJiri Pirko 		rtnl_lock();
750c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
751c988d1e8SJiri Pirko 			unsigned long age;
752c988d1e8SJiri Pirko 
753c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
754c988d1e8SJiri Pirko 				continue;
755c988d1e8SJiri Pirko 
756c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
757c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
758c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
759c988d1e8SJiri Pirko 
760c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
761c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
7622638eb8bSFlorian Westphal 				struct in_ifaddr __rcu **ifap;
7632638eb8bSFlorian Westphal 				struct in_ifaddr *tmp;
7645c766d64SJiri Pirko 
7652638eb8bSFlorian Westphal 				ifap = &ifa->ifa_dev->ifa_list;
7662638eb8bSFlorian Westphal 				tmp = rtnl_dereference(*ifap);
7672638eb8bSFlorian Westphal 				while (tmp) {
76840008e92SFlorian Westphal 					if (tmp == ifa) {
7695c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
7705c766d64SJiri Pirko 							     ifap, 1);
771c988d1e8SJiri Pirko 						break;
7725c766d64SJiri Pirko 					}
7732638eb8bSFlorian Westphal 					ifap = &tmp->ifa_next;
7742638eb8bSFlorian Westphal 					tmp = rtnl_dereference(*ifap);
775c988d1e8SJiri Pirko 				}
776c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
777c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
778c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
779c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
7805c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
7815c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
7825c766d64SJiri Pirko 			}
7835c766d64SJiri Pirko 		}
784c988d1e8SJiri Pirko 		rtnl_unlock();
7855c766d64SJiri Pirko 	}
7865c766d64SJiri Pirko 
7875c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7885c766d64SJiri Pirko 	next_sched = next;
7895c766d64SJiri Pirko 
7905c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7915c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7925c766d64SJiri Pirko 		next_sched = next_sec;
7935c766d64SJiri Pirko 
7945c766d64SJiri Pirko 	now = jiffies;
7955c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7965c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7975c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7985c766d64SJiri Pirko 
799906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
800906e073fSviresh kumar 			next_sched - now);
8015c766d64SJiri Pirko }
8025c766d64SJiri Pirko 
set_ifa_lifetime(struct in_ifaddr * ifa,__u32 valid_lft,__u32 prefered_lft)8035c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
8045c766d64SJiri Pirko 			     __u32 prefered_lft)
8055c766d64SJiri Pirko {
8065c766d64SJiri Pirko 	unsigned long timeout;
8075c766d64SJiri Pirko 
8085c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
8095c766d64SJiri Pirko 
8105c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
8115c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
8125c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
8135c766d64SJiri Pirko 	else
8145c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
8155c766d64SJiri Pirko 
8165c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
8175c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
8185c766d64SJiri Pirko 		if (timeout == 0)
8195c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
8205c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
8215c766d64SJiri Pirko 	}
8225c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
8235c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
8245c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
8255c766d64SJiri Pirko }
8265c766d64SJiri Pirko 
rtm_to_ifaddr(struct net * net,struct nlmsghdr * nlh,__u32 * pvalid_lft,__u32 * pprefered_lft,struct netlink_ext_ack * extack)8275c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
828dac9c979SDavid Ahern 				       __u32 *pvalid_lft, __u32 *pprefered_lft,
829dac9c979SDavid Ahern 				       struct netlink_ext_ack *extack)
8301da177e4SLinus Torvalds {
8315c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
8325c753978SThomas Graf 	struct in_ifaddr *ifa;
8335c753978SThomas Graf 	struct ifaddrmsg *ifm;
8341da177e4SLinus Torvalds 	struct net_device *dev;
8351da177e4SLinus Torvalds 	struct in_device *in_dev;
8367b218574SDenis V. Lunev 	int err;
8371da177e4SLinus Torvalds 
8388cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
8398cb08174SJohannes Berg 				     ifa_ipv4_policy, extack);
8405c753978SThomas Graf 	if (err < 0)
8415c753978SThomas Graf 		goto errout;
8421da177e4SLinus Torvalds 
8435c753978SThomas Graf 	ifm = nlmsg_data(nlh);
844c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
845b4672c73SHangbin Liu 
846b4672c73SHangbin Liu 	if (ifm->ifa_prefixlen > 32) {
847b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length");
8485c753978SThomas Graf 		goto errout;
849b4672c73SHangbin Liu 	}
850b4672c73SHangbin Liu 
851b4672c73SHangbin Liu 	if (!tb[IFA_LOCAL]) {
852b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied");
853b4672c73SHangbin Liu 		goto errout;
854b4672c73SHangbin Liu 	}
8551da177e4SLinus Torvalds 
8564b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
8575c753978SThomas Graf 	err = -ENODEV;
858b4672c73SHangbin Liu 	if (!dev) {
859b4672c73SHangbin Liu 		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
8605c753978SThomas Graf 		goto errout;
861b4672c73SHangbin Liu 	}
8621da177e4SLinus Torvalds 
8635c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
8645c753978SThomas Graf 	err = -ENOBUFS;
86551456b29SIan Morris 	if (!in_dev)
8665c753978SThomas Graf 		goto errout;
86771e27da9SHerbert Xu 
8685c753978SThomas Graf 	ifa = inet_alloc_ifa();
86951456b29SIan Morris 	if (!ifa)
8705c753978SThomas Graf 		/*
8715c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
8725c753978SThomas Graf 		 * assigned to its device and is destroy with it.
8735c753978SThomas Graf 		 */
8745c753978SThomas Graf 		goto errout;
8755c753978SThomas Graf 
876a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
8771d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
8785c753978SThomas Graf 	in_dev_hold(in_dev);
8795c753978SThomas Graf 
88051456b29SIan Morris 	if (!tb[IFA_ADDRESS])
8815c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
8825c753978SThomas Graf 
883fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
8841da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
8851da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
886ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
887ad6c8135SJiri Pirko 					 ifm->ifa_flags;
8881da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
8891da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
8905c753978SThomas Graf 
89167b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
89267b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
8935c753978SThomas Graf 
8945c753978SThomas Graf 	if (tb[IFA_BROADCAST])
89567b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
8965c753978SThomas Graf 
8975c753978SThomas Graf 	if (tb[IFA_LABEL])
898872f6903SFrancis Laniel 		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8991da177e4SLinus Torvalds 	else
9001da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
9011da177e4SLinus Torvalds 
902af4d768aSDavid Ahern 	if (tb[IFA_RT_PRIORITY])
903af4d768aSDavid Ahern 		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
904af4d768aSDavid Ahern 
90547f0bd50SJacques de Laval 	if (tb[IFA_PROTO])
90647f0bd50SJacques de Laval 		ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
90747f0bd50SJacques de Laval 
9085c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
9095c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
9105c766d64SJiri Pirko 
9115c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
9125c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
913b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid");
9145c766d64SJiri Pirko 			err = -EINVAL;
915446266b0SDaniel Borkmann 			goto errout_free;
9165c766d64SJiri Pirko 		}
9175c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
9185c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
9195c766d64SJiri Pirko 	}
9205c766d64SJiri Pirko 
9215c753978SThomas Graf 	return ifa;
9225c753978SThomas Graf 
923446266b0SDaniel Borkmann errout_free:
924446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
9255c753978SThomas Graf errout:
9265c753978SThomas Graf 	return ERR_PTR(err);
9275c753978SThomas Graf }
9285c753978SThomas Graf 
find_matching_ifa(struct in_ifaddr * ifa)9295c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
9305c766d64SJiri Pirko {
9315c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
932ef11db33SFlorian Westphal 	struct in_ifaddr *ifa1;
9335c766d64SJiri Pirko 
9345c766d64SJiri Pirko 	if (!ifa->ifa_local)
9355c766d64SJiri Pirko 		return NULL;
9365c766d64SJiri Pirko 
937ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
9385c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
9395c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
9405c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
9415c766d64SJiri Pirko 			return ifa1;
9425c766d64SJiri Pirko 	}
9435c766d64SJiri Pirko 	return NULL;
9445c766d64SJiri Pirko }
9455c766d64SJiri Pirko 
inet_rtm_newaddr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)946c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
947c21ef3e3SDavid Ahern 			    struct netlink_ext_ack *extack)
9485c753978SThomas Graf {
9493b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
9505c753978SThomas Graf 	struct in_ifaddr *ifa;
9515c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
9525c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
9535c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
9545c753978SThomas Graf 
9555c753978SThomas Graf 	ASSERT_RTNL();
9565c753978SThomas Graf 
957dac9c979SDavid Ahern 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
9585c753978SThomas Graf 	if (IS_ERR(ifa))
9595c753978SThomas Graf 		return PTR_ERR(ifa);
9605c753978SThomas Graf 
9615c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
9625c766d64SJiri Pirko 	if (!ifa_existing) {
9635c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
964614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
9655c766d64SJiri Pirko 		 */
9665c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
96793a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
968690cc863STaras Chornyi 			int ret = ip_mc_autojoin_config(net, true, ifa);
96993a714d6SMadhu Challa 
97093a714d6SMadhu Challa 			if (ret < 0) {
971b4672c73SHangbin Liu 				NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed");
97293a714d6SMadhu Challa 				inet_free_ifa(ifa);
97393a714d6SMadhu Challa 				return ret;
97493a714d6SMadhu Challa 			}
97593a714d6SMadhu Challa 		}
976de95e047SDavid Ahern 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
977de95e047SDavid Ahern 					 extack);
9785c766d64SJiri Pirko 	} else {
979af4d768aSDavid Ahern 		u32 new_metric = ifa->ifa_rt_priority;
9805c4a9aa8SPetr Machata 		u8 new_proto = ifa->ifa_proto;
981af4d768aSDavid Ahern 
9825c766d64SJiri Pirko 		inet_free_ifa(ifa);
9835c766d64SJiri Pirko 
9845c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
985b4672c73SHangbin Liu 		    !(nlh->nlmsg_flags & NLM_F_REPLACE)) {
986b4672c73SHangbin Liu 			NL_SET_ERR_MSG(extack, "ipv4: Address already assigned");
9875c766d64SJiri Pirko 			return -EEXIST;
988b4672c73SHangbin Liu 		}
98934e2ed34SJiri Pirko 		ifa = ifa_existing;
990af4d768aSDavid Ahern 
991af4d768aSDavid Ahern 		if (ifa->ifa_rt_priority != new_metric) {
992af4d768aSDavid Ahern 			fib_modify_prefix_metric(ifa, new_metric);
993af4d768aSDavid Ahern 			ifa->ifa_rt_priority = new_metric;
994af4d768aSDavid Ahern 		}
995af4d768aSDavid Ahern 
9965c4a9aa8SPetr Machata 		ifa->ifa_proto = new_proto;
9975c4a9aa8SPetr Machata 
99834e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
99905a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
1000906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
1001906e073fSviresh kumar 				&check_lifetime_work, 0);
100234e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
10035c766d64SJiri Pirko 	}
10045c766d64SJiri Pirko 	return 0;
10051da177e4SLinus Torvalds }
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds /*
10081da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
10091da177e4SLinus Torvalds  */
10101da177e4SLinus Torvalds 
inet_abc_len(__be32 addr)101140384999SEric Dumazet static int inet_abc_len(__be32 addr)
10121da177e4SLinus Torvalds {
10131da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
10141da177e4SLinus Torvalds 
101565cab850SDave Taht 	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
10161da177e4SLinus Torvalds 		rc = 0;
10171da177e4SLinus Torvalds 	else {
1018714e85beSAl Viro 		__u32 haddr = ntohl(addr);
1019714e85beSAl Viro 		if (IN_CLASSA(haddr))
10201da177e4SLinus Torvalds 			rc = 8;
1021714e85beSAl Viro 		else if (IN_CLASSB(haddr))
10221da177e4SLinus Torvalds 			rc = 16;
1023714e85beSAl Viro 		else if (IN_CLASSC(haddr))
10241da177e4SLinus Torvalds 			rc = 24;
102565cab850SDave Taht 		else if (IN_CLASSE(haddr))
102665cab850SDave Taht 			rc = 32;
10271da177e4SLinus Torvalds 	}
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds 	return rc;
10301da177e4SLinus Torvalds }
10311da177e4SLinus Torvalds 
10321da177e4SLinus Torvalds 
devinet_ioctl(struct net * net,unsigned int cmd,struct ifreq * ifr)103303aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
10341da177e4SLinus Torvalds {
10351da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
103603aef17bSAl Viro 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
10372638eb8bSFlorian Westphal 	struct in_ifaddr __rcu **ifap = NULL;
10381da177e4SLinus Torvalds 	struct in_device *in_dev;
10391da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
10401da177e4SLinus Torvalds 	struct net_device *dev;
10411da177e4SLinus Torvalds 	char *colon;
10421da177e4SLinus Torvalds 	int ret = -EFAULT;
10431da177e4SLinus Torvalds 	int tryaddrmatch = 0;
10441da177e4SLinus Torvalds 
104503aef17bSAl Viro 	ifr->ifr_name[IFNAMSIZ - 1] = 0;
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	/* save original address for comparison */
10481da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
10491da177e4SLinus Torvalds 
105003aef17bSAl Viro 	colon = strchr(ifr->ifr_name, ':');
10511da177e4SLinus Torvalds 	if (colon)
10521da177e4SLinus Torvalds 		*colon = 0;
10531da177e4SLinus Torvalds 
105403aef17bSAl Viro 	dev_load(net, ifr->ifr_name);
10551da177e4SLinus Torvalds 
10561da177e4SLinus Torvalds 	switch (cmd) {
10571da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10581da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10591da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10601da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10611da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
10621da177e4SLinus Torvalds 		   so that we do not impose a lock.
10631da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
10641da177e4SLinus Torvalds 		 */
10651da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
10661da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
10671da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
10681da177e4SLinus Torvalds 		break;
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
1071bf5b30b8SZhao Hongjiang 		ret = -EPERM;
107252e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10731da177e4SLinus Torvalds 			goto out;
10741da177e4SLinus Torvalds 		break;
10751da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10761da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10771da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10781da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
1079bf5b30b8SZhao Hongjiang 		ret = -EPERM;
108052e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
10811da177e4SLinus Torvalds 			goto out;
10821da177e4SLinus Torvalds 		ret = -EINVAL;
10831da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
10841da177e4SLinus Torvalds 			goto out;
10851da177e4SLinus Torvalds 		break;
10861da177e4SLinus Torvalds 	default:
10871da177e4SLinus Torvalds 		ret = -EINVAL;
10881da177e4SLinus Torvalds 		goto out;
10891da177e4SLinus Torvalds 	}
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds 	rtnl_lock();
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 	ret = -ENODEV;
109403aef17bSAl Viro 	dev = __dev_get_by_name(net, ifr->ifr_name);
10959f9354b9SEric Dumazet 	if (!dev)
10961da177e4SLinus Torvalds 		goto done;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	if (colon)
10991da177e4SLinus Torvalds 		*colon = ':';
11001da177e4SLinus Torvalds 
11019f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
11029f9354b9SEric Dumazet 	if (in_dev) {
11031da177e4SLinus Torvalds 		if (tryaddrmatch) {
11041da177e4SLinus Torvalds 			/* Matthias Andree */
11051da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
11061da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
11071da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
11081da177e4SLinus Torvalds 			   This is checked above. */
11092638eb8bSFlorian Westphal 
11102638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11112638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11121da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
111303aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
11141da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
11156c91afe1SDavid S. Miller 							ifa->ifa_local) {
11161da177e4SLinus Torvalds 					break; /* found */
11171da177e4SLinus Torvalds 				}
11181da177e4SLinus Torvalds 			}
11191da177e4SLinus Torvalds 		}
11201da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
11211da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
11221da177e4SLinus Torvalds 		   comparing just the label */
11231da177e4SLinus Torvalds 		if (!ifa) {
11242638eb8bSFlorian Westphal 			for (ifap = &in_dev->ifa_list;
11252638eb8bSFlorian Westphal 			     (ifa = rtnl_dereference(*ifap)) != NULL;
11261da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
112703aef17bSAl Viro 				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
11281da177e4SLinus Torvalds 					break;
11291da177e4SLinus Torvalds 		}
11301da177e4SLinus Torvalds 	}
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
11331da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
11341da177e4SLinus Torvalds 		goto done;
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	switch (cmd) {
11371da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
113830e948a3STonghao Zhang 		ret = 0;
11391da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
114003aef17bSAl Viro 		break;
11411da177e4SLinus Torvalds 
11421da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
114330e948a3STonghao Zhang 		ret = 0;
11441da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
114503aef17bSAl Viro 		break;
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
114830e948a3STonghao Zhang 		ret = 0;
11491da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
115003aef17bSAl Viro 		break;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
115330e948a3STonghao Zhang 		ret = 0;
11541da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
115503aef17bSAl Viro 		break;
11561da177e4SLinus Torvalds 
11571da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
11581da177e4SLinus Torvalds 		if (colon) {
11591da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
11601da177e4SLinus Torvalds 			if (!ifa)
11611da177e4SLinus Torvalds 				break;
11621da177e4SLinus Torvalds 			ret = 0;
116303aef17bSAl Viro 			if (!(ifr->ifr_flags & IFF_UP))
11641da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
11651da177e4SLinus Torvalds 			break;
11661da177e4SLinus Torvalds 		}
1167567c5e13SPetr Machata 		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
11681da177e4SLinus Torvalds 		break;
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
11711da177e4SLinus Torvalds 		ret = -EINVAL;
11721da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11731da177e4SLinus Torvalds 			break;
11741da177e4SLinus Torvalds 
11751da177e4SLinus Torvalds 		if (!ifa) {
11761da177e4SLinus Torvalds 			ret = -ENOBUFS;
11779f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
11789f9354b9SEric Dumazet 			if (!ifa)
11791da177e4SLinus Torvalds 				break;
1180c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
11811da177e4SLinus Torvalds 			if (colon)
118203aef17bSAl Viro 				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
11831da177e4SLinus Torvalds 			else
11841da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11851da177e4SLinus Torvalds 		} else {
11861da177e4SLinus Torvalds 			ret = 0;
11871da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
11881da177e4SLinus Torvalds 				break;
11891da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11901da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1191148f9729SBjorn Mork 			ifa->ifa_scope = 0;
11921da177e4SLinus Torvalds 		}
11931da177e4SLinus Torvalds 
11941da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
11951da177e4SLinus Torvalds 
11961da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
11971da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
11981da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
11991da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12001da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
12011da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
12021da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
12031da177e4SLinus Torvalds 		} else {
12041da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
12051da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
12061da177e4SLinus Torvalds 		}
12075c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
12081da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
12091da177e4SLinus Torvalds 		break;
12101da177e4SLinus Torvalds 
12111da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
12121da177e4SLinus Torvalds 		ret = 0;
12131da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
12141da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12151da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
12161da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12171da177e4SLinus Torvalds 		}
12181da177e4SLinus Torvalds 		break;
12191da177e4SLinus Torvalds 
12201da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
12211da177e4SLinus Torvalds 		ret = 0;
12221da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
12231da177e4SLinus Torvalds 			break;
12241da177e4SLinus Torvalds 		ret = -EINVAL;
12251da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
12261da177e4SLinus Torvalds 			break;
12271da177e4SLinus Torvalds 		ret = 0;
12281da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
12291da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
12301da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
12311da177e4SLinus Torvalds 		break;
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
12341da177e4SLinus Torvalds 
12351da177e4SLinus Torvalds 		/*
12361da177e4SLinus Torvalds 		 *	The mask we set must be legal.
12371da177e4SLinus Torvalds 		 */
12381da177e4SLinus Torvalds 		ret = -EINVAL;
12391da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
12401da177e4SLinus Torvalds 			break;
12411da177e4SLinus Torvalds 		ret = 0;
12421da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1243a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
12441da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
12451da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
12461da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
12471da177e4SLinus Torvalds 
12481da177e4SLinus Torvalds 			/* See if current broadcast address matches
12491da177e4SLinus Torvalds 			 * with current netmask, then recalculate
12501da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
12511da177e4SLinus Torvalds 			 * funny address, so don't touch it since
12521da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
12531da177e4SLinus Torvalds 			 */
12541da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
12551da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
12561da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1257dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
12581da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
12591da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
12601da177e4SLinus Torvalds 			}
12611da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
12621da177e4SLinus Torvalds 		}
12631da177e4SLinus Torvalds 		break;
12641da177e4SLinus Torvalds 	}
12651da177e4SLinus Torvalds done:
12661da177e4SLinus Torvalds 	rtnl_unlock();
12671da177e4SLinus Torvalds out:
12681da177e4SLinus Torvalds 	return ret;
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds 
inet_gifconf(struct net_device * dev,char __user * buf,int len,int size)1271b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
12721da177e4SLinus Torvalds {
1273e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1274ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
12751da177e4SLinus Torvalds 	struct ifreq ifr;
12761da177e4SLinus Torvalds 	int done = 0;
12771da177e4SLinus Torvalds 
127836fd633eSAl Viro 	if (WARN_ON(size > sizeof(struct ifreq)))
127936fd633eSAl Viro 		goto out;
128036fd633eSAl Viro 
12819f9354b9SEric Dumazet 	if (!in_dev)
12821da177e4SLinus Torvalds 		goto out;
12831da177e4SLinus Torvalds 
1284ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
12851da177e4SLinus Torvalds 		if (!buf) {
128636fd633eSAl Viro 			done += size;
12871da177e4SLinus Torvalds 			continue;
12881da177e4SLinus Torvalds 		}
128936fd633eSAl Viro 		if (len < size)
12901da177e4SLinus Torvalds 			break;
12911da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
12921da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
12951da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
12961da177e4SLinus Torvalds 								ifa->ifa_local;
12971da177e4SLinus Torvalds 
129836fd633eSAl Viro 		if (copy_to_user(buf + done, &ifr, size)) {
12991da177e4SLinus Torvalds 			done = -EFAULT;
13001da177e4SLinus Torvalds 			break;
13011da177e4SLinus Torvalds 		}
130236fd633eSAl Viro 		len  -= size;
130336fd633eSAl Viro 		done += size;
13041da177e4SLinus Torvalds 	}
13051da177e4SLinus Torvalds out:
13061da177e4SLinus Torvalds 	return done;
13071da177e4SLinus Torvalds }
13081da177e4SLinus Torvalds 
in_dev_select_addr(const struct in_device * in_dev,int scope)13098b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev,
13108b57fd1eSGao Feng 				 int scope)
13118b57fd1eSGao Feng {
1312d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1313d519e870SFlorian Westphal 
1314d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1315d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1316d519e870SFlorian Westphal 			continue;
13178b57fd1eSGao Feng 		if (ifa->ifa_scope != RT_SCOPE_LINK &&
13188b57fd1eSGao Feng 		    ifa->ifa_scope <= scope)
13198b57fd1eSGao Feng 			return ifa->ifa_local;
1320d519e870SFlorian Westphal 	}
13218b57fd1eSGao Feng 
13228b57fd1eSGao Feng 	return 0;
13238b57fd1eSGao Feng }
13248b57fd1eSGao Feng 
inet_select_addr(const struct net_device * dev,__be32 dst,int scope)1325a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
13261da177e4SLinus Torvalds {
1327d519e870SFlorian Westphal 	const struct in_ifaddr *ifa;
1328a61ced5dSAl Viro 	__be32 addr = 0;
1329d8c444d5SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
13301da177e4SLinus Torvalds 	struct in_device *in_dev;
1331c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
13323f2fb9a8SDavid Ahern 	int master_idx;
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds 	rcu_read_lock();
1335e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
13361da177e4SLinus Torvalds 	if (!in_dev)
13371da177e4SLinus Torvalds 		goto no_in_dev;
13381da177e4SLinus Torvalds 
1339d8c444d5SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1340d8c444d5SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1341d8c444d5SShijie Luo 
1342d519e870SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1343d519e870SFlorian Westphal 		if (ifa->ifa_flags & IFA_F_SECONDARY)
1344d519e870SFlorian Westphal 			continue;
1345d8c444d5SShijie Luo 		if (min(ifa->ifa_scope, localnet_scope) > scope)
13461da177e4SLinus Torvalds 			continue;
13471da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
13481da177e4SLinus Torvalds 			addr = ifa->ifa_local;
13491da177e4SLinus Torvalds 			break;
13501da177e4SLinus Torvalds 		}
13511da177e4SLinus Torvalds 		if (!addr)
13521da177e4SLinus Torvalds 			addr = ifa->ifa_local;
1353d519e870SFlorian Westphal 	}
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 	if (addr)
1356c6d14c84SEric Dumazet 		goto out_unlock;
13579f9354b9SEric Dumazet no_in_dev:
13583f2fb9a8SDavid Ahern 	master_idx = l3mdev_master_ifindex_rcu(dev);
13591da177e4SLinus Torvalds 
136017b693cdSDavid Lamparter 	/* For VRFs, the VRF device takes the place of the loopback device,
136117b693cdSDavid Lamparter 	 * with addresses on it being preferred.  Note in such cases the
136217b693cdSDavid Lamparter 	 * loopback device will be among the devices that fail the master_idx
136317b693cdSDavid Lamparter 	 * equality check in the loop below.
136417b693cdSDavid Lamparter 	 */
136517b693cdSDavid Lamparter 	if (master_idx &&
136617b693cdSDavid Lamparter 	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
136717b693cdSDavid Lamparter 	    (in_dev = __in_dev_get_rcu(dev))) {
13688b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13698b57fd1eSGao Feng 		if (addr)
137017b693cdSDavid Lamparter 			goto out_unlock;
137117b693cdSDavid Lamparter 	}
137217b693cdSDavid Lamparter 
13731da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1374ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
13751da177e4SLinus Torvalds 	   in dev_base list.
13761da177e4SLinus Torvalds 	 */
1377c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
13783f2fb9a8SDavid Ahern 		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
13793f2fb9a8SDavid Ahern 			continue;
13803f2fb9a8SDavid Ahern 
13819f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
13829f9354b9SEric Dumazet 		if (!in_dev)
13831da177e4SLinus Torvalds 			continue;
13841da177e4SLinus Torvalds 
13858b57fd1eSGao Feng 		addr = in_dev_select_addr(in_dev, scope);
13868b57fd1eSGao Feng 		if (addr)
1387c6d14c84SEric Dumazet 			goto out_unlock;
13881da177e4SLinus Torvalds 	}
1389c6d14c84SEric Dumazet out_unlock:
13901da177e4SLinus Torvalds 	rcu_read_unlock();
13911da177e4SLinus Torvalds 	return addr;
13921da177e4SLinus Torvalds }
13939f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
13941da177e4SLinus Torvalds 
confirm_addr_indev(struct in_device * in_dev,__be32 dst,__be32 local,int scope)139560cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
139660cad5daSAl Viro 			      __be32 local, int scope)
13971da177e4SLinus Torvalds {
1398650638a7SShijie Luo 	unsigned char localnet_scope = RT_SCOPE_HOST;
1399ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1400a144ea4bSAl Viro 	__be32 addr = 0;
1401ef11db33SFlorian Westphal 	int same = 0;
14021da177e4SLinus Torvalds 
1403650638a7SShijie Luo 	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
1404650638a7SShijie Luo 		localnet_scope = RT_SCOPE_LINK;
1405650638a7SShijie Luo 
1406ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rcu(ifa, in_dev) {
1407650638a7SShijie Luo 		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
1408650638a7SShijie Luo 
14091da177e4SLinus Torvalds 		if (!addr &&
14101da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
1411650638a7SShijie Luo 		    min_scope <= scope) {
14121da177e4SLinus Torvalds 			addr = ifa->ifa_local;
14131da177e4SLinus Torvalds 			if (same)
14141da177e4SLinus Torvalds 				break;
14151da177e4SLinus Torvalds 		}
14161da177e4SLinus Torvalds 		if (!same) {
14171da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
14181da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
14191da177e4SLinus Torvalds 			if (same && addr) {
14201da177e4SLinus Torvalds 				if (local || !dst)
14211da177e4SLinus Torvalds 					break;
14221da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
14231da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
14241da177e4SLinus Torvalds 					break;
14251da177e4SLinus Torvalds 				/* No, then can we use new local src? */
1426650638a7SShijie Luo 				if (min_scope <= scope) {
14271da177e4SLinus Torvalds 					addr = ifa->ifa_local;
14281da177e4SLinus Torvalds 					break;
14291da177e4SLinus Torvalds 				}
14301da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
14311da177e4SLinus Torvalds 				same = 0;
14321da177e4SLinus Torvalds 			}
14331da177e4SLinus Torvalds 		}
1434ef11db33SFlorian Westphal 	}
14351da177e4SLinus Torvalds 
14361da177e4SLinus Torvalds 	return same ? addr : 0;
14371da177e4SLinus Torvalds }
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds /*
14401da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1441b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1442b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
14431da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
14441da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
14451da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
14461da177e4SLinus Torvalds  */
inet_confirm_addr(struct net * net,struct in_device * in_dev,__be32 dst,__be32 local,int scope)1447b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
14489bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
14491da177e4SLinus Torvalds {
145060cad5daSAl Viro 	__be32 addr = 0;
14519bd85e32SDenis V. Lunev 	struct net_device *dev;
14521da177e4SLinus Torvalds 
145300db4124SIan Morris 	if (in_dev)
14549bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
14551da177e4SLinus Torvalds 
14561da177e4SLinus Torvalds 	rcu_read_lock();
1457c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
14589f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
14599f9354b9SEric Dumazet 		if (in_dev) {
14601da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
14611da177e4SLinus Torvalds 			if (addr)
14621da177e4SLinus Torvalds 				break;
14631da177e4SLinus Torvalds 		}
14641da177e4SLinus Torvalds 	}
14651da177e4SLinus Torvalds 	rcu_read_unlock();
14661da177e4SLinus Torvalds 
14671da177e4SLinus Torvalds 	return addr;
14681da177e4SLinus Torvalds }
1469eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
14701da177e4SLinus Torvalds 
14711da177e4SLinus Torvalds /*
14721da177e4SLinus Torvalds  *	Device notifier
14731da177e4SLinus Torvalds  */
14741da177e4SLinus Torvalds 
register_inetaddr_notifier(struct notifier_block * nb)14751da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
14761da177e4SLinus Torvalds {
1477e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
14781da177e4SLinus Torvalds }
14799f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
14801da177e4SLinus Torvalds 
unregister_inetaddr_notifier(struct notifier_block * nb)14811da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
14821da177e4SLinus Torvalds {
1483e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
14841da177e4SLinus Torvalds }
14859f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
14861da177e4SLinus Torvalds 
register_inetaddr_validator_notifier(struct notifier_block * nb)14873ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb)
14883ad7d246SKrister Johansen {
14893ad7d246SKrister Johansen 	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
14903ad7d246SKrister Johansen }
14913ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier);
14923ad7d246SKrister Johansen 
unregister_inetaddr_validator_notifier(struct notifier_block * nb)14933ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
14943ad7d246SKrister Johansen {
14953ad7d246SKrister Johansen 	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
14963ad7d246SKrister Johansen 	    nb);
14973ad7d246SKrister Johansen }
14983ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
14993ad7d246SKrister Johansen 
15009f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
15019f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
15021da177e4SLinus Torvalds */
inetdev_changename(struct net_device * dev,struct in_device * in_dev)15031da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
15041da177e4SLinus Torvalds {
15051da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
15061da177e4SLinus Torvalds 	int named = 0;
15071da177e4SLinus Torvalds 
1508ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
15091da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
15101da177e4SLinus Torvalds 
15111da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
15121da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15131da177e4SLinus Torvalds 		if (named++ == 0)
1514573bf470SThomas Graf 			goto skip;
151544344b2aSMark McLoughlin 		dot = strchr(old, ':');
151651456b29SIan Morris 		if (!dot) {
15171da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
15181da177e4SLinus Torvalds 			dot = old;
15191da177e4SLinus Torvalds 		}
15209f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
15211da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
15229f9354b9SEric Dumazet 		else
15231da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1524573bf470SThomas Graf skip:
1525573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
15261da177e4SLinus Torvalds 	}
15271da177e4SLinus Torvalds }
15281da177e4SLinus Torvalds 
inetdev_send_gratuitous_arp(struct net_device * dev,struct in_device * in_dev)1529d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1530d11327adSIan Campbell 					struct in_device *in_dev)
1531d11327adSIan Campbell 
1532d11327adSIan Campbell {
1533ef11db33SFlorian Westphal 	const struct in_ifaddr *ifa;
1534d11327adSIan Campbell 
1535ef11db33SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1536d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
15376c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
15386c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1539d11327adSIan Campbell 			 dev->dev_addr, NULL);
1540d11327adSIan Campbell 	}
1541b76d0789SZoltan Kiss }
1542d11327adSIan Campbell 
15431da177e4SLinus Torvalds /* Called only under RTNL semaphore */
15441da177e4SLinus Torvalds 
inetdev_event(struct notifier_block * this,unsigned long event,void * ptr)15451da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
15461da177e4SLinus Torvalds 			 void *ptr)
15471da177e4SLinus Torvalds {
1548351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1549748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
15501da177e4SLinus Torvalds 
15511da177e4SLinus Torvalds 	ASSERT_RTNL();
15521da177e4SLinus Torvalds 
15531da177e4SLinus Torvalds 	if (!in_dev) {
15548030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
15551da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
155620e61da7SWANG Cong 			if (IS_ERR(in_dev))
155720e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
15580cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
155942f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
156042f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
15611da177e4SLinus Torvalds 			}
156206770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
156306770843SBreno Leitao 			/* Re-enabling IP */
156406770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
156506770843SBreno Leitao 				in_dev = inetdev_init(dev);
15668030f544SHerbert Xu 		}
15671da177e4SLinus Torvalds 		goto out;
15681da177e4SLinus Torvalds 	}
15691da177e4SLinus Torvalds 
15701da177e4SLinus Torvalds 	switch (event) {
15711da177e4SLinus Torvalds 	case NETDEV_REGISTER:
157291df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1573a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
15741da177e4SLinus Torvalds 		break;
15751da177e4SLinus Torvalds 	case NETDEV_UP:
157606770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
15771da177e4SLinus Torvalds 			break;
15780cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
15799f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
15809f9354b9SEric Dumazet 
15819f9354b9SEric Dumazet 			if (ifa) {
1582fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
15831da177e4SLinus Torvalds 				ifa->ifa_local =
15841da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
15851da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
15861da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
15871da177e4SLinus Torvalds 				in_dev_hold(in_dev);
15881da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
15891da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
15901da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
15915c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
15925c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1593dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1594dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
15951da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
15961da177e4SLinus Torvalds 			}
15971da177e4SLinus Torvalds 		}
15981da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1599a8eceea8SJoe Perches 		fallthrough;
1600eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1601d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1602d11327adSIan Campbell 			break;
1603a8eceea8SJoe Perches 		fallthrough;
1604d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1605a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1606d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
16071da177e4SLinus Torvalds 		break;
16081da177e4SLinus Torvalds 	case NETDEV_DOWN:
16091da177e4SLinus Torvalds 		ip_mc_down(in_dev);
16101da177e4SLinus Torvalds 		break;
161193d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
161275c78500SMoni Shoua 		ip_mc_unmap(in_dev);
161375c78500SMoni Shoua 		break;
161493d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
161575c78500SMoni Shoua 		ip_mc_remap(in_dev);
161675c78500SMoni Shoua 		break;
16171da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
161806770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
16191da177e4SLinus Torvalds 			break;
162006770843SBreno Leitao 		/* disable IP when MTU is not enough */
1621a8eceea8SJoe Perches 		fallthrough;
16221da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
16231da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
16241da177e4SLinus Torvalds 		break;
16251da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
16261da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
16271da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
16281da177e4SLinus Torvalds 		 */
16291da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
16301da177e4SLinus Torvalds 
163151602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
163266f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
16331da177e4SLinus Torvalds 		break;
16341da177e4SLinus Torvalds 	}
16351da177e4SLinus Torvalds out:
16361da177e4SLinus Torvalds 	return NOTIFY_DONE;
16371da177e4SLinus Torvalds }
16381da177e4SLinus Torvalds 
16391da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
16401da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
16411da177e4SLinus Torvalds };
16421da177e4SLinus Torvalds 
inet_nlmsg_size(void)164340384999SEric Dumazet static size_t inet_nlmsg_size(void)
1644339bf98fSThomas Graf {
1645339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1646339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1647339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1648339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1649ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
165063b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
165147f0bd50SJacques de Laval 	       + nla_total_size(1)  /* IFA_PROTO */
1652af4d768aSDavid Ahern 	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
165363b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1654339bf98fSThomas Graf }
1655339bf98fSThomas Graf 
cstamp_delta(unsigned long cstamp)16565c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
16575c766d64SJiri Pirko {
16585c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
16595c766d64SJiri Pirko }
16605c766d64SJiri Pirko 
put_cacheinfo(struct sk_buff * skb,unsigned long cstamp,unsigned long tstamp,u32 preferred,u32 valid)16615c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
16625c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
16635c766d64SJiri Pirko {
16645c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
16655c766d64SJiri Pirko 
16665c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
16675c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
16685c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
16695c766d64SJiri Pirko 	ci.ifa_valid = valid;
16705c766d64SJiri Pirko 
16715c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
16725c766d64SJiri Pirko }
16735c766d64SJiri Pirko 
inet_fill_ifaddr(struct sk_buff * skb,struct in_ifaddr * ifa,struct inet_fill_args * args)16741da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1675978a46faSChristian Brauner 			    struct inet_fill_args *args)
16761da177e4SLinus Torvalds {
16771da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
16781da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
16795c766d64SJiri Pirko 	u32 preferred, valid;
16801da177e4SLinus Torvalds 
1681978a46faSChristian Brauner 	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
1682978a46faSChristian Brauner 			args->flags);
168351456b29SIan Morris 	if (!nlh)
168426932566SPatrick McHardy 		return -EMSGSIZE;
168547f68512SThomas Graf 
168647f68512SThomas Graf 	ifm = nlmsg_data(nlh);
16871da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
16881da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
16895c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
16901da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
16911da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
16921da177e4SLinus Torvalds 
1693978a46faSChristian Brauner 	if (args->netnsid >= 0 &&
1694978a46faSChristian Brauner 	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
1695d3807145SChristian Brauner 		goto nla_put_failure;
1696d3807145SChristian Brauner 
16975c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
16985c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
16995c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
17005c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
17015c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
17025c766d64SJiri Pirko 
17035c766d64SJiri Pirko 			if (preferred > tval)
17045c766d64SJiri Pirko 				preferred -= tval;
17055c766d64SJiri Pirko 			else
17065c766d64SJiri Pirko 				preferred = 0;
17075c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
17085c766d64SJiri Pirko 				if (valid > tval)
17095c766d64SJiri Pirko 					valid -= tval;
17105c766d64SJiri Pirko 				else
17115c766d64SJiri Pirko 					valid = 0;
17125c766d64SJiri Pirko 			}
17135c766d64SJiri Pirko 		}
17145c766d64SJiri Pirko 	} else {
17155c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
17165c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
17175c766d64SJiri Pirko 	}
1718f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1719930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1720f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1721930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1722f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1723930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1724f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
17255c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
172647f0bd50SJacques de Laval 	    (ifa->ifa_proto &&
172747f0bd50SJacques de Laval 	     nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
1728ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
1729af4d768aSDavid Ahern 	    (ifa->ifa_rt_priority &&
1730af4d768aSDavid Ahern 	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
17315c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
17325c766d64SJiri Pirko 			  preferred, valid))
1733f3756b79SDavid S. Miller 		goto nla_put_failure;
173447f68512SThomas Graf 
1735053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1736053c095aSJohannes Berg 	return 0;
173747f68512SThomas Graf 
173847f68512SThomas Graf nla_put_failure:
173926932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
174026932566SPatrick McHardy 	return -EMSGSIZE;
17411da177e4SLinus Torvalds }
17421da177e4SLinus Torvalds 
inet_valid_dump_ifaddr_req(const struct nlmsghdr * nlh,struct inet_fill_args * fillargs,struct net ** tgt_net,struct sock * sk,struct netlink_callback * cb)1743c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
1744c33078e3SDavid Ahern 				      struct inet_fill_args *fillargs,
1745c33078e3SDavid Ahern 				      struct net **tgt_net, struct sock *sk,
17465fcd266aSDavid Ahern 				      struct netlink_callback *cb)
1747c33078e3SDavid Ahern {
17485fcd266aSDavid Ahern 	struct netlink_ext_ack *extack = cb->extack;
1749c33078e3SDavid Ahern 	struct nlattr *tb[IFA_MAX+1];
1750c33078e3SDavid Ahern 	struct ifaddrmsg *ifm;
1751c33078e3SDavid Ahern 	int err, i;
1752c33078e3SDavid Ahern 
1753c33078e3SDavid Ahern 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
1754c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
1755c33078e3SDavid Ahern 		return -EINVAL;
1756c33078e3SDavid Ahern 	}
1757c33078e3SDavid Ahern 
1758c33078e3SDavid Ahern 	ifm = nlmsg_data(nlh);
1759c33078e3SDavid Ahern 	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
1760c33078e3SDavid Ahern 		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
1761c33078e3SDavid Ahern 		return -EINVAL;
1762c33078e3SDavid Ahern 	}
17635fcd266aSDavid Ahern 
17645fcd266aSDavid Ahern 	fillargs->ifindex = ifm->ifa_index;
17655fcd266aSDavid Ahern 	if (fillargs->ifindex) {
17665fcd266aSDavid Ahern 		cb->answer_flags |= NLM_F_DUMP_FILTERED;
17675fcd266aSDavid Ahern 		fillargs->flags |= NLM_F_DUMP_FILTERED;
1768c33078e3SDavid Ahern 	}
1769c33078e3SDavid Ahern 
17708cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
1771c33078e3SDavid Ahern 					    ifa_ipv4_policy, extack);
1772c33078e3SDavid Ahern 	if (err < 0)
1773c33078e3SDavid Ahern 		return err;
1774c33078e3SDavid Ahern 
1775c33078e3SDavid Ahern 	for (i = 0; i <= IFA_MAX; ++i) {
1776c33078e3SDavid Ahern 		if (!tb[i])
1777c33078e3SDavid Ahern 			continue;
1778c33078e3SDavid Ahern 
1779c33078e3SDavid Ahern 		if (i == IFA_TARGET_NETNSID) {
1780c33078e3SDavid Ahern 			struct net *net;
1781c33078e3SDavid Ahern 
1782c33078e3SDavid Ahern 			fillargs->netnsid = nla_get_s32(tb[i]);
1783c33078e3SDavid Ahern 
1784c33078e3SDavid Ahern 			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
1785c33078e3SDavid Ahern 			if (IS_ERR(net)) {
1786bf4cc40eSBjørn Mork 				fillargs->netnsid = -1;
1787c33078e3SDavid Ahern 				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
1788c33078e3SDavid Ahern 				return PTR_ERR(net);
1789c33078e3SDavid Ahern 			}
1790c33078e3SDavid Ahern 			*tgt_net = net;
1791c33078e3SDavid Ahern 		} else {
1792c33078e3SDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
1793c33078e3SDavid Ahern 			return -EINVAL;
1794c33078e3SDavid Ahern 		}
1795c33078e3SDavid Ahern 	}
1796c33078e3SDavid Ahern 
1797c33078e3SDavid Ahern 	return 0;
1798c33078e3SDavid Ahern }
1799c33078e3SDavid Ahern 
in_dev_dump_addr(struct in_device * in_dev,struct sk_buff * skb,struct netlink_callback * cb,int s_ip_idx,struct inet_fill_args * fillargs)18001c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
18011c98eca4SDavid Ahern 			    struct netlink_callback *cb, int s_ip_idx,
18021c98eca4SDavid Ahern 			    struct inet_fill_args *fillargs)
18031c98eca4SDavid Ahern {
18041c98eca4SDavid Ahern 	struct in_ifaddr *ifa;
18051c98eca4SDavid Ahern 	int ip_idx = 0;
18061c98eca4SDavid Ahern 	int err;
18071c98eca4SDavid Ahern 
1808d3e6e285SFlorian Westphal 	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
1809ef11db33SFlorian Westphal 		if (ip_idx < s_ip_idx) {
1810ef11db33SFlorian Westphal 			ip_idx++;
18111c98eca4SDavid Ahern 			continue;
1812ef11db33SFlorian Westphal 		}
18131c98eca4SDavid Ahern 		err = inet_fill_ifaddr(skb, ifa, fillargs);
18141c98eca4SDavid Ahern 		if (err < 0)
18151c98eca4SDavid Ahern 			goto done;
18161c98eca4SDavid Ahern 
18171c98eca4SDavid Ahern 		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1818ef11db33SFlorian Westphal 		ip_idx++;
18191c98eca4SDavid Ahern 	}
18201c98eca4SDavid Ahern 	err = 0;
18211c98eca4SDavid Ahern 
18221c98eca4SDavid Ahern done:
18231c98eca4SDavid Ahern 	cb->args[2] = ip_idx;
18241c98eca4SDavid Ahern 
18251c98eca4SDavid Ahern 	return err;
18261c98eca4SDavid Ahern }
18271c98eca4SDavid Ahern 
1828*6634a8ecSEric Dumazet /* Combine dev_addr_genid and dev_base_seq to detect changes.
1829*6634a8ecSEric Dumazet  */
inet_base_seq(const struct net * net)1830*6634a8ecSEric Dumazet static u32 inet_base_seq(const struct net *net)
1831*6634a8ecSEric Dumazet {
1832*6634a8ecSEric Dumazet 	u32 res = atomic_read(&net->ipv4.dev_addr_genid) +
1833*6634a8ecSEric Dumazet 		  net->dev_base_seq;
1834*6634a8ecSEric Dumazet 
1835*6634a8ecSEric Dumazet 	/* Must not return 0 (see nl_dump_check_consistent()).
1836*6634a8ecSEric Dumazet 	 * Chose a value far away from 0.
1837*6634a8ecSEric Dumazet 	 */
1838*6634a8ecSEric Dumazet 	if (!res)
1839*6634a8ecSEric Dumazet 		res = 0x80000000;
1840*6634a8ecSEric Dumazet 	return res;
1841*6634a8ecSEric Dumazet }
1842*6634a8ecSEric Dumazet 
inet_dump_ifaddr(struct sk_buff * skb,struct netlink_callback * cb)18431da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
18441da177e4SLinus Torvalds {
1845c33078e3SDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
1846978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1847978a46faSChristian Brauner 		.portid = NETLINK_CB(cb->skb).portid,
1848c33078e3SDavid Ahern 		.seq = nlh->nlmsg_seq,
1849978a46faSChristian Brauner 		.event = RTM_NEWADDR,
1850978a46faSChristian Brauner 		.flags = NLM_F_MULTI,
1851978a46faSChristian Brauner 		.netnsid = -1,
1852978a46faSChristian Brauner 	};
18533b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1854d3807145SChristian Brauner 	struct net *tgt_net = net;
1855eec4df98SEric Dumazet 	int h, s_h;
1856eec4df98SEric Dumazet 	int idx, s_idx;
18571c98eca4SDavid Ahern 	int s_ip_idx;
18581da177e4SLinus Torvalds 	struct net_device *dev;
18591da177e4SLinus Torvalds 	struct in_device *in_dev;
1860eec4df98SEric Dumazet 	struct hlist_head *head;
1861d7e38611SDavid Ahern 	int err = 0;
18621da177e4SLinus Torvalds 
1863eec4df98SEric Dumazet 	s_h = cb->args[0];
1864eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
18651c98eca4SDavid Ahern 	s_ip_idx = cb->args[2];
1866eec4df98SEric Dumazet 
1867c33078e3SDavid Ahern 	if (cb->strict_check) {
1868c33078e3SDavid Ahern 		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
18695fcd266aSDavid Ahern 						 skb->sk, cb);
1870c33078e3SDavid Ahern 		if (err < 0)
1871d7e38611SDavid Ahern 			goto put_tgt_net;
18725fcd266aSDavid Ahern 
1873d7e38611SDavid Ahern 		err = 0;
18745fcd266aSDavid Ahern 		if (fillargs.ifindex) {
18755fcd266aSDavid Ahern 			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1876d7e38611SDavid Ahern 			if (!dev) {
1877d7e38611SDavid Ahern 				err = -ENODEV;
1878d7e38611SDavid Ahern 				goto put_tgt_net;
1879d7e38611SDavid Ahern 			}
18805fcd266aSDavid Ahern 
18815fcd266aSDavid Ahern 			in_dev = __in_dev_get_rtnl(dev);
18825fcd266aSDavid Ahern 			if (in_dev) {
18835fcd266aSDavid Ahern 				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
18845fcd266aSDavid Ahern 						       &fillargs);
18855fcd266aSDavid Ahern 			}
18865fcd266aSDavid Ahern 			goto put_tgt_net;
18875fcd266aSDavid Ahern 		}
1888d3807145SChristian Brauner 	}
1889d3807145SChristian Brauner 
1890eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18917562f876SPavel Emelianov 		idx = 0;
1892d3807145SChristian Brauner 		head = &tgt_net->dev_index_head[h];
1893eec4df98SEric Dumazet 		rcu_read_lock();
1894*6634a8ecSEric Dumazet 		cb->seq = inet_base_seq(tgt_net);
1895b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18961da177e4SLinus Torvalds 			if (idx < s_idx)
18977562f876SPavel Emelianov 				goto cont;
18984b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
18991da177e4SLinus Torvalds 				s_ip_idx = 0;
1900eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
19019f9354b9SEric Dumazet 			if (!in_dev)
19027562f876SPavel Emelianov 				goto cont;
19031da177e4SLinus Torvalds 
19041c98eca4SDavid Ahern 			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
19051c98eca4SDavid Ahern 					       &fillargs);
19061c98eca4SDavid Ahern 			if (err < 0) {
1907eec4df98SEric Dumazet 				rcu_read_unlock();
19081da177e4SLinus Torvalds 				goto done;
19091da177e4SLinus Torvalds 			}
19107562f876SPavel Emelianov cont:
19117562f876SPavel Emelianov 			idx++;
19121da177e4SLinus Torvalds 		}
1913eec4df98SEric Dumazet 		rcu_read_unlock();
1914eec4df98SEric Dumazet 	}
19151da177e4SLinus Torvalds 
19161da177e4SLinus Torvalds done:
1917eec4df98SEric Dumazet 	cb->args[0] = h;
1918eec4df98SEric Dumazet 	cb->args[1] = idx;
19195fcd266aSDavid Ahern put_tgt_net:
1920978a46faSChristian Brauner 	if (fillargs.netnsid >= 0)
1921d3807145SChristian Brauner 		put_net(tgt_net);
19221da177e4SLinus Torvalds 
19237c1e8a38SArthur Gautier 	return skb->len ? : err;
19241da177e4SLinus Torvalds }
19251da177e4SLinus Torvalds 
rtmsg_ifa(int event,struct in_ifaddr * ifa,struct nlmsghdr * nlh,u32 portid)1926d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
192715e47304SEric W. Biederman 		      u32 portid)
19281da177e4SLinus Torvalds {
1929978a46faSChristian Brauner 	struct inet_fill_args fillargs = {
1930978a46faSChristian Brauner 		.portid = portid,
1931978a46faSChristian Brauner 		.seq = nlh ? nlh->nlmsg_seq : 0,
1932978a46faSChristian Brauner 		.event = event,
1933978a46faSChristian Brauner 		.flags = 0,
1934978a46faSChristian Brauner 		.netnsid = -1,
1935978a46faSChristian Brauner 	};
193647f68512SThomas Graf 	struct sk_buff *skb;
1937d6062cbbSThomas Graf 	int err = -ENOBUFS;
19384b8aa9abSDenis V. Lunev 	struct net *net;
19391da177e4SLinus Torvalds 
1940c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1941339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
194251456b29SIan Morris 	if (!skb)
1943d6062cbbSThomas Graf 		goto errout;
1944d6062cbbSThomas Graf 
1945978a46faSChristian Brauner 	err = inet_fill_ifaddr(skb, ifa, &fillargs);
194626932566SPatrick McHardy 	if (err < 0) {
194726932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
194826932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
194926932566SPatrick McHardy 		kfree_skb(skb);
195026932566SPatrick McHardy 		goto errout;
195126932566SPatrick McHardy 	}
195215e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
19531ce85fe4SPablo Neira Ayuso 	return;
1954d6062cbbSThomas Graf errout:
1955d6062cbbSThomas Graf 	if (err < 0)
19564b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
19571da177e4SLinus Torvalds }
19581da177e4SLinus Torvalds 
inet_get_link_af_size(const struct net_device * dev,u32 ext_filter_mask)1959b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1960b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
19619f0f7272SThomas Graf {
19621fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19639f0f7272SThomas Graf 
19649f0f7272SThomas Graf 	if (!in_dev)
19659f0f7272SThomas Graf 		return 0;
19669f0f7272SThomas Graf 
19679f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
19689f0f7272SThomas Graf }
19699f0f7272SThomas Graf 
inet_fill_link_af(struct sk_buff * skb,const struct net_device * dev,u32 ext_filter_mask)1970d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1971d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
19729f0f7272SThomas Graf {
19731fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
19749f0f7272SThomas Graf 	struct nlattr *nla;
19759f0f7272SThomas Graf 	int i;
19769f0f7272SThomas Graf 
19779f0f7272SThomas Graf 	if (!in_dev)
19789f0f7272SThomas Graf 		return -ENODATA;
19799f0f7272SThomas Graf 
19809f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
198151456b29SIan Morris 	if (!nla)
19829f0f7272SThomas Graf 		return -EMSGSIZE;
19839f0f7272SThomas Graf 
19849f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
19859f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
19869f0f7272SThomas Graf 
19879f0f7272SThomas Graf 	return 0;
19889f0f7272SThomas Graf }
19899f0f7272SThomas Graf 
19909f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
19919f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
19929f0f7272SThomas Graf };
19939f0f7272SThomas Graf 
inet_validate_link_af(const struct net_device * dev,const struct nlattr * nla,struct netlink_ext_ack * extack)1994cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
19958679c31eSRocco Yue 				 const struct nlattr *nla,
19968679c31eSRocco Yue 				 struct netlink_ext_ack *extack)
19979f0f7272SThomas Graf {
19989f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
19999f0f7272SThomas Graf 	int err, rem;
20009f0f7272SThomas Graf 
2001a100243dSCong Wang 	if (dev && !__in_dev_get_rtnl(dev))
2002cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
20039f0f7272SThomas Graf 
20048cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
20058679c31eSRocco Yue 					  inet_af_policy, extack);
20069f0f7272SThomas Graf 	if (err < 0)
20079f0f7272SThomas Graf 		return err;
20089f0f7272SThomas Graf 
20099f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20109f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
20119f0f7272SThomas Graf 			int cfgid = nla_type(a);
20129f0f7272SThomas Graf 
20139f0f7272SThomas Graf 			if (nla_len(a) < 4)
20149f0f7272SThomas Graf 				return -EINVAL;
20159f0f7272SThomas Graf 
20169f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
20179f0f7272SThomas Graf 				return -EINVAL;
20189f0f7272SThomas Graf 		}
20199f0f7272SThomas Graf 	}
20209f0f7272SThomas Graf 
2021cf7afbfeSThomas Graf 	return 0;
2022cf7afbfeSThomas Graf }
2023cf7afbfeSThomas Graf 
inet_set_link_af(struct net_device * dev,const struct nlattr * nla,struct netlink_ext_ack * extack)20243583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
20253583a4e8SStephen Hemminger 			    struct netlink_ext_ack *extack)
2026cf7afbfeSThomas Graf {
2027a100243dSCong Wang 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
2028cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
2029cf7afbfeSThomas Graf 	int rem;
2030cf7afbfeSThomas Graf 
2031cf7afbfeSThomas Graf 	if (!in_dev)
2032cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
2033cf7afbfeSThomas Graf 
20348cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
20355ac6b198SZheng Yongjun 		return -EINVAL;
2036cf7afbfeSThomas Graf 
20379f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
20389f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
20399f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
20409f0f7272SThomas Graf 	}
20419f0f7272SThomas Graf 
20429f0f7272SThomas Graf 	return 0;
20439f0f7272SThomas Graf }
20449f0f7272SThomas Graf 
inet_netconf_msgsize_devconf(int type)2045edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
2046edc9e748SNicolas Dichtel {
2047edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
2048edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
2049136ba622SZhang Shengju 	bool all = false;
2050edc9e748SNicolas Dichtel 
2051136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2052136ba622SZhang Shengju 		all = true;
2053136ba622SZhang Shengju 
2054136ba622SZhang Shengju 	if (all || type == NETCONFA_FORWARDING)
2055edc9e748SNicolas Dichtel 		size += nla_total_size(4);
2056136ba622SZhang Shengju 	if (all || type == NETCONFA_RP_FILTER)
2057cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
2058136ba622SZhang Shengju 	if (all || type == NETCONFA_MC_FORWARDING)
2059d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
20605cbf777cSXin Long 	if (all || type == NETCONFA_BC_FORWARDING)
20615cbf777cSXin Long 		size += nla_total_size(4);
2062136ba622SZhang Shengju 	if (all || type == NETCONFA_PROXY_NEIGH)
2063f085ff1cSstephen hemminger 		size += nla_total_size(4);
2064136ba622SZhang Shengju 	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
2065974d7af5SAndy Gospodarek 		size += nla_total_size(4);
2066edc9e748SNicolas Dichtel 
2067edc9e748SNicolas Dichtel 	return size;
2068edc9e748SNicolas Dichtel }
2069edc9e748SNicolas Dichtel 
inet_netconf_fill_devconf(struct sk_buff * skb,int ifindex,struct ipv4_devconf * devconf,u32 portid,u32 seq,int event,unsigned int flags,int type)2070edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
2071edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
2072edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
2073edc9e748SNicolas Dichtel 				     int type)
2074edc9e748SNicolas Dichtel {
2075edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
2076edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
2077136ba622SZhang Shengju 	bool all = false;
2078edc9e748SNicolas Dichtel 
2079edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
2080edc9e748SNicolas Dichtel 			flags);
208151456b29SIan Morris 	if (!nlh)
2082edc9e748SNicolas Dichtel 		return -EMSGSIZE;
2083edc9e748SNicolas Dichtel 
2084136ba622SZhang Shengju 	if (type == NETCONFA_ALL)
2085136ba622SZhang Shengju 		all = true;
2086136ba622SZhang Shengju 
2087edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
2088edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
2089edc9e748SNicolas Dichtel 
2090edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
2091edc9e748SNicolas Dichtel 		goto nla_put_failure;
2092edc9e748SNicolas Dichtel 
2093b5c9641dSDavid Ahern 	if (!devconf)
2094b5c9641dSDavid Ahern 		goto out;
2095b5c9641dSDavid Ahern 
2096136ba622SZhang Shengju 	if ((all || type == NETCONFA_FORWARDING) &&
2097edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
2098edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
2099edc9e748SNicolas Dichtel 		goto nla_put_failure;
2100136ba622SZhang Shengju 	if ((all || type == NETCONFA_RP_FILTER) &&
2101cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
2102cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
2103cc535dfbSNicolas Dichtel 		goto nla_put_failure;
2104136ba622SZhang Shengju 	if ((all || type == NETCONFA_MC_FORWARDING) &&
2105d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
2106d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
2107d67b8c61SNicolas Dichtel 		goto nla_put_failure;
21085cbf777cSXin Long 	if ((all || type == NETCONFA_BC_FORWARDING) &&
21095cbf777cSXin Long 	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
21105cbf777cSXin Long 			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
21115cbf777cSXin Long 		goto nla_put_failure;
2112136ba622SZhang Shengju 	if ((all || type == NETCONFA_PROXY_NEIGH) &&
211309aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
2114f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
2115f085ff1cSstephen hemminger 		goto nla_put_failure;
2116136ba622SZhang Shengju 	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
2117974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2118974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
2119974d7af5SAndy Gospodarek 		goto nla_put_failure;
2120edc9e748SNicolas Dichtel 
2121b5c9641dSDavid Ahern out:
2122053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
2123053c095aSJohannes Berg 	return 0;
2124edc9e748SNicolas Dichtel 
2125edc9e748SNicolas Dichtel nla_put_failure:
2126edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
2127edc9e748SNicolas Dichtel 	return -EMSGSIZE;
2128edc9e748SNicolas Dichtel }
2129edc9e748SNicolas Dichtel 
inet_netconf_notify_devconf(struct net * net,int event,int type,int ifindex,struct ipv4_devconf * devconf)21303b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type,
21313b022865SDavid Ahern 				 int ifindex, struct ipv4_devconf *devconf)
2132edc9e748SNicolas Dichtel {
2133edc9e748SNicolas Dichtel 	struct sk_buff *skb;
2134edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
2135edc9e748SNicolas Dichtel 
2136fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
213751456b29SIan Morris 	if (!skb)
2138edc9e748SNicolas Dichtel 		goto errout;
2139edc9e748SNicolas Dichtel 
2140edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
21413b022865SDavid Ahern 					event, 0, type);
2142edc9e748SNicolas Dichtel 	if (err < 0) {
2143edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
2144edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
2145edc9e748SNicolas Dichtel 		kfree_skb(skb);
2146edc9e748SNicolas Dichtel 		goto errout;
2147edc9e748SNicolas Dichtel 	}
2148fa17806cSEric Dumazet 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
2149edc9e748SNicolas Dichtel 	return;
2150edc9e748SNicolas Dichtel errout:
2151edc9e748SNicolas Dichtel 	if (err < 0)
2152edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
2153edc9e748SNicolas Dichtel }
2154edc9e748SNicolas Dichtel 
21559e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
21569e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
21579e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
2158cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
215909aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
2160974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
21619e551110SNicolas Dichtel };
21629e551110SNicolas Dichtel 
inet_netconf_valid_get_req(struct sk_buff * skb,const struct nlmsghdr * nlh,struct nlattr ** tb,struct netlink_ext_ack * extack)2163eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb,
2164eede370dSJakub Kicinski 				      const struct nlmsghdr *nlh,
2165eede370dSJakub Kicinski 				      struct nlattr **tb,
2166eede370dSJakub Kicinski 				      struct netlink_ext_ack *extack)
2167eede370dSJakub Kicinski {
2168eede370dSJakub Kicinski 	int i, err;
2169eede370dSJakub Kicinski 
2170eede370dSJakub Kicinski 	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
2171eede370dSJakub Kicinski 		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
2172eede370dSJakub Kicinski 		return -EINVAL;
2173eede370dSJakub Kicinski 	}
2174eede370dSJakub Kicinski 
2175eede370dSJakub Kicinski 	if (!netlink_strict_get_check(skb))
21768cb08174SJohannes Berg 		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
21778cb08174SJohannes Berg 					      tb, NETCONFA_MAX,
21788cb08174SJohannes Berg 					      devconf_ipv4_policy, extack);
2179eede370dSJakub Kicinski 
21808cb08174SJohannes Berg 	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
21818cb08174SJohannes Berg 					    tb, NETCONFA_MAX,
21828cb08174SJohannes Berg 					    devconf_ipv4_policy, extack);
2183eede370dSJakub Kicinski 	if (err)
2184eede370dSJakub Kicinski 		return err;
2185eede370dSJakub Kicinski 
2186eede370dSJakub Kicinski 	for (i = 0; i <= NETCONFA_MAX; i++) {
2187eede370dSJakub Kicinski 		if (!tb[i])
2188eede370dSJakub Kicinski 			continue;
2189eede370dSJakub Kicinski 
2190eede370dSJakub Kicinski 		switch (i) {
2191eede370dSJakub Kicinski 		case NETCONFA_IFINDEX:
2192eede370dSJakub Kicinski 			break;
2193eede370dSJakub Kicinski 		default:
2194eede370dSJakub Kicinski 			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
2195eede370dSJakub Kicinski 			return -EINVAL;
2196eede370dSJakub Kicinski 		}
2197eede370dSJakub Kicinski 	}
2198eede370dSJakub Kicinski 
2199eede370dSJakub Kicinski 	return 0;
2200eede370dSJakub Kicinski }
2201eede370dSJakub Kicinski 
inet_netconf_get_devconf(struct sk_buff * in_skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)22029e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
2203c21ef3e3SDavid Ahern 				    struct nlmsghdr *nlh,
2204c21ef3e3SDavid Ahern 				    struct netlink_ext_ack *extack)
22059e551110SNicolas Dichtel {
22069e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
22079e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
22089e551110SNicolas Dichtel 	struct sk_buff *skb;
22099e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
22109e551110SNicolas Dichtel 	struct in_device *in_dev;
22119e551110SNicolas Dichtel 	struct net_device *dev;
22129e551110SNicolas Dichtel 	int ifindex;
22139e551110SNicolas Dichtel 	int err;
22149e551110SNicolas Dichtel 
2215eede370dSJakub Kicinski 	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
2216eede370dSJakub Kicinski 	if (err)
22179e551110SNicolas Dichtel 		goto errout;
22189e551110SNicolas Dichtel 
2219a97eb33fSAnton Protopopov 	err = -EINVAL;
22209e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
22219e551110SNicolas Dichtel 		goto errout;
22229e551110SNicolas Dichtel 
22239e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
22249e551110SNicolas Dichtel 	switch (ifindex) {
22259e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
22269e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
22279e551110SNicolas Dichtel 		break;
22289e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
22299e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
22309e551110SNicolas Dichtel 		break;
22319e551110SNicolas Dichtel 	default:
22329e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
223351456b29SIan Morris 		if (!dev)
22349e551110SNicolas Dichtel 			goto errout;
22359e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
223651456b29SIan Morris 		if (!in_dev)
22379e551110SNicolas Dichtel 			goto errout;
22389e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
22399e551110SNicolas Dichtel 		break;
22409e551110SNicolas Dichtel 	}
22419e551110SNicolas Dichtel 
22429e551110SNicolas Dichtel 	err = -ENOBUFS;
2243fa17806cSEric Dumazet 	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
224451456b29SIan Morris 	if (!skb)
22459e551110SNicolas Dichtel 		goto errout;
22469e551110SNicolas Dichtel 
22479e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
22489e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
22499e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
2250136ba622SZhang Shengju 					NETCONFA_ALL);
22519e551110SNicolas Dichtel 	if (err < 0) {
22529e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
22539e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
22549e551110SNicolas Dichtel 		kfree_skb(skb);
22559e551110SNicolas Dichtel 		goto errout;
22569e551110SNicolas Dichtel 	}
22579e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
22589e551110SNicolas Dichtel errout:
22599e551110SNicolas Dichtel 	return err;
22609e551110SNicolas Dichtel }
22619e551110SNicolas Dichtel 
inet_netconf_dump_devconf(struct sk_buff * skb,struct netlink_callback * cb)22627a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
22637a674200SNicolas Dichtel 				     struct netlink_callback *cb)
22647a674200SNicolas Dichtel {
2265addd383fSDavid Ahern 	const struct nlmsghdr *nlh = cb->nlh;
22667a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
22677a674200SNicolas Dichtel 	int h, s_h;
22687a674200SNicolas Dichtel 	int idx, s_idx;
22697a674200SNicolas Dichtel 	struct net_device *dev;
22707a674200SNicolas Dichtel 	struct in_device *in_dev;
22717a674200SNicolas Dichtel 	struct hlist_head *head;
22727a674200SNicolas Dichtel 
2273addd383fSDavid Ahern 	if (cb->strict_check) {
2274addd383fSDavid Ahern 		struct netlink_ext_ack *extack = cb->extack;
2275addd383fSDavid Ahern 		struct netconfmsg *ncm;
2276addd383fSDavid Ahern 
2277addd383fSDavid Ahern 		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
2278addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
2279addd383fSDavid Ahern 			return -EINVAL;
2280addd383fSDavid Ahern 		}
2281addd383fSDavid Ahern 
2282addd383fSDavid Ahern 		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
2283addd383fSDavid Ahern 			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
2284addd383fSDavid Ahern 			return -EINVAL;
2285addd383fSDavid Ahern 		}
2286addd383fSDavid Ahern 	}
2287addd383fSDavid Ahern 
22887a674200SNicolas Dichtel 	s_h = cb->args[0];
22897a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
22907a674200SNicolas Dichtel 
22917a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
22927a674200SNicolas Dichtel 		idx = 0;
22937a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
22947a674200SNicolas Dichtel 		rcu_read_lock();
2295*6634a8ecSEric Dumazet 		cb->seq = inet_base_seq(net);
22967a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
22977a674200SNicolas Dichtel 			if (idx < s_idx)
22987a674200SNicolas Dichtel 				goto cont;
22997a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
23007a674200SNicolas Dichtel 			if (!in_dev)
23017a674200SNicolas Dichtel 				goto cont;
23027a674200SNicolas Dichtel 
23037a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
23047a674200SNicolas Dichtel 						      &in_dev->cnf,
23057a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
2306addd383fSDavid Ahern 						      nlh->nlmsg_seq,
23077a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
23087a674200SNicolas Dichtel 						      NLM_F_MULTI,
2309136ba622SZhang Shengju 						      NETCONFA_ALL) < 0) {
23107a674200SNicolas Dichtel 				rcu_read_unlock();
23117a674200SNicolas Dichtel 				goto done;
23127a674200SNicolas Dichtel 			}
23130465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
23147a674200SNicolas Dichtel cont:
23157a674200SNicolas Dichtel 			idx++;
23167a674200SNicolas Dichtel 		}
23177a674200SNicolas Dichtel 		rcu_read_unlock();
23187a674200SNicolas Dichtel 	}
23197a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
23207a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
23217a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
23227a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2323addd383fSDavid Ahern 					      nlh->nlmsg_seq,
23247a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2325136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
23267a674200SNicolas Dichtel 			goto done;
23277a674200SNicolas Dichtel 		else
23287a674200SNicolas Dichtel 			h++;
23297a674200SNicolas Dichtel 	}
23307a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
23317a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
23327a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
23337a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
2334addd383fSDavid Ahern 					      nlh->nlmsg_seq,
23357a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
2336136ba622SZhang Shengju 					      NETCONFA_ALL) < 0)
23377a674200SNicolas Dichtel 			goto done;
23387a674200SNicolas Dichtel 		else
23397a674200SNicolas Dichtel 			h++;
23407a674200SNicolas Dichtel 	}
23417a674200SNicolas Dichtel done:
23427a674200SNicolas Dichtel 	cb->args[0] = h;
23437a674200SNicolas Dichtel 	cb->args[1] = idx;
23447a674200SNicolas Dichtel 
23457a674200SNicolas Dichtel 	return skb->len;
23467a674200SNicolas Dichtel }
23477a674200SNicolas Dichtel 
23481da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
23491da177e4SLinus Torvalds 
devinet_copy_dflt_conf(struct net * net,int i)2350c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
235131be3085SHerbert Xu {
235231be3085SHerbert Xu 	struct net_device *dev;
235331be3085SHerbert Xu 
235431be3085SHerbert Xu 	rcu_read_lock();
2355c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
2356c6d14c84SEric Dumazet 		struct in_device *in_dev;
2357c6d14c84SEric Dumazet 
235831be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
235931be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
23609355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
2361c6d14c84SEric Dumazet 	}
236231be3085SHerbert Xu 	rcu_read_unlock();
236331be3085SHerbert Xu }
236431be3085SHerbert Xu 
2365c6d14c84SEric Dumazet /* called with RTNL locked */
inet_forward_change(struct net * net)2366c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
236768dd299bSPavel Emelyanov {
236868dd299bSPavel Emelyanov 	struct net_device *dev;
2369586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
237068dd299bSPavel Emelyanov 
2371586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
23729355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
23733b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23743b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2375edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
2376edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
23773b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23783b022865SDavid Ahern 				    NETCONFA_FORWARDING,
2379edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
2380edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
238168dd299bSPavel Emelyanov 
2382c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
238368dd299bSPavel Emelyanov 		struct in_device *in_dev;
2384fa17806cSEric Dumazet 
23850187bdfbSBen Hutchings 		if (on)
23860187bdfbSBen Hutchings 			dev_disable_lro(dev);
2387fa17806cSEric Dumazet 
2388fa17806cSEric Dumazet 		in_dev = __in_dev_get_rtnl(dev);
2389edc9e748SNicolas Dichtel 		if (in_dev) {
239068dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
23913b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
23923b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2393edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2394edc9e748SNicolas Dichtel 		}
239568dd299bSPavel Emelyanov 	}
239668dd299bSPavel Emelyanov }
239768dd299bSPavel Emelyanov 
devinet_conf_ifindex(struct net * net,struct ipv4_devconf * cnf)2398f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2399f085ff1cSstephen hemminger {
2400f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2401f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2402f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2403f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2404f085ff1cSstephen hemminger 	else {
2405f085ff1cSstephen hemminger 		struct in_device *idev
2406f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2407f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2408f085ff1cSstephen hemminger 	}
2409f085ff1cSstephen hemminger }
2410f085ff1cSstephen hemminger 
devinet_conf_proc(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)2411fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
241232927393SChristoph Hellwig 			     void *buffer, size_t *lenp, loff_t *ppos)
241331be3085SHerbert Xu {
2414d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
24158d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2416d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
241731be3085SHerbert Xu 
241831be3085SHerbert Xu 	if (write) {
241931be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2420c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
242131be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2422f085ff1cSstephen hemminger 		int ifindex;
242331be3085SHerbert Xu 
242431be3085SHerbert Xu 		set_bit(i, cnf->state);
242531be3085SHerbert Xu 
24269355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2427c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2428d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2429d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2430d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
24314ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2432f085ff1cSstephen hemminger 
24335cbf777cSXin Long 		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
24345cbf777cSXin Long 		    new_value != old_value)
24355cbf777cSXin Long 			rt_cache_flush(net);
24365cbf777cSXin Long 
2437cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2438cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2439f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24403b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24413b022865SDavid Ahern 						    NETCONFA_RP_FILTER,
2442cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2443cc535dfbSNicolas Dichtel 		}
2444f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2445f085ff1cSstephen hemminger 		    new_value != old_value) {
2446f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
24473b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24483b022865SDavid Ahern 						    NETCONFA_PROXY_NEIGH,
2449f085ff1cSstephen hemminger 						    ifindex, cnf);
2450f085ff1cSstephen hemminger 		}
2451974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2452974d7af5SAndy Gospodarek 		    new_value != old_value) {
2453974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
24543b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
24553b022865SDavid Ahern 						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2456974d7af5SAndy Gospodarek 						    ifindex, cnf);
2457974d7af5SAndy Gospodarek 		}
245831be3085SHerbert Xu 	}
245931be3085SHerbert Xu 
246031be3085SHerbert Xu 	return ret;
246131be3085SHerbert Xu }
246231be3085SHerbert Xu 
devinet_sysctl_forward(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)2463fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
246432927393SChristoph Hellwig 				  void *buffer, size_t *lenp, loff_t *ppos)
24651da177e4SLinus Torvalds {
24661da177e4SLinus Torvalds 	int *valp = ctl->data;
24671da177e4SLinus Torvalds 	int val = *valp;
246888af182eSEric W. Biederman 	loff_t pos = *ppos;
24698292d7f6SYang Yang 	struct net *net = ctl->extra2;
24708292d7f6SYang Yang 	int ret;
24718292d7f6SYang Yang 
24728292d7f6SYang Yang 	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
24738292d7f6SYang Yang 		return -EPERM;
24748292d7f6SYang Yang 
24758292d7f6SYang Yang 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
24761da177e4SLinus Torvalds 
24771da177e4SLinus Torvalds 	if (write && *valp != val) {
24780187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
247988af182eSEric W. Biederman 			if (!rtnl_trylock()) {
248088af182eSEric W. Biederman 				/* Restore the original values before restarting */
248188af182eSEric W. Biederman 				*valp = val;
248288af182eSEric W. Biederman 				*ppos = pos;
24839b8adb5eSEric W. Biederman 				return restart_syscall();
248488af182eSEric W. Biederman 			}
24850187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2486c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2487edc9e748SNicolas Dichtel 			} else {
24880187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
24890187bdfbSBen Hutchings 				struct in_device *idev =
24900187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2491edc9e748SNicolas Dichtel 				if (*valp)
24920187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
24933b022865SDavid Ahern 				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
2494edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2495edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2496edc9e748SNicolas Dichtel 							    cnf);
24970187bdfbSBen Hutchings 			}
24980187bdfbSBen Hutchings 			rtnl_unlock();
24994ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2500edc9e748SNicolas Dichtel 		} else
25013b022865SDavid Ahern 			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
25023b022865SDavid Ahern 						    NETCONFA_FORWARDING,
2503edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2504edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
25050187bdfbSBen Hutchings 	}
25061da177e4SLinus Torvalds 
25071da177e4SLinus Torvalds 	return ret;
25081da177e4SLinus Torvalds }
25091da177e4SLinus Torvalds 
ipv4_doint_and_flush(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)2510fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
251132927393SChristoph Hellwig 				void *buffer, size_t *lenp, loff_t *ppos)
25121da177e4SLinus Torvalds {
25131da177e4SLinus Torvalds 	int *valp = ctl->data;
25141da177e4SLinus Torvalds 	int val = *valp;
25158d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
251676e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
25171da177e4SLinus Torvalds 
25181da177e4SLinus Torvalds 	if (write && *valp != val)
25194ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
25201da177e4SLinus Torvalds 
25211da177e4SLinus Torvalds 	return ret;
25221da177e4SLinus Torvalds }
25231da177e4SLinus Torvalds 
2524f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
252542f811b8SHerbert Xu 	{ \
252642f811b8SHerbert Xu 		.procname	= name, \
252742f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
252802291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
252942f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
253042f811b8SHerbert Xu 		.mode		= mval, \
253142f811b8SHerbert Xu 		.proc_handler	= proc, \
253231be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
253342f811b8SHerbert Xu 	}
253442f811b8SHerbert Xu 
253542f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2536f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
253742f811b8SHerbert Xu 
253842f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2539f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
254042f811b8SHerbert Xu 
2541f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2542f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
254342f811b8SHerbert Xu 
254442f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2545f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
254642f811b8SHerbert Xu 
25471da177e4SLinus Torvalds static struct devinet_sysctl_table {
25481da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
254902291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
25501da177e4SLinus Torvalds } devinet_sysctl = {
25511da177e4SLinus Torvalds 	.devinet_vars = {
255242f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2553f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
255442f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
25555cbf777cSXin Long 		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
255642f811b8SHerbert Xu 
255742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
255842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
255942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
256042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
256142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
256242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
256342f811b8SHerbert Xu 					"accept_source_route"),
25648153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
256528f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
256642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
256742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
256842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
256942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
257042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
257142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
257242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
257342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
257442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2575eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
2576fcdb44d0SJames Prestwood 		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
2577fcdb44d0SJames Prestwood 					"arp_evict_nocarrier"),
257865324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
25795c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
25805c6fe01cSWilliam Manley 					"force_igmp_version"),
25812690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
25822690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
25832690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
25842690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
25850eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
25860eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
258797daf331SJohannes Berg 		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
258897daf331SJohannes Berg 					"drop_gratuitous_arp"),
258942f811b8SHerbert Xu 
259042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
259142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
259242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
259342f811b8SHerbert Xu 					      "promote_secondaries"),
2594d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2595d0daebc3SThomas Graf 					      "route_localnet"),
259612b74dfaSJohannes Berg 		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
259712b74dfaSJohannes Berg 					      "drop_unicast_in_l2_multicast"),
25981da177e4SLinus Torvalds 	},
25991da177e4SLinus Torvalds };
26001da177e4SLinus Torvalds 
__devinet_sysctl_register(struct net * net,char * dev_name,int ifindex,struct ipv4_devconf * p)2601ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
260229c994e3SNicolas Dichtel 				     int ifindex, struct ipv4_devconf *p)
26031da177e4SLinus Torvalds {
26041da177e4SLinus Torvalds 	int i;
26059fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
26068607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2607bfada697SPavel Emelyanov 
2608425b9c7fSVasily Averin 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT);
26091da177e4SLinus Torvalds 	if (!t)
26109fa89642SPavel Emelyanov 		goto out;
26119fa89642SPavel Emelyanov 
26121da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
26131da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
261431be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2615c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
26161da177e4SLinus Torvalds 	}
26171da177e4SLinus Torvalds 
26188607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
26191da177e4SLinus Torvalds 
26208607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
26211da177e4SLinus Torvalds 	if (!t->sysctl_header)
26228607ddb8SEric W. Biederman 		goto free;
26231da177e4SLinus Torvalds 
26241da177e4SLinus Torvalds 	p->sysctl = t;
262529c994e3SNicolas Dichtel 
26263b022865SDavid Ahern 	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
26273b022865SDavid Ahern 				    ifindex, p);
2628ea40b324SPavel Emelyanov 	return 0;
26291da177e4SLinus Torvalds 
26301da177e4SLinus Torvalds free:
26311da177e4SLinus Torvalds 	kfree(t);
26329fa89642SPavel Emelyanov out:
26336def4801Sliuguoqiang 	return -ENOMEM;
26341da177e4SLinus Torvalds }
26351da177e4SLinus Torvalds 
__devinet_sysctl_unregister(struct net * net,struct ipv4_devconf * cnf,int ifindex)2636b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net,
2637b5c9641dSDavid Ahern 					struct ipv4_devconf *cnf, int ifindex)
263866f27a52SPavel Emelyanov {
263951602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
264066f27a52SPavel Emelyanov 
2641b5c9641dSDavid Ahern 	if (t) {
264251602b2aSPavel Emelyanov 		cnf->sysctl = NULL;
2643ff538818SLucian Adrian Grijincu 		unregister_net_sysctl_table(t->sysctl_header);
26441da177e4SLinus Torvalds 		kfree(t);
26451da177e4SLinus Torvalds 	}
264651602b2aSPavel Emelyanov 
2647b5c9641dSDavid Ahern 	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
2648b5c9641dSDavid Ahern }
2649b5c9641dSDavid Ahern 
devinet_sysctl_register(struct in_device * idev)265020e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
265151602b2aSPavel Emelyanov {
265220e61da7SWANG Cong 	int err;
265320e61da7SWANG Cong 
265420e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
265520e61da7SWANG Cong 		return -EINVAL;
265620e61da7SWANG Cong 
265720e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
265820e61da7SWANG Cong 	if (err)
265920e61da7SWANG Cong 		return err;
266020e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
266129c994e3SNicolas Dichtel 					idev->dev->ifindex, &idev->cnf);
266220e61da7SWANG Cong 	if (err)
266320e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
266420e61da7SWANG Cong 	return err;
266551602b2aSPavel Emelyanov }
266651602b2aSPavel Emelyanov 
devinet_sysctl_unregister(struct in_device * idev)266751602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
266851602b2aSPavel Emelyanov {
2669b5c9641dSDavid Ahern 	struct net *net = dev_net(idev->dev);
2670b5c9641dSDavid Ahern 
2671b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
267251602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
26731da177e4SLinus Torvalds }
26741da177e4SLinus Torvalds 
267568dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
267668dd299bSPavel Emelyanov 	{
267768dd299bSPavel Emelyanov 		.procname	= "ip_forward",
267868dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
267902291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
268068dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
268168dd299bSPavel Emelyanov 		.mode		= 0644,
268268dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
268368dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2684c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
268568dd299bSPavel Emelyanov 	},
268668dd299bSPavel Emelyanov 	{ },
268768dd299bSPavel Emelyanov };
26882a75de0cSEric Dumazet #endif
268968dd299bSPavel Emelyanov 
devinet_init_net(struct net * net)2690752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2691752d14dcSPavel Emelyanov {
2692752d14dcSPavel Emelyanov 	int err;
2693752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
26942a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2695856c395cSCong Wang 	struct ctl_table *tbl;
2696752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
26972a75de0cSEric Dumazet #endif
2698752d14dcSPavel Emelyanov 
2699752d14dcSPavel Emelyanov 	err = -ENOMEM;
2700856c395cSCong Wang 	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
270151456b29SIan Morris 	if (!all)
2702752d14dcSPavel Emelyanov 		goto err_alloc_all;
2703752d14dcSPavel Emelyanov 
2704856c395cSCong Wang 	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
270551456b29SIan Morris 	if (!dflt)
2706752d14dcSPavel Emelyanov 		goto err_alloc_dflt;
2707752d14dcSPavel Emelyanov 
27082a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2709856c395cSCong Wang 	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
271051456b29SIan Morris 	if (!tbl)
2711752d14dcSPavel Emelyanov 		goto err_alloc_ctl;
2712752d14dcSPavel Emelyanov 
271302291680SEric W. Biederman 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2714752d14dcSPavel Emelyanov 	tbl[0].extra1 = all;
2715752d14dcSPavel Emelyanov 	tbl[0].extra2 = net;
27162a75de0cSEric Dumazet #endif
2717856c395cSCong Wang 
27189efd6a3cSNicolas Dichtel 	if (!net_eq(net, &init_net)) {
2719a5612ca1SKuniyuki Iwashima 		switch (net_inherit_devconf()) {
2720a5612ca1SKuniyuki Iwashima 		case 3:
27219efd6a3cSNicolas Dichtel 			/* copy from the current netns */
27229efd6a3cSNicolas Dichtel 			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
27239efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27249efd6a3cSNicolas Dichtel 			memcpy(dflt,
27259efd6a3cSNicolas Dichtel 			       current->nsproxy->net_ns->ipv4.devconf_dflt,
27269efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2727a5612ca1SKuniyuki Iwashima 			break;
2728a5612ca1SKuniyuki Iwashima 		case 0:
2729a5612ca1SKuniyuki Iwashima 		case 1:
2730a5612ca1SKuniyuki Iwashima 			/* copy from init_net */
27319efd6a3cSNicolas Dichtel 			memcpy(all, init_net.ipv4.devconf_all,
27329efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf));
27339efd6a3cSNicolas Dichtel 			memcpy(dflt, init_net.ipv4.devconf_dflt,
27349efd6a3cSNicolas Dichtel 			       sizeof(ipv4_devconf_dflt));
2735a5612ca1SKuniyuki Iwashima 			break;
2736a5612ca1SKuniyuki Iwashima 		case 2:
2737a5612ca1SKuniyuki Iwashima 			/* use compiled values */
2738a5612ca1SKuniyuki Iwashima 			break;
27399efd6a3cSNicolas Dichtel 		}
2740752d14dcSPavel Emelyanov 	}
2741752d14dcSPavel Emelyanov 
2742752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
274329c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
2744752d14dcSPavel Emelyanov 	if (err < 0)
2745752d14dcSPavel Emelyanov 		goto err_reg_all;
2746752d14dcSPavel Emelyanov 
274729c994e3SNicolas Dichtel 	err = __devinet_sysctl_register(net, "default",
274829c994e3SNicolas Dichtel 					NETCONFA_IFINDEX_DEFAULT, dflt);
2749752d14dcSPavel Emelyanov 	if (err < 0)
2750752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2751752d14dcSPavel Emelyanov 
2752752d14dcSPavel Emelyanov 	err = -ENOMEM;
2753c899710fSJoel Granados 	forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl,
2754c899710fSJoel Granados 					  ARRAY_SIZE(ctl_forward_entry));
275551456b29SIan Morris 	if (!forw_hdr)
2756752d14dcSPavel Emelyanov 		goto err_reg_ctl;
27572a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2758752d14dcSPavel Emelyanov #endif
2759752d14dcSPavel Emelyanov 
2760752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2761752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2762752d14dcSPavel Emelyanov 	return 0;
2763752d14dcSPavel Emelyanov 
2764752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2765752d14dcSPavel Emelyanov err_reg_ctl:
2766b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
2767752d14dcSPavel Emelyanov err_reg_dflt:
2768b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
2769752d14dcSPavel Emelyanov err_reg_all:
2770752d14dcSPavel Emelyanov 	kfree(tbl);
2771752d14dcSPavel Emelyanov err_alloc_ctl:
27722a75de0cSEric Dumazet #endif
2773752d14dcSPavel Emelyanov 	kfree(dflt);
2774752d14dcSPavel Emelyanov err_alloc_dflt:
2775752d14dcSPavel Emelyanov 	kfree(all);
2776752d14dcSPavel Emelyanov err_alloc_all:
2777752d14dcSPavel Emelyanov 	return err;
2778752d14dcSPavel Emelyanov }
2779752d14dcSPavel Emelyanov 
devinet_exit_net(struct net * net)2780752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2781752d14dcSPavel Emelyanov {
27822a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2783752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2784752d14dcSPavel Emelyanov 
2785752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2786752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2787b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
2788b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_DEFAULT);
2789b5c9641dSDavid Ahern 	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
2790b5c9641dSDavid Ahern 				    NETCONFA_IFINDEX_ALL);
2791752d14dcSPavel Emelyanov 	kfree(tbl);
27922a75de0cSEric Dumazet #endif
2793752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2794752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2795752d14dcSPavel Emelyanov }
2796752d14dcSPavel Emelyanov 
2797752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2798752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2799752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2800752d14dcSPavel Emelyanov };
2801752d14dcSPavel Emelyanov 
2802207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
28039f0f7272SThomas Graf 	.family		  = AF_INET,
28049f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
28059f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2806cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2807cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
28089f0f7272SThomas Graf };
28099f0f7272SThomas Graf 
devinet_init(void)28101da177e4SLinus Torvalds void __init devinet_init(void)
28111da177e4SLinus Torvalds {
2812fd23c3b3SDavid S. Miller 	int i;
2813fd23c3b3SDavid S. Miller 
2814fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2815fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2816fd23c3b3SDavid S. Miller 
2817752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
28181da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
281963f3444fSThomas Graf 
2820906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
28215c766d64SJiri Pirko 
28229f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
28239f0f7272SThomas Graf 
2824b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
2825b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
2826b97bac64SFlorian Westphal 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
28279e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
2828b97bac64SFlorian Westphal 		      inet_netconf_dump_devconf, 0);
28291da177e4SLinus Torvalds }
2830