xref: /linux/net/ipv4/devinet.c (revision b601fa197fff265bf60eaf6950d4c194da080f4a)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	NET3	IP device support routines.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
1002c30a84SJesper Juhl  * 		Authors:	Ross Biro
111da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
121da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Additional Authors:
151da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
161da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *	Changes:
191da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
201da177e4SLinus Torvalds  *					lists.
211da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
221da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
231da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
241da177e4SLinus Torvalds  *					fall back to comparing just the label
251da177e4SLinus Torvalds  *					if no match found.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <asm/uaccess.h>
301da177e4SLinus Torvalds #include <linux/bitops.h>
314fc268d2SRandy Dunlap #include <linux/capability.h>
321da177e4SLinus Torvalds #include <linux/module.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/kernel.h>
351da177e4SLinus Torvalds #include <linux/string.h>
361da177e4SLinus Torvalds #include <linux/mm.h>
371da177e4SLinus Torvalds #include <linux/socket.h>
381da177e4SLinus Torvalds #include <linux/sockios.h>
391da177e4SLinus Torvalds #include <linux/in.h>
401da177e4SLinus Torvalds #include <linux/errno.h>
411da177e4SLinus Torvalds #include <linux/interrupt.h>
421823730fSThomas Graf #include <linux/if_addr.h>
431da177e4SLinus Torvalds #include <linux/if_ether.h>
441da177e4SLinus Torvalds #include <linux/inet.h>
451da177e4SLinus Torvalds #include <linux/netdevice.h>
461da177e4SLinus Torvalds #include <linux/etherdevice.h>
471da177e4SLinus Torvalds #include <linux/skbuff.h>
481da177e4SLinus Torvalds #include <linux/init.h>
491da177e4SLinus Torvalds #include <linux/notifier.h>
501da177e4SLinus Torvalds #include <linux/inetdevice.h>
511da177e4SLinus Torvalds #include <linux/igmp.h>
525a0e3ad6STejun Heo #include <linux/slab.h>
53fd23c3b3SDavid S. Miller #include <linux/hash.h>
541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
551da177e4SLinus Torvalds #include <linux/sysctl.h>
561da177e4SLinus Torvalds #endif
571da177e4SLinus Torvalds #include <linux/kmod.h>
58edc9e748SNicolas Dichtel #include <linux/netconf.h>
591da177e4SLinus Torvalds 
6014c85021SArnaldo Carvalho de Melo #include <net/arp.h>
611da177e4SLinus Torvalds #include <net/ip.h>
621da177e4SLinus Torvalds #include <net/route.h>
631da177e4SLinus Torvalds #include <net/ip_fib.h>
6463f3444fSThomas Graf #include <net/rtnetlink.h>
65752d14dcSPavel Emelyanov #include <net/net_namespace.h>
665c766d64SJiri Pirko #include <net/addrconf.h>
671da177e4SLinus Torvalds 
68406b6f97SDavid S. Miller #include "fib_lookup.h"
69406b6f97SDavid S. Miller 
700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7142f811b8SHerbert Xu 	.data = {
7202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
772690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
7842f811b8SHerbert Xu 	},
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8242f811b8SHerbert Xu 	.data = {
8302291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
882690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
9042f811b8SHerbert Xu 	},
911da177e4SLinus Torvalds };
921da177e4SLinus Torvalds 
939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
949355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9542f811b8SHerbert Xu 
96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
975c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
985c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
995c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1005176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1015c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
102ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
1035c753978SThomas Graf };
1045c753978SThomas Graf 
10540384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
10640384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
10740384999SEric Dumazet 
108fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
109fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock);
110fd23c3b3SDavid S. Miller 
11140384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr)
112fd23c3b3SDavid S. Miller {
11340384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
114fd23c3b3SDavid S. Miller 
11540384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
116fd23c3b3SDavid S. Miller }
117fd23c3b3SDavid S. Miller 
118fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
119fd23c3b3SDavid S. Miller {
12040384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
121fd23c3b3SDavid S. Miller 
122fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
123fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
124fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
125fd23c3b3SDavid S. Miller }
126fd23c3b3SDavid S. Miller 
127fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
128fd23c3b3SDavid S. Miller {
129fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
130fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
131fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
132fd23c3b3SDavid S. Miller }
133fd23c3b3SDavid S. Miller 
1349435eb1cSDavid S. Miller /**
1359435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1369435eb1cSDavid S. Miller  * @net: the net namespace
1379435eb1cSDavid S. Miller  * @addr: the source address
1389435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1399435eb1cSDavid S. Miller  *
1409435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1419435eb1cSDavid S. Miller  */
1429435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1439435eb1cSDavid S. Miller {
14440384999SEric Dumazet 	u32 hash = inet_addr_hash(net, addr);
1459435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1469435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1479435eb1cSDavid S. Miller 
1489435eb1cSDavid S. Miller 	rcu_read_lock();
149b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) {
15040384999SEric Dumazet 		if (ifa->ifa_local == addr) {
1519435eb1cSDavid S. Miller 			struct net_device *dev = ifa->ifa_dev->dev;
1529435eb1cSDavid S. Miller 
1539435eb1cSDavid S. Miller 			if (!net_eq(dev_net(dev), net))
1549435eb1cSDavid S. Miller 				continue;
1559435eb1cSDavid S. Miller 			result = dev;
1569435eb1cSDavid S. Miller 			break;
1579435eb1cSDavid S. Miller 		}
1589435eb1cSDavid S. Miller 	}
159406b6f97SDavid S. Miller 	if (!result) {
160406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
161406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
162406b6f97SDavid S. Miller 		struct fib_table *local;
163406b6f97SDavid S. Miller 
164406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
165406b6f97SDavid S. Miller 		 * over loopback subnets work.
166406b6f97SDavid S. Miller 		 */
167406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
168406b6f97SDavid S. Miller 		if (local &&
169406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
170406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
171406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
172406b6f97SDavid S. Miller 	}
1739435eb1cSDavid S. Miller 	if (result && devref)
1749435eb1cSDavid S. Miller 		dev_hold(result);
1759435eb1cSDavid S. Miller 	rcu_read_unlock();
1769435eb1cSDavid S. Miller 	return result;
1779435eb1cSDavid S. Miller }
1789435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1799435eb1cSDavid S. Miller 
180d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1811da177e4SLinus Torvalds 
182e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1831da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1841da177e4SLinus Torvalds 			 int destroy);
1851da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
18666f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev);
18751602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
18851602b2aSPavel Emelyanov #else
18940384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev)
19051602b2aSPavel Emelyanov {
19151602b2aSPavel Emelyanov }
19240384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
19351602b2aSPavel Emelyanov {
19451602b2aSPavel Emelyanov }
1951da177e4SLinus Torvalds #endif
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /* Locks all the inet devices. */
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
2001da177e4SLinus Torvalds {
20193adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2051da177e4SLinus Torvalds {
2061da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2071da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2081da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2091da177e4SLinus Torvalds 	kfree(ifa);
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
21240384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2131da177e4SLinus Torvalds {
2141da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2201da177e4SLinus Torvalds 
221547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
222547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
223e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2241da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
22591df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2261da177e4SLinus Torvalds #endif
2271da177e4SLinus Torvalds 	dev_put(dev);
2281da177e4SLinus Torvalds 	if (!idev->dead)
2299f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2309f9354b9SEric Dumazet 	else
2311da177e4SLinus Torvalds 		kfree(idev);
2321da177e4SLinus Torvalds }
2339f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2341da177e4SLinus Torvalds 
23571e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds 	struct in_device *in_dev;
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds 	ASSERT_RTNL();
2401da177e4SLinus Torvalds 
2410da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2421da177e4SLinus Torvalds 	if (!in_dev)
2431da177e4SLinus Torvalds 		goto out;
244c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2459355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2461da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2471da177e4SLinus Torvalds 	in_dev->dev = dev;
2489f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2499f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2501da177e4SLinus Torvalds 		goto out_kfree;
2510187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2520187bdfbSBen Hutchings 		dev_disable_lro(dev);
2531da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2541da177e4SLinus Torvalds 	dev_hold(dev);
25530c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2561da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2571da177e4SLinus Torvalds 
25866f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2591da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2601da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2611da177e4SLinus Torvalds 		ip_mc_up(in_dev);
262483479ecSJarek Poplawski 
26330c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
264cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
265483479ecSJarek Poplawski out:
2661da177e4SLinus Torvalds 	return in_dev;
2671da177e4SLinus Torvalds out_kfree:
2681da177e4SLinus Torvalds 	kfree(in_dev);
2691da177e4SLinus Torvalds 	in_dev = NULL;
2701da177e4SLinus Torvalds 	goto out;
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2761da177e4SLinus Torvalds 	in_dev_put(idev);
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2801da177e4SLinus Torvalds {
2811da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2821da177e4SLinus Torvalds 	struct net_device *dev;
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	ASSERT_RTNL();
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	dev = in_dev->dev;
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	in_dev->dead = 1;
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2931da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2941da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2951da177e4SLinus Torvalds 	}
2961da177e4SLinus Torvalds 
297a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
2981da177e4SLinus Torvalds 
29951602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3001da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3011da177e4SLinus Torvalds 	arp_ifdown(dev);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
306ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3071da177e4SLinus Torvalds {
3081da177e4SLinus Torvalds 	rcu_read_lock();
3091da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3101da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3111da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3121da177e4SLinus Torvalds 				rcu_read_unlock();
3131da177e4SLinus Torvalds 				return 1;
3141da177e4SLinus Torvalds 			}
3151da177e4SLinus Torvalds 		}
3161da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3171da177e4SLinus Torvalds 	rcu_read_unlock();
3181da177e4SLinus Torvalds 	return 0;
3191da177e4SLinus Torvalds }
3201da177e4SLinus Torvalds 
321d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
32215e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3231da177e4SLinus Torvalds {
3248f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3250ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3260ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3270ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3280ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	ASSERT_RTNL();
3311da177e4SLinus Torvalds 
3328f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3338f937c60SHarald Welte 	 * unless alias promotion is set
3348f937c60SHarald Welte 	 **/
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3371da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3400ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3410ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3420ff60a45SJamal Hadi Salim 				last_prim = ifa;
3430ff60a45SJamal Hadi Salim 
3441da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3451da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3461da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3471da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3480ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3491da177e4SLinus Torvalds 				continue;
3501da177e4SLinus Torvalds 			}
3511da177e4SLinus Torvalds 
3520ff60a45SJamal Hadi Salim 			if (!do_promote) {
353fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3541da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3551da177e4SLinus Torvalds 
35615e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
357e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
358e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3591da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3608f937c60SHarald Welte 			} else {
3618f937c60SHarald Welte 				promote = ifa;
3628f937c60SHarald Welte 				break;
3638f937c60SHarald Welte 			}
3641da177e4SLinus Torvalds 		}
3651da177e4SLinus Torvalds 	}
3661da177e4SLinus Torvalds 
3672d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3682d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3692d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3702d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3712d230e2bSJulian Anastasov 	 */
3722d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3732d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3742d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3752d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3762d230e2bSJulian Anastasov 	}
3772d230e2bSJulian Anastasov 
3781da177e4SLinus Torvalds 	/* 2. Unlink it */
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
381fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3821da177e4SLinus Torvalds 
3831da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3861da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3871da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3881da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3891da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3901da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3911da177e4SLinus Torvalds 	   So that, this order is correct.
3921da177e4SLinus Torvalds 	 */
39315e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
394e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3950ff60a45SJamal Hadi Salim 
3960ff60a45SJamal Hadi Salim 	if (promote) {
39704024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
3980ff60a45SJamal Hadi Salim 
3990ff60a45SJamal Hadi Salim 		if (prev_prom) {
4000ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
4010ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
4020ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4030ff60a45SJamal Hadi Salim 		}
4040ff60a45SJamal Hadi Salim 
4050ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
40615e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
407e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
408e041c683SAlan Stern 				NETDEV_UP, promote);
40904024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4100ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4110ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4120ff60a45SJamal Hadi Salim 					continue;
4130ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4140ff60a45SJamal Hadi Salim 		}
4150ff60a45SJamal Hadi Salim 
4160ff60a45SJamal Hadi Salim 	}
4176363097cSHerbert Xu 	if (destroy)
4181da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4191da177e4SLinus Torvalds }
4201da177e4SLinus Torvalds 
421d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
422d6062cbbSThomas Graf 			 int destroy)
423d6062cbbSThomas Graf {
424d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
425d6062cbbSThomas Graf }
426d6062cbbSThomas Graf 
4275c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4285c766d64SJiri Pirko 
4295c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4305c766d64SJiri Pirko 
431d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
43215e47304SEric W. Biederman 			     u32 portid)
4331da177e4SLinus Torvalds {
4341da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4351da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	ASSERT_RTNL();
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4401da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4411da177e4SLinus Torvalds 		return 0;
4421da177e4SLinus Torvalds 	}
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4451da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4481da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4491da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4501da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4511da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4521da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4531da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4541da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4551da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4561da177e4SLinus Torvalds 				return -EEXIST;
4571da177e4SLinus Torvalds 			}
4581da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4591da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4601da177e4SLinus Torvalds 				return -EINVAL;
4611da177e4SLinus Torvalds 			}
4621da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4631da177e4SLinus Torvalds 		}
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
4671da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4681da177e4SLinus Torvalds 		ifap = last_primary;
4691da177e4SLinus Torvalds 	}
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4721da177e4SLinus Torvalds 	*ifap = ifa;
4731da177e4SLinus Torvalds 
474fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
475fd23c3b3SDavid S. Miller 
4765c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
4775c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, 0);
4785c766d64SJiri Pirko 
4791da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4801da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4811da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
48215e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
483e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	return 0;
4861da177e4SLinus Torvalds }
4871da177e4SLinus Torvalds 
488d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
489d6062cbbSThomas Graf {
490d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
491d6062cbbSThomas Graf }
492d6062cbbSThomas Graf 
4931da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4941da177e4SLinus Torvalds {
495e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	ASSERT_RTNL();
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 	if (!in_dev) {
5001da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5011da177e4SLinus Torvalds 		return -ENOBUFS;
5021da177e4SLinus Torvalds 	}
50371e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5041d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5051da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
506547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5071da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5081da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5091da177e4SLinus Torvalds 	}
510f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5111da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5121da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5131da177e4SLinus Torvalds }
5141da177e4SLinus Torvalds 
5158723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5168723e1b4SEric Dumazet  * We dont take a reference on found in_device
5178723e1b4SEric Dumazet  */
5187fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5191da177e4SLinus Torvalds {
5201da177e4SLinus Torvalds 	struct net_device *dev;
5211da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
522c148fc2eSEric Dumazet 
523c148fc2eSEric Dumazet 	rcu_read_lock();
524c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5251da177e4SLinus Torvalds 	if (dev)
5268723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
527c148fc2eSEric Dumazet 	rcu_read_unlock();
5281da177e4SLinus Torvalds 	return in_dev;
5291da177e4SLinus Torvalds }
5309f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5331da177e4SLinus Torvalds 
53460cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
53560cad5daSAl Viro 				    __be32 mask)
5361da177e4SLinus Torvalds {
5371da177e4SLinus Torvalds 	ASSERT_RTNL();
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5401da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5411da177e4SLinus Torvalds 			return ifa;
5421da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5431da177e4SLinus Torvalds 	return NULL;
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds 
546661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
5471da177e4SLinus Torvalds {
5483b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
549dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5501da177e4SLinus Torvalds 	struct in_device *in_dev;
551dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5521da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
553dfdd5fd4SThomas Graf 	int err = -EINVAL;
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	ASSERT_RTNL();
5561da177e4SLinus Torvalds 
557dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
558dfdd5fd4SThomas Graf 	if (err < 0)
559dfdd5fd4SThomas Graf 		goto errout;
560dfdd5fd4SThomas Graf 
561dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5627fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
563dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
564dfdd5fd4SThomas Graf 		err = -ENODEV;
565dfdd5fd4SThomas Graf 		goto errout;
566dfdd5fd4SThomas Graf 	}
567dfdd5fd4SThomas Graf 
5681da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5691da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
570dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
571a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5721da177e4SLinus Torvalds 			continue;
573dfdd5fd4SThomas Graf 
574dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
575dfdd5fd4SThomas Graf 			continue;
576dfdd5fd4SThomas Graf 
577dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
578dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
579a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
580dfdd5fd4SThomas Graf 			continue;
581dfdd5fd4SThomas Graf 
58215e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
5831da177e4SLinus Torvalds 		return 0;
5841da177e4SLinus Torvalds 	}
585dfdd5fd4SThomas Graf 
586dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
587dfdd5fd4SThomas Graf errout:
588dfdd5fd4SThomas Graf 	return err;
5891da177e4SLinus Torvalds }
5901da177e4SLinus Torvalds 
5915c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
5925c766d64SJiri Pirko 
5935c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
5945c766d64SJiri Pirko {
5955c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
5965c766d64SJiri Pirko 	struct in_ifaddr *ifa;
597c988d1e8SJiri Pirko 	struct hlist_node *n;
5985c766d64SJiri Pirko 	int i;
5995c766d64SJiri Pirko 
6005c766d64SJiri Pirko 	now = jiffies;
6015c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6025c766d64SJiri Pirko 
6035c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
604c988d1e8SJiri Pirko 		bool change_needed = false;
605c988d1e8SJiri Pirko 
606c988d1e8SJiri Pirko 		rcu_read_lock();
607b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6085c766d64SJiri Pirko 			unsigned long age;
6095c766d64SJiri Pirko 
6105c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6115c766d64SJiri Pirko 				continue;
6125c766d64SJiri Pirko 
6135c766d64SJiri Pirko 			/* We try to batch several events at once. */
6145c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6155c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6165c766d64SJiri Pirko 
6175c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6185c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
619c988d1e8SJiri Pirko 				change_needed = true;
620c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
621c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
622c988d1e8SJiri Pirko 				continue;
623c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
624c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
625c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
626c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
627c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
628c988d1e8SJiri Pirko 
629c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
630c988d1e8SJiri Pirko 					change_needed = true;
631c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
632c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
633c988d1e8SJiri Pirko 					       next)) {
634c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
635c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
636c988d1e8SJiri Pirko 			}
637c988d1e8SJiri Pirko 		}
638c988d1e8SJiri Pirko 		rcu_read_unlock();
639c988d1e8SJiri Pirko 		if (!change_needed)
640c988d1e8SJiri Pirko 			continue;
641c988d1e8SJiri Pirko 		rtnl_lock();
642c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
643c988d1e8SJiri Pirko 			unsigned long age;
644c988d1e8SJiri Pirko 
645c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
646c988d1e8SJiri Pirko 				continue;
647c988d1e8SJiri Pirko 
648c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
649c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
650c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
651c988d1e8SJiri Pirko 
652c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
653c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
6545c766d64SJiri Pirko 				struct in_ifaddr **ifap;
6555c766d64SJiri Pirko 
6565c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
657c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
658c988d1e8SJiri Pirko 					if (*ifap == ifa) {
6595c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
6605c766d64SJiri Pirko 							     ifap, 1);
661c988d1e8SJiri Pirko 						break;
6625c766d64SJiri Pirko 					}
663c988d1e8SJiri Pirko 				}
664c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
665c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
666c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
667c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
6685c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
6695c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
6705c766d64SJiri Pirko 			}
6715c766d64SJiri Pirko 		}
672c988d1e8SJiri Pirko 		rtnl_unlock();
6735c766d64SJiri Pirko 	}
6745c766d64SJiri Pirko 
6755c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
6765c766d64SJiri Pirko 	next_sched = next;
6775c766d64SJiri Pirko 
6785c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
6795c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
6805c766d64SJiri Pirko 		next_sched = next_sec;
6815c766d64SJiri Pirko 
6825c766d64SJiri Pirko 	now = jiffies;
6835c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
6845c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
6855c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
6865c766d64SJiri Pirko 
6875c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, next_sched - now);
6885c766d64SJiri Pirko }
6895c766d64SJiri Pirko 
6905c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
6915c766d64SJiri Pirko 			     __u32 prefered_lft)
6925c766d64SJiri Pirko {
6935c766d64SJiri Pirko 	unsigned long timeout;
6945c766d64SJiri Pirko 
6955c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
6965c766d64SJiri Pirko 
6975c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
6985c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
6995c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7005c766d64SJiri Pirko 	else
7015c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
7025c766d64SJiri Pirko 
7035c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
7045c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
7055c766d64SJiri Pirko 		if (timeout == 0)
7065c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7075c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7085c766d64SJiri Pirko 	}
7095c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7105c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7115c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7125c766d64SJiri Pirko }
7135c766d64SJiri Pirko 
7145c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
7155c766d64SJiri Pirko 				       __u32 *pvalid_lft, __u32 *pprefered_lft)
7161da177e4SLinus Torvalds {
7175c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7185c753978SThomas Graf 	struct in_ifaddr *ifa;
7195c753978SThomas Graf 	struct ifaddrmsg *ifm;
7201da177e4SLinus Torvalds 	struct net_device *dev;
7211da177e4SLinus Torvalds 	struct in_device *in_dev;
7227b218574SDenis V. Lunev 	int err;
7231da177e4SLinus Torvalds 
7245c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
7255c753978SThomas Graf 	if (err < 0)
7265c753978SThomas Graf 		goto errout;
7271da177e4SLinus Torvalds 
7285c753978SThomas Graf 	ifm = nlmsg_data(nlh);
729c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
7307b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
7315c753978SThomas Graf 		goto errout;
7321da177e4SLinus Torvalds 
7334b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
7345c753978SThomas Graf 	err = -ENODEV;
7357b218574SDenis V. Lunev 	if (dev == NULL)
7365c753978SThomas Graf 		goto errout;
7371da177e4SLinus Torvalds 
7385c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
7395c753978SThomas Graf 	err = -ENOBUFS;
7407b218574SDenis V. Lunev 	if (in_dev == NULL)
7415c753978SThomas Graf 		goto errout;
74271e27da9SHerbert Xu 
7435c753978SThomas Graf 	ifa = inet_alloc_ifa();
7447b218574SDenis V. Lunev 	if (ifa == NULL)
7455c753978SThomas Graf 		/*
7465c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
7475c753978SThomas Graf 		 * assigned to its device and is destroy with it.
7485c753978SThomas Graf 		 */
7495c753978SThomas Graf 		goto errout;
7505c753978SThomas Graf 
751a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
7521d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
7535c753978SThomas Graf 	in_dev_hold(in_dev);
7545c753978SThomas Graf 
7555c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
7565c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
7575c753978SThomas Graf 
758fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
7591da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
7601da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
761ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
762ad6c8135SJiri Pirko 					 ifm->ifa_flags;
7631da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
7641da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
7655c753978SThomas Graf 
766a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
767a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
7685c753978SThomas Graf 
7695c753978SThomas Graf 	if (tb[IFA_BROADCAST])
770a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
7715c753978SThomas Graf 
7725c753978SThomas Graf 	if (tb[IFA_LABEL])
7735c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
7741da177e4SLinus Torvalds 	else
7751da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
7761da177e4SLinus Torvalds 
7775c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
7785c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
7795c766d64SJiri Pirko 
7805c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
7815c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
7825c766d64SJiri Pirko 			err = -EINVAL;
783446266b0SDaniel Borkmann 			goto errout_free;
7845c766d64SJiri Pirko 		}
7855c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
7865c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
7875c766d64SJiri Pirko 	}
7885c766d64SJiri Pirko 
7895c753978SThomas Graf 	return ifa;
7905c753978SThomas Graf 
791446266b0SDaniel Borkmann errout_free:
792446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
7935c753978SThomas Graf errout:
7945c753978SThomas Graf 	return ERR_PTR(err);
7955c753978SThomas Graf }
7965c753978SThomas Graf 
7975c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
7985c766d64SJiri Pirko {
7995c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8005c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8015c766d64SJiri Pirko 
8025c766d64SJiri Pirko 	if (!ifa->ifa_local)
8035c766d64SJiri Pirko 		return NULL;
8045c766d64SJiri Pirko 
8055c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8065c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8075c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8085c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8095c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8105c766d64SJiri Pirko 			return ifa1;
8115c766d64SJiri Pirko 	}
8125c766d64SJiri Pirko 	return NULL;
8135c766d64SJiri Pirko }
8145c766d64SJiri Pirko 
815661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
8165c753978SThomas Graf {
8173b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8185c753978SThomas Graf 	struct in_ifaddr *ifa;
8195c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8205c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8215c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8225c753978SThomas Graf 
8235c753978SThomas Graf 	ASSERT_RTNL();
8245c753978SThomas Graf 
8255c766d64SJiri Pirko 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
8265c753978SThomas Graf 	if (IS_ERR(ifa))
8275c753978SThomas Graf 		return PTR_ERR(ifa);
8285c753978SThomas Graf 
8295c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
8305c766d64SJiri Pirko 	if (!ifa_existing) {
8315c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
8325c766d64SJiri Pirko 		 * userspace alreay relies on not having to provide this.
8335c766d64SJiri Pirko 		 */
8345c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
83515e47304SEric W. Biederman 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
8365c766d64SJiri Pirko 	} else {
8375c766d64SJiri Pirko 		inet_free_ifa(ifa);
8385c766d64SJiri Pirko 
8395c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
8405c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
8415c766d64SJiri Pirko 			return -EEXIST;
84234e2ed34SJiri Pirko 		ifa = ifa_existing;
84334e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
84405a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
84505a324b9SJiri Pirko 		schedule_delayed_work(&check_lifetime_work, 0);
84634e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
84734e2ed34SJiri Pirko 		blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
8485c766d64SJiri Pirko 	}
8495c766d64SJiri Pirko 	return 0;
8501da177e4SLinus Torvalds }
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds /*
8531da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
8541da177e4SLinus Torvalds  */
8551da177e4SLinus Torvalds 
85640384999SEric Dumazet static int inet_abc_len(__be32 addr)
8571da177e4SLinus Torvalds {
8581da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
8591da177e4SLinus Torvalds 
860f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
8611da177e4SLinus Torvalds 		rc = 0;
8621da177e4SLinus Torvalds 	else {
863714e85beSAl Viro 		__u32 haddr = ntohl(addr);
8641da177e4SLinus Torvalds 
865714e85beSAl Viro 		if (IN_CLASSA(haddr))
8661da177e4SLinus Torvalds 			rc = 8;
867714e85beSAl Viro 		else if (IN_CLASSB(haddr))
8681da177e4SLinus Torvalds 			rc = 16;
869714e85beSAl Viro 		else if (IN_CLASSC(haddr))
8701da177e4SLinus Torvalds 			rc = 24;
8711da177e4SLinus Torvalds 	}
8721da177e4SLinus Torvalds 
8731da177e4SLinus Torvalds 	return rc;
8741da177e4SLinus Torvalds }
8751da177e4SLinus Torvalds 
8761da177e4SLinus Torvalds 
877e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
8781da177e4SLinus Torvalds {
8791da177e4SLinus Torvalds 	struct ifreq ifr;
8801da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
8811da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
8821da177e4SLinus Torvalds 	struct in_device *in_dev;
8831da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
8841da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
8851da177e4SLinus Torvalds 	struct net_device *dev;
8861da177e4SLinus Torvalds 	char *colon;
8871da177e4SLinus Torvalds 	int ret = -EFAULT;
8881da177e4SLinus Torvalds 	int tryaddrmatch = 0;
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds 	/*
8911da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
8921da177e4SLinus Torvalds 	 */
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
8951da177e4SLinus Torvalds 		goto out;
8961da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
8971da177e4SLinus Torvalds 
8981da177e4SLinus Torvalds 	/* save original address for comparison */
8991da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
9021da177e4SLinus Torvalds 	if (colon)
9031da177e4SLinus Torvalds 		*colon = 0;
9041da177e4SLinus Torvalds 
905e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 	switch (cmd) {
9081da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9091da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9101da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9111da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9121da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
9131da177e4SLinus Torvalds 		   so that we do not impose a lock.
9141da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
9151da177e4SLinus Torvalds 		 */
9161da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
9171da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
9181da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
9191da177e4SLinus Torvalds 		break;
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
922bf5b30b8SZhao Hongjiang 		ret = -EPERM;
92352e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9241da177e4SLinus Torvalds 			goto out;
9251da177e4SLinus Torvalds 		break;
9261da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
9271da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
9281da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
9291da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
930bf5b30b8SZhao Hongjiang 		ret = -EPERM;
93152e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9321da177e4SLinus Torvalds 			goto out;
9331da177e4SLinus Torvalds 		ret = -EINVAL;
9341da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
9351da177e4SLinus Torvalds 			goto out;
9361da177e4SLinus Torvalds 		break;
9371da177e4SLinus Torvalds 	default:
9381da177e4SLinus Torvalds 		ret = -EINVAL;
9391da177e4SLinus Torvalds 		goto out;
9401da177e4SLinus Torvalds 	}
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 	rtnl_lock();
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 	ret = -ENODEV;
9459f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
9469f9354b9SEric Dumazet 	if (!dev)
9471da177e4SLinus Torvalds 		goto done;
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 	if (colon)
9501da177e4SLinus Torvalds 		*colon = ':';
9511da177e4SLinus Torvalds 
9529f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
9539f9354b9SEric Dumazet 	if (in_dev) {
9541da177e4SLinus Torvalds 		if (tryaddrmatch) {
9551da177e4SLinus Torvalds 			/* Matthias Andree */
9561da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
9571da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
9581da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
9591da177e4SLinus Torvalds 			   This is checked above. */
9601da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
9611da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
9621da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
9631da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
9646c91afe1SDavid S. Miller 							ifa->ifa_local) {
9651da177e4SLinus Torvalds 					break; /* found */
9661da177e4SLinus Torvalds 				}
9671da177e4SLinus Torvalds 			}
9681da177e4SLinus Torvalds 		}
9691da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
9701da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
9711da177e4SLinus Torvalds 		   comparing just the label */
9721da177e4SLinus Torvalds 		if (!ifa) {
9731da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
9741da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
9751da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
9761da177e4SLinus Torvalds 					break;
9771da177e4SLinus Torvalds 		}
9781da177e4SLinus Torvalds 	}
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
9811da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
9821da177e4SLinus Torvalds 		goto done;
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	switch (cmd) {
9851da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9861da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
9871da177e4SLinus Torvalds 		goto rarok;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9901da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
9911da177e4SLinus Torvalds 		goto rarok;
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9941da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
9951da177e4SLinus Torvalds 		goto rarok;
9961da177e4SLinus Torvalds 
9971da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9981da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
9991da177e4SLinus Torvalds 		goto rarok;
10001da177e4SLinus Torvalds 
10011da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10021da177e4SLinus Torvalds 		if (colon) {
10031da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
10041da177e4SLinus Torvalds 			if (!ifa)
10051da177e4SLinus Torvalds 				break;
10061da177e4SLinus Torvalds 			ret = 0;
10071da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
10081da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
10091da177e4SLinus Torvalds 			break;
10101da177e4SLinus Torvalds 		}
10111da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
10121da177e4SLinus Torvalds 		break;
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10151da177e4SLinus Torvalds 		ret = -EINVAL;
10161da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10171da177e4SLinus Torvalds 			break;
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds 		if (!ifa) {
10201da177e4SLinus Torvalds 			ret = -ENOBUFS;
10219f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
10229f9354b9SEric Dumazet 			if (!ifa)
10231da177e4SLinus Torvalds 				break;
1024c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
10251da177e4SLinus Torvalds 			if (colon)
10261da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
10271da177e4SLinus Torvalds 			else
10281da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
10291da177e4SLinus Torvalds 		} else {
10301da177e4SLinus Torvalds 			ret = 0;
10311da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
10321da177e4SLinus Torvalds 				break;
10331da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10341da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1035148f9729SBjorn Mork 			ifa->ifa_scope = 0;
10361da177e4SLinus Torvalds 		}
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
10391da177e4SLinus Torvalds 
10401da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
10411da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
10421da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
10431da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10441da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
10451da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
10461da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
10471da177e4SLinus Torvalds 		} else {
10481da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
10491da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
10501da177e4SLinus Torvalds 		}
10515c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
10521da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
10531da177e4SLinus Torvalds 		break;
10541da177e4SLinus Torvalds 
10551da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10561da177e4SLinus Torvalds 		ret = 0;
10571da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
10581da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10591da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
10601da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
10611da177e4SLinus Torvalds 		}
10621da177e4SLinus Torvalds 		break;
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10651da177e4SLinus Torvalds 		ret = 0;
10661da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
10671da177e4SLinus Torvalds 			break;
10681da177e4SLinus Torvalds 		ret = -EINVAL;
10691da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10701da177e4SLinus Torvalds 			break;
10711da177e4SLinus Torvalds 		ret = 0;
10721da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
10731da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
10741da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
10751da177e4SLinus Torvalds 		break;
10761da177e4SLinus Torvalds 
10771da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 		/*
10801da177e4SLinus Torvalds 		 *	The mask we set must be legal.
10811da177e4SLinus Torvalds 		 */
10821da177e4SLinus Torvalds 		ret = -EINVAL;
10831da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
10841da177e4SLinus Torvalds 			break;
10851da177e4SLinus Torvalds 		ret = 0;
10861da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1087a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
10881da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10891da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
10901da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
10911da177e4SLinus Torvalds 
10921da177e4SLinus Torvalds 			/* See if current broadcast address matches
10931da177e4SLinus Torvalds 			 * with current netmask, then recalculate
10941da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
10951da177e4SLinus Torvalds 			 * funny address, so don't touch it since
10961da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
10971da177e4SLinus Torvalds 			 */
10981da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10991da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11001da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1101dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11021da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
11031da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
11041da177e4SLinus Torvalds 			}
11051da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11061da177e4SLinus Torvalds 		}
11071da177e4SLinus Torvalds 		break;
11081da177e4SLinus Torvalds 	}
11091da177e4SLinus Torvalds done:
11101da177e4SLinus Torvalds 	rtnl_unlock();
11111da177e4SLinus Torvalds out:
11121da177e4SLinus Torvalds 	return ret;
11131da177e4SLinus Torvalds rarok:
11141da177e4SLinus Torvalds 	rtnl_unlock();
11151da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
11161da177e4SLinus Torvalds 	goto out;
11171da177e4SLinus Torvalds }
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
11201da177e4SLinus Torvalds {
1121e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11221da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
11231da177e4SLinus Torvalds 	struct ifreq ifr;
11241da177e4SLinus Torvalds 	int done = 0;
11251da177e4SLinus Torvalds 
11269f9354b9SEric Dumazet 	if (!in_dev)
11271da177e4SLinus Torvalds 		goto out;
11281da177e4SLinus Torvalds 
11299f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
11301da177e4SLinus Torvalds 		if (!buf) {
11311da177e4SLinus Torvalds 			done += sizeof(ifr);
11321da177e4SLinus Torvalds 			continue;
11331da177e4SLinus Torvalds 		}
11341da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
11351da177e4SLinus Torvalds 			break;
11361da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
11371da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
11381da177e4SLinus Torvalds 
11391da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
11401da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
11411da177e4SLinus Torvalds 								ifa->ifa_local;
11421da177e4SLinus Torvalds 
11431da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
11441da177e4SLinus Torvalds 			done = -EFAULT;
11451da177e4SLinus Torvalds 			break;
11461da177e4SLinus Torvalds 		}
11471da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
11481da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
11491da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
11501da177e4SLinus Torvalds 	}
11511da177e4SLinus Torvalds out:
11521da177e4SLinus Torvalds 	return done;
11531da177e4SLinus Torvalds }
11541da177e4SLinus Torvalds 
1155a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
11561da177e4SLinus Torvalds {
1157a61ced5dSAl Viro 	__be32 addr = 0;
11581da177e4SLinus Torvalds 	struct in_device *in_dev;
1159c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds 	rcu_read_lock();
1162e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
11631da177e4SLinus Torvalds 	if (!in_dev)
11641da177e4SLinus Torvalds 		goto no_in_dev;
11651da177e4SLinus Torvalds 
11661da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
11671da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
11681da177e4SLinus Torvalds 			continue;
11691da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
11701da177e4SLinus Torvalds 			addr = ifa->ifa_local;
11711da177e4SLinus Torvalds 			break;
11721da177e4SLinus Torvalds 		}
11731da177e4SLinus Torvalds 		if (!addr)
11741da177e4SLinus Torvalds 			addr = ifa->ifa_local;
11751da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
11761da177e4SLinus Torvalds 
11771da177e4SLinus Torvalds 	if (addr)
1178c6d14c84SEric Dumazet 		goto out_unlock;
11799f9354b9SEric Dumazet no_in_dev:
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
11821da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
11831da177e4SLinus Torvalds 	   in dev_base list.
11841da177e4SLinus Torvalds 	 */
1185c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
11869f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
11879f9354b9SEric Dumazet 		if (!in_dev)
11881da177e4SLinus Torvalds 			continue;
11891da177e4SLinus Torvalds 
11901da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
11911da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
11921da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
11931da177e4SLinus Torvalds 				addr = ifa->ifa_local;
1194c6d14c84SEric Dumazet 				goto out_unlock;
11951da177e4SLinus Torvalds 			}
11961da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
11971da177e4SLinus Torvalds 	}
1198c6d14c84SEric Dumazet out_unlock:
11991da177e4SLinus Torvalds 	rcu_read_unlock();
12001da177e4SLinus Torvalds 	return addr;
12011da177e4SLinus Torvalds }
12029f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
12031da177e4SLinus Torvalds 
120460cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
120560cad5daSAl Viro 			      __be32 local, int scope)
12061da177e4SLinus Torvalds {
12071da177e4SLinus Torvalds 	int same = 0;
1208a144ea4bSAl Viro 	__be32 addr = 0;
12091da177e4SLinus Torvalds 
12101da177e4SLinus Torvalds 	for_ifa(in_dev) {
12111da177e4SLinus Torvalds 		if (!addr &&
12121da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
12131da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
12141da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12151da177e4SLinus Torvalds 			if (same)
12161da177e4SLinus Torvalds 				break;
12171da177e4SLinus Torvalds 		}
12181da177e4SLinus Torvalds 		if (!same) {
12191da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
12201da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
12211da177e4SLinus Torvalds 			if (same && addr) {
12221da177e4SLinus Torvalds 				if (local || !dst)
12231da177e4SLinus Torvalds 					break;
12241da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
12251da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
12261da177e4SLinus Torvalds 					break;
12271da177e4SLinus Torvalds 				/* No, then can we use new local src? */
12281da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
12291da177e4SLinus Torvalds 					addr = ifa->ifa_local;
12301da177e4SLinus Torvalds 					break;
12311da177e4SLinus Torvalds 				}
12321da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
12331da177e4SLinus Torvalds 				same = 0;
12341da177e4SLinus Torvalds 			}
12351da177e4SLinus Torvalds 		}
12361da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 	return same ? addr : 0;
12391da177e4SLinus Torvalds }
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds /*
12421da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1243*b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1244*b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
12451da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
12461da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
12471da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
12481da177e4SLinus Torvalds  */
1249*b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
12509bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
12511da177e4SLinus Torvalds {
125260cad5daSAl Viro 	__be32 addr = 0;
12539bd85e32SDenis V. Lunev 	struct net_device *dev;
12541da177e4SLinus Torvalds 
1255*b601fa19SNicolas Dichtel 	if (in_dev != NULL)
12569bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
12571da177e4SLinus Torvalds 
12581da177e4SLinus Torvalds 	rcu_read_lock();
1259c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
12609f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
12619f9354b9SEric Dumazet 		if (in_dev) {
12621da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
12631da177e4SLinus Torvalds 			if (addr)
12641da177e4SLinus Torvalds 				break;
12651da177e4SLinus Torvalds 		}
12661da177e4SLinus Torvalds 	}
12671da177e4SLinus Torvalds 	rcu_read_unlock();
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 	return addr;
12701da177e4SLinus Torvalds }
1271eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
12721da177e4SLinus Torvalds 
12731da177e4SLinus Torvalds /*
12741da177e4SLinus Torvalds  *	Device notifier
12751da177e4SLinus Torvalds  */
12761da177e4SLinus Torvalds 
12771da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
12781da177e4SLinus Torvalds {
1279e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
12801da177e4SLinus Torvalds }
12819f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
12821da177e4SLinus Torvalds 
12831da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
12841da177e4SLinus Torvalds {
1285e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
12861da177e4SLinus Torvalds }
12879f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
12881da177e4SLinus Torvalds 
12899f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
12909f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
12911da177e4SLinus Torvalds */
12921da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
12931da177e4SLinus Torvalds {
12941da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12951da177e4SLinus Torvalds 	int named = 0;
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12981da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
13011da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
13021da177e4SLinus Torvalds 		if (named++ == 0)
1303573bf470SThomas Graf 			goto skip;
130444344b2aSMark McLoughlin 		dot = strchr(old, ':');
13051da177e4SLinus Torvalds 		if (dot == NULL) {
13061da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
13071da177e4SLinus Torvalds 			dot = old;
13081da177e4SLinus Torvalds 		}
13099f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
13101da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
13119f9354b9SEric Dumazet 		else
13121da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1313573bf470SThomas Graf skip:
1314573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
13151da177e4SLinus Torvalds 	}
13161da177e4SLinus Torvalds }
13171da177e4SLinus Torvalds 
131840384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
131906770843SBreno Leitao {
132006770843SBreno Leitao 	return mtu >= 68;
132106770843SBreno Leitao }
132206770843SBreno Leitao 
1323d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1324d11327adSIan Campbell 					struct in_device *in_dev)
1325d11327adSIan Campbell 
1326d11327adSIan Campbell {
1327b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1328d11327adSIan Campbell 
1329b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1330b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1331d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
13326c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
13336c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1334d11327adSIan Campbell 			 dev->dev_addr, NULL);
1335d11327adSIan Campbell 	}
1336b76d0789SZoltan Kiss }
1337d11327adSIan Campbell 
13381da177e4SLinus Torvalds /* Called only under RTNL semaphore */
13391da177e4SLinus Torvalds 
13401da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
13411da177e4SLinus Torvalds 			 void *ptr)
13421da177e4SLinus Torvalds {
1343351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1344748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13451da177e4SLinus Torvalds 
13461da177e4SLinus Torvalds 	ASSERT_RTNL();
13471da177e4SLinus Torvalds 
13481da177e4SLinus Torvalds 	if (!in_dev) {
13498030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
13501da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
13518d76527eSHerbert Xu 			if (!in_dev)
1352b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
13530cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
135442f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
135542f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
13561da177e4SLinus Torvalds 			}
135706770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
135806770843SBreno Leitao 			/* Re-enabling IP */
135906770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
136006770843SBreno Leitao 				in_dev = inetdev_init(dev);
13618030f544SHerbert Xu 		}
13621da177e4SLinus Torvalds 		goto out;
13631da177e4SLinus Torvalds 	}
13641da177e4SLinus Torvalds 
13651da177e4SLinus Torvalds 	switch (event) {
13661da177e4SLinus Torvalds 	case NETDEV_REGISTER:
136791df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1368a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
13691da177e4SLinus Torvalds 		break;
13701da177e4SLinus Torvalds 	case NETDEV_UP:
137106770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
13721da177e4SLinus Torvalds 			break;
13730cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
13749f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
13759f9354b9SEric Dumazet 
13769f9354b9SEric Dumazet 			if (ifa) {
1377fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
13781da177e4SLinus Torvalds 				ifa->ifa_local =
13791da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
13801da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
13811da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
13821da177e4SLinus Torvalds 				in_dev_hold(in_dev);
13831da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
13841da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
13851da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
13865c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
13875c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
13881da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
13891da177e4SLinus Torvalds 			}
13901da177e4SLinus Torvalds 		}
13911da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1392eefef1cfSStephen Hemminger 		/* fall through */
1393eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1394d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1395d11327adSIan Campbell 			break;
1396d11327adSIan Campbell 		/* fall through */
1397d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1398a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1399d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
14001da177e4SLinus Torvalds 		break;
14011da177e4SLinus Torvalds 	case NETDEV_DOWN:
14021da177e4SLinus Torvalds 		ip_mc_down(in_dev);
14031da177e4SLinus Torvalds 		break;
140493d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
140575c78500SMoni Shoua 		ip_mc_unmap(in_dev);
140675c78500SMoni Shoua 		break;
140793d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
140875c78500SMoni Shoua 		ip_mc_remap(in_dev);
140975c78500SMoni Shoua 		break;
14101da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
141106770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
14121da177e4SLinus Torvalds 			break;
141306770843SBreno Leitao 		/* disable IP when MTU is not enough */
14141da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
14151da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
14161da177e4SLinus Torvalds 		break;
14171da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
14181da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
14191da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
14201da177e4SLinus Torvalds 		 */
14211da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
14221da177e4SLinus Torvalds 
142351602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
142466f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
14251da177e4SLinus Torvalds 		break;
14261da177e4SLinus Torvalds 	}
14271da177e4SLinus Torvalds out:
14281da177e4SLinus Torvalds 	return NOTIFY_DONE;
14291da177e4SLinus Torvalds }
14301da177e4SLinus Torvalds 
14311da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
14321da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
14331da177e4SLinus Torvalds };
14341da177e4SLinus Torvalds 
143540384999SEric Dumazet static size_t inet_nlmsg_size(void)
1436339bf98fSThomas Graf {
1437339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1438339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1439339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1440339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1441ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
1442ad6c8135SJiri Pirko 	       + nla_total_size(4);  /* IFA_FLAGS */
1443339bf98fSThomas Graf }
1444339bf98fSThomas Graf 
14455c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
14465c766d64SJiri Pirko {
14475c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
14485c766d64SJiri Pirko }
14495c766d64SJiri Pirko 
14505c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
14515c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
14525c766d64SJiri Pirko {
14535c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
14545c766d64SJiri Pirko 
14555c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
14565c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
14575c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
14585c766d64SJiri Pirko 	ci.ifa_valid = valid;
14595c766d64SJiri Pirko 
14605c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
14615c766d64SJiri Pirko }
14625c766d64SJiri Pirko 
14631da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
146415e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
14651da177e4SLinus Torvalds {
14661da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
14671da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
14685c766d64SJiri Pirko 	u32 preferred, valid;
14691da177e4SLinus Torvalds 
147015e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
147147f68512SThomas Graf 	if (nlh == NULL)
147226932566SPatrick McHardy 		return -EMSGSIZE;
147347f68512SThomas Graf 
147447f68512SThomas Graf 	ifm = nlmsg_data(nlh);
14751da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
14761da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
14775c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
14781da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
14791da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
14801da177e4SLinus Torvalds 
14815c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
14825c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
14835c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
14845c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
14855c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
14865c766d64SJiri Pirko 
14875c766d64SJiri Pirko 			if (preferred > tval)
14885c766d64SJiri Pirko 				preferred -= tval;
14895c766d64SJiri Pirko 			else
14905c766d64SJiri Pirko 				preferred = 0;
14915c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
14925c766d64SJiri Pirko 				if (valid > tval)
14935c766d64SJiri Pirko 					valid -= tval;
14945c766d64SJiri Pirko 				else
14955c766d64SJiri Pirko 					valid = 0;
14965c766d64SJiri Pirko 			}
14975c766d64SJiri Pirko 		}
14985c766d64SJiri Pirko 	} else {
14995c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
15005c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
15015c766d64SJiri Pirko 	}
1502f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1503f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1504f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1505f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) ||
1506f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1507f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1508f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
15095c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1510ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
15115c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
15125c766d64SJiri Pirko 			  preferred, valid))
1513f3756b79SDavid S. Miller 		goto nla_put_failure;
151447f68512SThomas Graf 
151547f68512SThomas Graf 	return nlmsg_end(skb, nlh);
151647f68512SThomas Graf 
151747f68512SThomas Graf nla_put_failure:
151826932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
151926932566SPatrick McHardy 	return -EMSGSIZE;
15201da177e4SLinus Torvalds }
15211da177e4SLinus Torvalds 
15221da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
15231da177e4SLinus Torvalds {
15243b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1525eec4df98SEric Dumazet 	int h, s_h;
1526eec4df98SEric Dumazet 	int idx, s_idx;
1527eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
15281da177e4SLinus Torvalds 	struct net_device *dev;
15291da177e4SLinus Torvalds 	struct in_device *in_dev;
15301da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1531eec4df98SEric Dumazet 	struct hlist_head *head;
15321da177e4SLinus Torvalds 
1533eec4df98SEric Dumazet 	s_h = cb->args[0];
1534eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1535eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1536eec4df98SEric Dumazet 
1537eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
15387562f876SPavel Emelianov 		idx = 0;
1539eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1540eec4df98SEric Dumazet 		rcu_read_lock();
15410465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
15420465277fSNicolas Dichtel 			  net->dev_base_seq;
1543b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
15441da177e4SLinus Torvalds 			if (idx < s_idx)
15457562f876SPavel Emelianov 				goto cont;
15464b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
15471da177e4SLinus Torvalds 				s_ip_idx = 0;
1548eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
15499f9354b9SEric Dumazet 			if (!in_dev)
15507562f876SPavel Emelianov 				goto cont;
15511da177e4SLinus Torvalds 
15521da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
15531da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
15541da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1555596e4150SStephen Hemminger 					continue;
1556eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
155715e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
15581da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1559eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1560eec4df98SEric Dumazet 					rcu_read_unlock();
15611da177e4SLinus Torvalds 					goto done;
15621da177e4SLinus Torvalds 				}
15630465277fSNicolas Dichtel 				nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1564eec4df98SEric Dumazet 			}
15657562f876SPavel Emelianov cont:
15667562f876SPavel Emelianov 			idx++;
15671da177e4SLinus Torvalds 		}
1568eec4df98SEric Dumazet 		rcu_read_unlock();
1569eec4df98SEric Dumazet 	}
15701da177e4SLinus Torvalds 
15711da177e4SLinus Torvalds done:
1572eec4df98SEric Dumazet 	cb->args[0] = h;
1573eec4df98SEric Dumazet 	cb->args[1] = idx;
1574eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
15751da177e4SLinus Torvalds 
15761da177e4SLinus Torvalds 	return skb->len;
15771da177e4SLinus Torvalds }
15781da177e4SLinus Torvalds 
1579d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
158015e47304SEric W. Biederman 		      u32 portid)
15811da177e4SLinus Torvalds {
158247f68512SThomas Graf 	struct sk_buff *skb;
1583d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1584d6062cbbSThomas Graf 	int err = -ENOBUFS;
15854b8aa9abSDenis V. Lunev 	struct net *net;
15861da177e4SLinus Torvalds 
1587c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1588339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
158947f68512SThomas Graf 	if (skb == NULL)
1590d6062cbbSThomas Graf 		goto errout;
1591d6062cbbSThomas Graf 
159215e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
159326932566SPatrick McHardy 	if (err < 0) {
159426932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
159526932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
159626932566SPatrick McHardy 		kfree_skb(skb);
159726932566SPatrick McHardy 		goto errout;
159826932566SPatrick McHardy 	}
159915e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
16001ce85fe4SPablo Neira Ayuso 	return;
1601d6062cbbSThomas Graf errout:
1602d6062cbbSThomas Graf 	if (err < 0)
16034b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
16041da177e4SLinus Torvalds }
16051da177e4SLinus Torvalds 
16069f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
16079f0f7272SThomas Graf {
16081fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16099f0f7272SThomas Graf 
16109f0f7272SThomas Graf 	if (!in_dev)
16119f0f7272SThomas Graf 		return 0;
16129f0f7272SThomas Graf 
16139f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
16149f0f7272SThomas Graf }
16159f0f7272SThomas Graf 
16169f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
16179f0f7272SThomas Graf {
16181fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16199f0f7272SThomas Graf 	struct nlattr *nla;
16209f0f7272SThomas Graf 	int i;
16219f0f7272SThomas Graf 
16229f0f7272SThomas Graf 	if (!in_dev)
16239f0f7272SThomas Graf 		return -ENODATA;
16249f0f7272SThomas Graf 
16259f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
16269f0f7272SThomas Graf 	if (nla == NULL)
16279f0f7272SThomas Graf 		return -EMSGSIZE;
16289f0f7272SThomas Graf 
16299f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
16309f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
16319f0f7272SThomas Graf 
16329f0f7272SThomas Graf 	return 0;
16339f0f7272SThomas Graf }
16349f0f7272SThomas Graf 
16359f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
16369f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
16379f0f7272SThomas Graf };
16389f0f7272SThomas Graf 
1639cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1640cf7afbfeSThomas Graf 				 const struct nlattr *nla)
16419f0f7272SThomas Graf {
16429f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
16439f0f7272SThomas Graf 	int err, rem;
16449f0f7272SThomas Graf 
1645f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1646cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
16479f0f7272SThomas Graf 
16489f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
16499f0f7272SThomas Graf 	if (err < 0)
16509f0f7272SThomas Graf 		return err;
16519f0f7272SThomas Graf 
16529f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
16539f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
16549f0f7272SThomas Graf 			int cfgid = nla_type(a);
16559f0f7272SThomas Graf 
16569f0f7272SThomas Graf 			if (nla_len(a) < 4)
16579f0f7272SThomas Graf 				return -EINVAL;
16589f0f7272SThomas Graf 
16599f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
16609f0f7272SThomas Graf 				return -EINVAL;
16619f0f7272SThomas Graf 		}
16629f0f7272SThomas Graf 	}
16639f0f7272SThomas Graf 
1664cf7afbfeSThomas Graf 	return 0;
1665cf7afbfeSThomas Graf }
1666cf7afbfeSThomas Graf 
1667cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1668cf7afbfeSThomas Graf {
1669f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1670cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1671cf7afbfeSThomas Graf 	int rem;
1672cf7afbfeSThomas Graf 
1673cf7afbfeSThomas Graf 	if (!in_dev)
1674cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1675cf7afbfeSThomas Graf 
1676cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1677cf7afbfeSThomas Graf 		BUG();
1678cf7afbfeSThomas Graf 
16799f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
16809f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
16819f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
16829f0f7272SThomas Graf 	}
16839f0f7272SThomas Graf 
16849f0f7272SThomas Graf 	return 0;
16859f0f7272SThomas Graf }
16869f0f7272SThomas Graf 
1687edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1688edc9e748SNicolas Dichtel {
1689edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1690edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1691edc9e748SNicolas Dichtel 
16929e551110SNicolas Dichtel 	/* type -1 is used for ALL */
16939e551110SNicolas Dichtel 	if (type == -1 || type == NETCONFA_FORWARDING)
1694edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1695cc535dfbSNicolas Dichtel 	if (type == -1 || type == NETCONFA_RP_FILTER)
1696cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1697d67b8c61SNicolas Dichtel 	if (type == -1 || type == NETCONFA_MC_FORWARDING)
1698d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
1699edc9e748SNicolas Dichtel 
1700edc9e748SNicolas Dichtel 	return size;
1701edc9e748SNicolas Dichtel }
1702edc9e748SNicolas Dichtel 
1703edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1704edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1705edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1706edc9e748SNicolas Dichtel 				     int type)
1707edc9e748SNicolas Dichtel {
1708edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1709edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1710edc9e748SNicolas Dichtel 
1711edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1712edc9e748SNicolas Dichtel 			flags);
1713edc9e748SNicolas Dichtel 	if (nlh == NULL)
1714edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1715edc9e748SNicolas Dichtel 
1716edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1717edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1718edc9e748SNicolas Dichtel 
1719edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1720edc9e748SNicolas Dichtel 		goto nla_put_failure;
1721edc9e748SNicolas Dichtel 
17229e551110SNicolas Dichtel 	/* type -1 is used for ALL */
17239e551110SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_FORWARDING) &&
1724edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1725edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1726edc9e748SNicolas Dichtel 		goto nla_put_failure;
1727cc535dfbSNicolas Dichtel 	if ((type == -1 || type == NETCONFA_RP_FILTER) &&
1728cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1729cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1730cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1731d67b8c61SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
1732d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1733d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1734d67b8c61SNicolas Dichtel 		goto nla_put_failure;
1735edc9e748SNicolas Dichtel 
1736edc9e748SNicolas Dichtel 	return nlmsg_end(skb, nlh);
1737edc9e748SNicolas Dichtel 
1738edc9e748SNicolas Dichtel nla_put_failure:
1739edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1740edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1741edc9e748SNicolas Dichtel }
1742edc9e748SNicolas Dichtel 
1743d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
1744edc9e748SNicolas Dichtel 				 struct ipv4_devconf *devconf)
1745edc9e748SNicolas Dichtel {
1746edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1747edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1748edc9e748SNicolas Dichtel 
1749edc9e748SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
1750edc9e748SNicolas Dichtel 	if (skb == NULL)
1751edc9e748SNicolas Dichtel 		goto errout;
1752edc9e748SNicolas Dichtel 
1753edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
1754edc9e748SNicolas Dichtel 					RTM_NEWNETCONF, 0, type);
1755edc9e748SNicolas Dichtel 	if (err < 0) {
1756edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1757edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1758edc9e748SNicolas Dichtel 		kfree_skb(skb);
1759edc9e748SNicolas Dichtel 		goto errout;
1760edc9e748SNicolas Dichtel 	}
1761edc9e748SNicolas Dichtel 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC);
1762edc9e748SNicolas Dichtel 	return;
1763edc9e748SNicolas Dichtel errout:
1764edc9e748SNicolas Dichtel 	if (err < 0)
1765edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1766edc9e748SNicolas Dichtel }
1767edc9e748SNicolas Dichtel 
17689e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
17699e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
17709e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1771cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
17729e551110SNicolas Dichtel };
17739e551110SNicolas Dichtel 
17749e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
1775661d2967SThomas Graf 				    struct nlmsghdr *nlh)
17769e551110SNicolas Dichtel {
17779e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
17789e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
17799e551110SNicolas Dichtel 	struct netconfmsg *ncm;
17809e551110SNicolas Dichtel 	struct sk_buff *skb;
17819e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
17829e551110SNicolas Dichtel 	struct in_device *in_dev;
17839e551110SNicolas Dichtel 	struct net_device *dev;
17849e551110SNicolas Dichtel 	int ifindex;
17859e551110SNicolas Dichtel 	int err;
17869e551110SNicolas Dichtel 
17879e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
17889e551110SNicolas Dichtel 			  devconf_ipv4_policy);
17899e551110SNicolas Dichtel 	if (err < 0)
17909e551110SNicolas Dichtel 		goto errout;
17919e551110SNicolas Dichtel 
17929e551110SNicolas Dichtel 	err = EINVAL;
17939e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
17949e551110SNicolas Dichtel 		goto errout;
17959e551110SNicolas Dichtel 
17969e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
17979e551110SNicolas Dichtel 	switch (ifindex) {
17989e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
17999e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
18009e551110SNicolas Dichtel 		break;
18019e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
18029e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
18039e551110SNicolas Dichtel 		break;
18049e551110SNicolas Dichtel 	default:
18059e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
18069e551110SNicolas Dichtel 		if (dev == NULL)
18079e551110SNicolas Dichtel 			goto errout;
18089e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
18099e551110SNicolas Dichtel 		if (in_dev == NULL)
18109e551110SNicolas Dichtel 			goto errout;
18119e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
18129e551110SNicolas Dichtel 		break;
18139e551110SNicolas Dichtel 	}
18149e551110SNicolas Dichtel 
18159e551110SNicolas Dichtel 	err = -ENOBUFS;
18169e551110SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
18179e551110SNicolas Dichtel 	if (skb == NULL)
18189e551110SNicolas Dichtel 		goto errout;
18199e551110SNicolas Dichtel 
18209e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
18219e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
18229e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
18239e551110SNicolas Dichtel 					-1);
18249e551110SNicolas Dichtel 	if (err < 0) {
18259e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
18269e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
18279e551110SNicolas Dichtel 		kfree_skb(skb);
18289e551110SNicolas Dichtel 		goto errout;
18299e551110SNicolas Dichtel 	}
18309e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
18319e551110SNicolas Dichtel errout:
18329e551110SNicolas Dichtel 	return err;
18339e551110SNicolas Dichtel }
18349e551110SNicolas Dichtel 
18357a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
18367a674200SNicolas Dichtel 				     struct netlink_callback *cb)
18377a674200SNicolas Dichtel {
18387a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
18397a674200SNicolas Dichtel 	int h, s_h;
18407a674200SNicolas Dichtel 	int idx, s_idx;
18417a674200SNicolas Dichtel 	struct net_device *dev;
18427a674200SNicolas Dichtel 	struct in_device *in_dev;
18437a674200SNicolas Dichtel 	struct hlist_head *head;
18447a674200SNicolas Dichtel 
18457a674200SNicolas Dichtel 	s_h = cb->args[0];
18467a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
18477a674200SNicolas Dichtel 
18487a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18497a674200SNicolas Dichtel 		idx = 0;
18507a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
18517a674200SNicolas Dichtel 		rcu_read_lock();
18520465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
18530465277fSNicolas Dichtel 			  net->dev_base_seq;
18547a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18557a674200SNicolas Dichtel 			if (idx < s_idx)
18567a674200SNicolas Dichtel 				goto cont;
18577a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
18587a674200SNicolas Dichtel 			if (!in_dev)
18597a674200SNicolas Dichtel 				goto cont;
18607a674200SNicolas Dichtel 
18617a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
18627a674200SNicolas Dichtel 						      &in_dev->cnf,
18637a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
18647a674200SNicolas Dichtel 						      cb->nlh->nlmsg_seq,
18657a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
18667a674200SNicolas Dichtel 						      NLM_F_MULTI,
18677a674200SNicolas Dichtel 						      -1) <= 0) {
18687a674200SNicolas Dichtel 				rcu_read_unlock();
18697a674200SNicolas Dichtel 				goto done;
18707a674200SNicolas Dichtel 			}
18710465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
18727a674200SNicolas Dichtel cont:
18737a674200SNicolas Dichtel 			idx++;
18747a674200SNicolas Dichtel 		}
18757a674200SNicolas Dichtel 		rcu_read_unlock();
18767a674200SNicolas Dichtel 	}
18777a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
18787a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
18797a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
18807a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
18817a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
18827a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
18837a674200SNicolas Dichtel 					      -1) <= 0)
18847a674200SNicolas Dichtel 			goto done;
18857a674200SNicolas Dichtel 		else
18867a674200SNicolas Dichtel 			h++;
18877a674200SNicolas Dichtel 	}
18887a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
18897a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
18907a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
18917a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
18927a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
18937a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
18947a674200SNicolas Dichtel 					      -1) <= 0)
18957a674200SNicolas Dichtel 			goto done;
18967a674200SNicolas Dichtel 		else
18977a674200SNicolas Dichtel 			h++;
18987a674200SNicolas Dichtel 	}
18997a674200SNicolas Dichtel done:
19007a674200SNicolas Dichtel 	cb->args[0] = h;
19017a674200SNicolas Dichtel 	cb->args[1] = idx;
19027a674200SNicolas Dichtel 
19037a674200SNicolas Dichtel 	return skb->len;
19047a674200SNicolas Dichtel }
19057a674200SNicolas Dichtel 
19061da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
19071da177e4SLinus Torvalds 
1908c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
190931be3085SHerbert Xu {
191031be3085SHerbert Xu 	struct net_device *dev;
191131be3085SHerbert Xu 
191231be3085SHerbert Xu 	rcu_read_lock();
1913c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1914c6d14c84SEric Dumazet 		struct in_device *in_dev;
1915c6d14c84SEric Dumazet 
191631be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
191731be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
19189355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1919c6d14c84SEric Dumazet 	}
192031be3085SHerbert Xu 	rcu_read_unlock();
192131be3085SHerbert Xu }
192231be3085SHerbert Xu 
1923c6d14c84SEric Dumazet /* called with RTNL locked */
1924c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
192568dd299bSPavel Emelyanov {
192668dd299bSPavel Emelyanov 	struct net_device *dev;
1927586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
192868dd299bSPavel Emelyanov 
1929586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
19309355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
1931edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1932edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
1933edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
1934edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1935edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
1936edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
193768dd299bSPavel Emelyanov 
1938c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
193968dd299bSPavel Emelyanov 		struct in_device *in_dev;
19400187bdfbSBen Hutchings 		if (on)
19410187bdfbSBen Hutchings 			dev_disable_lro(dev);
194268dd299bSPavel Emelyanov 		rcu_read_lock();
194368dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
1944edc9e748SNicolas Dichtel 		if (in_dev) {
194568dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1946edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1947edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
1948edc9e748SNicolas Dichtel 		}
194968dd299bSPavel Emelyanov 		rcu_read_unlock();
195068dd299bSPavel Emelyanov 	}
195168dd299bSPavel Emelyanov }
195268dd299bSPavel Emelyanov 
1953fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
19548d65af78SAlexey Dobriyan 			     void __user *buffer,
195531be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
195631be3085SHerbert Xu {
1957d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
19588d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
1959d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
196031be3085SHerbert Xu 
196131be3085SHerbert Xu 	if (write) {
196231be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1963c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
196431be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
196531be3085SHerbert Xu 
196631be3085SHerbert Xu 		set_bit(i, cnf->state);
196731be3085SHerbert Xu 
19689355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1969c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
1970d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
1971d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
1972d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
19734ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
1974cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
1975cc535dfbSNicolas Dichtel 		    new_value != old_value) {
1976cc535dfbSNicolas Dichtel 			int ifindex;
1977cc535dfbSNicolas Dichtel 
1978cc535dfbSNicolas Dichtel 			if (cnf == net->ipv4.devconf_dflt)
1979cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_DEFAULT;
1980cc535dfbSNicolas Dichtel 			else if (cnf == net->ipv4.devconf_all)
1981cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_ALL;
1982cc535dfbSNicolas Dichtel 			else {
1983cc535dfbSNicolas Dichtel 				struct in_device *idev =
1984cc535dfbSNicolas Dichtel 					container_of(cnf, struct in_device,
1985cc535dfbSNicolas Dichtel 						     cnf);
1986cc535dfbSNicolas Dichtel 				ifindex = idev->dev->ifindex;
1987cc535dfbSNicolas Dichtel 			}
1988cc535dfbSNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER,
1989cc535dfbSNicolas Dichtel 						    ifindex, cnf);
1990cc535dfbSNicolas Dichtel 		}
199131be3085SHerbert Xu 	}
199231be3085SHerbert Xu 
199331be3085SHerbert Xu 	return ret;
199431be3085SHerbert Xu }
199531be3085SHerbert Xu 
1996fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
19978d65af78SAlexey Dobriyan 				  void __user *buffer,
19981da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
19991da177e4SLinus Torvalds {
20001da177e4SLinus Torvalds 	int *valp = ctl->data;
20011da177e4SLinus Torvalds 	int val = *valp;
200288af182eSEric W. Biederman 	loff_t pos = *ppos;
20038d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
20041da177e4SLinus Torvalds 
20051da177e4SLinus Torvalds 	if (write && *valp != val) {
2006c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2007c0ce9fb3SPavel Emelyanov 
20080187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
200988af182eSEric W. Biederman 			if (!rtnl_trylock()) {
201088af182eSEric W. Biederman 				/* Restore the original values before restarting */
201188af182eSEric W. Biederman 				*valp = val;
201288af182eSEric W. Biederman 				*ppos = pos;
20139b8adb5eSEric W. Biederman 				return restart_syscall();
201488af182eSEric W. Biederman 			}
20150187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2016c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2017edc9e748SNicolas Dichtel 			} else {
20180187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
20190187bdfbSBen Hutchings 				struct in_device *idev =
20200187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2021edc9e748SNicolas Dichtel 				if (*valp)
20220187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
2023edc9e748SNicolas Dichtel 				inet_netconf_notify_devconf(net,
2024edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2025edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2026edc9e748SNicolas Dichtel 							    cnf);
20270187bdfbSBen Hutchings 			}
20280187bdfbSBen Hutchings 			rtnl_unlock();
20294ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2030edc9e748SNicolas Dichtel 		} else
2031edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
2032edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2033edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
20340187bdfbSBen Hutchings 	}
20351da177e4SLinus Torvalds 
20361da177e4SLinus Torvalds 	return ret;
20371da177e4SLinus Torvalds }
20381da177e4SLinus Torvalds 
2039fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
20408d65af78SAlexey Dobriyan 				void __user *buffer,
20411da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
20421da177e4SLinus Torvalds {
20431da177e4SLinus Torvalds 	int *valp = ctl->data;
20441da177e4SLinus Torvalds 	int val = *valp;
20458d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
204676e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
20471da177e4SLinus Torvalds 
20481da177e4SLinus Torvalds 	if (write && *valp != val)
20494ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
20501da177e4SLinus Torvalds 
20511da177e4SLinus Torvalds 	return ret;
20521da177e4SLinus Torvalds }
20531da177e4SLinus Torvalds 
2054f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
205542f811b8SHerbert Xu 	{ \
205642f811b8SHerbert Xu 		.procname	= name, \
205742f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
205802291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
205942f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
206042f811b8SHerbert Xu 		.mode		= mval, \
206142f811b8SHerbert Xu 		.proc_handler	= proc, \
206231be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
206342f811b8SHerbert Xu 	}
206442f811b8SHerbert Xu 
206542f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2066f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
206742f811b8SHerbert Xu 
206842f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2069f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
207042f811b8SHerbert Xu 
2071f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2072f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
207342f811b8SHerbert Xu 
207442f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2075f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
207642f811b8SHerbert Xu 
20771da177e4SLinus Torvalds static struct devinet_sysctl_table {
20781da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
207902291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
20801da177e4SLinus Torvalds } devinet_sysctl = {
20811da177e4SLinus Torvalds 	.devinet_vars = {
208242f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2083f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
208442f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
208542f811b8SHerbert Xu 
208642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
208742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
208842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
208942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
209042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
209142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
209242f811b8SHerbert Xu 					"accept_source_route"),
20938153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
209428f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
209542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
209642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
209742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
209842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
209942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
210042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
210142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
210242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
210342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2104eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
210565324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
21065c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
21075c6fe01cSWilliam Manley 					"force_igmp_version"),
21082690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
21092690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
21102690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
21112690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
211242f811b8SHerbert Xu 
211342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
211442f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
211542f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
211642f811b8SHerbert Xu 					      "promote_secondaries"),
2117d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2118d0daebc3SThomas Graf 					      "route_localnet"),
21191da177e4SLinus Torvalds 	},
21201da177e4SLinus Torvalds };
21211da177e4SLinus Torvalds 
2122ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
2123f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
21241da177e4SLinus Torvalds {
21251da177e4SLinus Torvalds 	int i;
21269fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
21278607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2128bfada697SPavel Emelyanov 
21299fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
21301da177e4SLinus Torvalds 	if (!t)
21319fa89642SPavel Emelyanov 		goto out;
21329fa89642SPavel Emelyanov 
21331da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
21341da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
213531be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2136c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
21371da177e4SLinus Torvalds 	}
21381da177e4SLinus Torvalds 
21398607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
21401da177e4SLinus Torvalds 
21418607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
21421da177e4SLinus Torvalds 	if (!t->sysctl_header)
21438607ddb8SEric W. Biederman 		goto free;
21441da177e4SLinus Torvalds 
21451da177e4SLinus Torvalds 	p->sysctl = t;
2146ea40b324SPavel Emelyanov 	return 0;
21471da177e4SLinus Torvalds 
21481da177e4SLinus Torvalds free:
21491da177e4SLinus Torvalds 	kfree(t);
21509fa89642SPavel Emelyanov out:
2151ea40b324SPavel Emelyanov 	return -ENOBUFS;
21521da177e4SLinus Torvalds }
21531da177e4SLinus Torvalds 
215451602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
215566f27a52SPavel Emelyanov {
215651602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
215766f27a52SPavel Emelyanov 
215851602b2aSPavel Emelyanov 	if (t == NULL)
215951602b2aSPavel Emelyanov 		return;
216051602b2aSPavel Emelyanov 
216151602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
2162ff538818SLucian Adrian Grijincu 	unregister_net_sysctl_table(t->sysctl_header);
21631da177e4SLinus Torvalds 	kfree(t);
21641da177e4SLinus Torvalds }
216551602b2aSPavel Emelyanov 
216651602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
216751602b2aSPavel Emelyanov {
216873af614aSJiri Pirko 	neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
2169c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
2170f8572d8fSEric W. Biederman 					&idev->cnf);
217151602b2aSPavel Emelyanov }
217251602b2aSPavel Emelyanov 
217351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
217451602b2aSPavel Emelyanov {
217551602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
217651602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
21771da177e4SLinus Torvalds }
21781da177e4SLinus Torvalds 
217968dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
218068dd299bSPavel Emelyanov 	{
218168dd299bSPavel Emelyanov 		.procname	= "ip_forward",
218268dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
218302291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
218468dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
218568dd299bSPavel Emelyanov 		.mode		= 0644,
218668dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
218768dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2188c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
218968dd299bSPavel Emelyanov 	},
219068dd299bSPavel Emelyanov 	{ },
219168dd299bSPavel Emelyanov };
21922a75de0cSEric Dumazet #endif
219368dd299bSPavel Emelyanov 
2194752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2195752d14dcSPavel Emelyanov {
2196752d14dcSPavel Emelyanov 	int err;
2197752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
21982a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
21992a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2200752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
22012a75de0cSEric Dumazet #endif
2202752d14dcSPavel Emelyanov 
2203752d14dcSPavel Emelyanov 	err = -ENOMEM;
2204752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2205752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2206752d14dcSPavel Emelyanov 
220709ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2208752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
2209752d14dcSPavel Emelyanov 		if (all == NULL)
2210752d14dcSPavel Emelyanov 			goto err_alloc_all;
2211752d14dcSPavel Emelyanov 
2212752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
2213752d14dcSPavel Emelyanov 		if (dflt == NULL)
2214752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2215752d14dcSPavel Emelyanov 
22162a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2217752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
2218752d14dcSPavel Emelyanov 		if (tbl == NULL)
2219752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2220752d14dcSPavel Emelyanov 
222102291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2222752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2223752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
22242a75de0cSEric Dumazet #endif
2225752d14dcSPavel Emelyanov 	}
2226752d14dcSPavel Emelyanov 
2227752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2228f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
2229752d14dcSPavel Emelyanov 	if (err < 0)
2230752d14dcSPavel Emelyanov 		goto err_reg_all;
2231752d14dcSPavel Emelyanov 
2232f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
2233752d14dcSPavel Emelyanov 	if (err < 0)
2234752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2235752d14dcSPavel Emelyanov 
2236752d14dcSPavel Emelyanov 	err = -ENOMEM;
22378607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
2238752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
2239752d14dcSPavel Emelyanov 		goto err_reg_ctl;
22402a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2241752d14dcSPavel Emelyanov #endif
2242752d14dcSPavel Emelyanov 
2243752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2244752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2245752d14dcSPavel Emelyanov 	return 0;
2246752d14dcSPavel Emelyanov 
2247752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2248752d14dcSPavel Emelyanov err_reg_ctl:
2249752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
2250752d14dcSPavel Emelyanov err_reg_dflt:
2251752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
2252752d14dcSPavel Emelyanov err_reg_all:
2253752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2254752d14dcSPavel Emelyanov 		kfree(tbl);
2255752d14dcSPavel Emelyanov err_alloc_ctl:
22562a75de0cSEric Dumazet #endif
2257752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2258752d14dcSPavel Emelyanov 		kfree(dflt);
2259752d14dcSPavel Emelyanov err_alloc_dflt:
2260752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2261752d14dcSPavel Emelyanov 		kfree(all);
2262752d14dcSPavel Emelyanov err_alloc_all:
2263752d14dcSPavel Emelyanov 	return err;
2264752d14dcSPavel Emelyanov }
2265752d14dcSPavel Emelyanov 
2266752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2267752d14dcSPavel Emelyanov {
22682a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2269752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2270752d14dcSPavel Emelyanov 
2271752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2272752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2273752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
2274752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
2275752d14dcSPavel Emelyanov 	kfree(tbl);
22762a75de0cSEric Dumazet #endif
2277752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2278752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2279752d14dcSPavel Emelyanov }
2280752d14dcSPavel Emelyanov 
2281752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2282752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2283752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2284752d14dcSPavel Emelyanov };
2285752d14dcSPavel Emelyanov 
22869f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
22879f0f7272SThomas Graf 	.family		  = AF_INET,
22889f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
22899f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2290cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2291cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
22929f0f7272SThomas Graf };
22939f0f7272SThomas Graf 
22941da177e4SLinus Torvalds void __init devinet_init(void)
22951da177e4SLinus Torvalds {
2296fd23c3b3SDavid S. Miller 	int i;
2297fd23c3b3SDavid S. Miller 
2298fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2299fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2300fd23c3b3SDavid S. Miller 
2301752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2302752d14dcSPavel Emelyanov 
23031da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
23041da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
230563f3444fSThomas Graf 
23065c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, 0);
23075c766d64SJiri Pirko 
23089f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
23099f0f7272SThomas Graf 
2310c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
2311c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
2312c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
23139e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
23147a674200SNicolas Dichtel 		      inet_netconf_dump_devconf, NULL);
23151da177e4SLinus Torvalds }
23161da177e4SLinus Torvalds 
2317