xref: /linux/net/ipv4/devinet.c (revision 2d230e2b2c3111cf4a11619f60dcd158ae84e3ab)
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 <asm/system.h>
311da177e4SLinus Torvalds #include <linux/bitops.h>
324fc268d2SRandy Dunlap #include <linux/capability.h>
331da177e4SLinus Torvalds #include <linux/module.h>
341da177e4SLinus Torvalds #include <linux/types.h>
351da177e4SLinus Torvalds #include <linux/kernel.h>
361da177e4SLinus Torvalds #include <linux/string.h>
371da177e4SLinus Torvalds #include <linux/mm.h>
381da177e4SLinus Torvalds #include <linux/socket.h>
391da177e4SLinus Torvalds #include <linux/sockios.h>
401da177e4SLinus Torvalds #include <linux/in.h>
411da177e4SLinus Torvalds #include <linux/errno.h>
421da177e4SLinus Torvalds #include <linux/interrupt.h>
431823730fSThomas Graf #include <linux/if_addr.h>
441da177e4SLinus Torvalds #include <linux/if_ether.h>
451da177e4SLinus Torvalds #include <linux/inet.h>
461da177e4SLinus Torvalds #include <linux/netdevice.h>
471da177e4SLinus Torvalds #include <linux/etherdevice.h>
481da177e4SLinus Torvalds #include <linux/skbuff.h>
491da177e4SLinus Torvalds #include <linux/init.h>
501da177e4SLinus Torvalds #include <linux/notifier.h>
511da177e4SLinus Torvalds #include <linux/inetdevice.h>
521da177e4SLinus Torvalds #include <linux/igmp.h>
535a0e3ad6STejun Heo #include <linux/slab.h>
54fd23c3b3SDavid S. Miller #include <linux/hash.h>
551da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
561da177e4SLinus Torvalds #include <linux/sysctl.h>
571da177e4SLinus Torvalds #endif
581da177e4SLinus Torvalds #include <linux/kmod.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>
661da177e4SLinus Torvalds 
670027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
6842f811b8SHerbert Xu 	.data = {
6902291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7002291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7102291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7202291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
7342f811b8SHerbert Xu 	},
741da177e4SLinus Torvalds };
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
7742f811b8SHerbert Xu 	.data = {
7802291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7902291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8002291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8102291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
8342f811b8SHerbert Xu 	},
841da177e4SLinus Torvalds };
851da177e4SLinus Torvalds 
869355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
879355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
8842f811b8SHerbert Xu 
89ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
905c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
915c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
925c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
935176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
945c753978SThomas Graf };
955c753978SThomas Graf 
96fd23c3b3SDavid S. Miller /* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE
97fd23c3b3SDavid S. Miller  * value.  So if you change this define, make appropriate changes to
98fd23c3b3SDavid S. Miller  * inet_addr_hash as well.
99fd23c3b3SDavid S. Miller  */
100fd23c3b3SDavid S. Miller #define IN4_ADDR_HSIZE	256
101fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
102fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock);
103fd23c3b3SDavid S. Miller 
104fd23c3b3SDavid S. Miller static inline unsigned int inet_addr_hash(struct net *net, __be32 addr)
105fd23c3b3SDavid S. Miller {
106fd23c3b3SDavid S. Miller 	u32 val = (__force u32) addr ^ hash_ptr(net, 8);
107fd23c3b3SDavid S. Miller 
108fd23c3b3SDavid S. Miller 	return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) &
109fd23c3b3SDavid S. Miller 		(IN4_ADDR_HSIZE - 1));
110fd23c3b3SDavid S. Miller }
111fd23c3b3SDavid S. Miller 
112fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
113fd23c3b3SDavid S. Miller {
114e066008bSDavid S. Miller 	unsigned int hash = inet_addr_hash(net, ifa->ifa_local);
115fd23c3b3SDavid S. Miller 
116fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
117fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
118fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
119fd23c3b3SDavid S. Miller }
120fd23c3b3SDavid S. Miller 
121fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
122fd23c3b3SDavid S. Miller {
123fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
124fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
125fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
126fd23c3b3SDavid S. Miller }
127fd23c3b3SDavid S. Miller 
1289435eb1cSDavid S. Miller /**
1299435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1309435eb1cSDavid S. Miller  * @net: the net namespace
1319435eb1cSDavid S. Miller  * @addr: the source address
1329435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1339435eb1cSDavid S. Miller  *
1349435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1359435eb1cSDavid S. Miller  */
1369435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1379435eb1cSDavid S. Miller {
1389435eb1cSDavid S. Miller 	unsigned int hash = inet_addr_hash(net, addr);
1399435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1409435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1419435eb1cSDavid S. Miller 	struct hlist_node *node;
1429435eb1cSDavid S. Miller 
1439435eb1cSDavid S. Miller 	rcu_read_lock();
1449435eb1cSDavid S. Miller 	hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
1459435eb1cSDavid S. Miller 		struct net_device *dev = ifa->ifa_dev->dev;
1469435eb1cSDavid S. Miller 
1479435eb1cSDavid S. Miller 		if (!net_eq(dev_net(dev), net))
1489435eb1cSDavid S. Miller 			continue;
149e066008bSDavid S. Miller 		if (ifa->ifa_local == addr) {
1509435eb1cSDavid S. Miller 			result = dev;
1519435eb1cSDavid S. Miller 			break;
1529435eb1cSDavid S. Miller 		}
1539435eb1cSDavid S. Miller 	}
1549435eb1cSDavid S. Miller 	if (result && devref)
1559435eb1cSDavid S. Miller 		dev_hold(result);
1569435eb1cSDavid S. Miller 	rcu_read_unlock();
1579435eb1cSDavid S. Miller 	return result;
1589435eb1cSDavid S. Miller }
1599435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1609435eb1cSDavid S. Miller 
161d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1621da177e4SLinus Torvalds 
163e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1641da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1651da177e4SLinus Torvalds 			 int destroy);
1661da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
16766f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev);
16851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
16951602b2aSPavel Emelyanov #else
17051602b2aSPavel Emelyanov static inline void devinet_sysctl_register(struct in_device *idev)
17151602b2aSPavel Emelyanov {
17251602b2aSPavel Emelyanov }
17351602b2aSPavel Emelyanov static inline void devinet_sysctl_unregister(struct in_device *idev)
17451602b2aSPavel Emelyanov {
17551602b2aSPavel Emelyanov }
1761da177e4SLinus Torvalds #endif
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds /* Locks all the inet devices. */
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1811da177e4SLinus Torvalds {
18293adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
1861da177e4SLinus Torvalds {
1871da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
1881da177e4SLinus Torvalds 	if (ifa->ifa_dev)
1891da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
1901da177e4SLinus Torvalds 	kfree(ifa);
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds static inline void inet_free_ifa(struct in_ifaddr *ifa)
1941da177e4SLinus Torvalds {
1951da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
1991da177e4SLinus Torvalds {
2001da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2011da177e4SLinus Torvalds 
202547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
203547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2041da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
2051da177e4SLinus Torvalds 	printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
2061da177e4SLinus Torvalds 	       idev, dev ? dev->name : "NIL");
2071da177e4SLinus Torvalds #endif
2081da177e4SLinus Torvalds 	dev_put(dev);
2091da177e4SLinus Torvalds 	if (!idev->dead)
2109f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2119f9354b9SEric Dumazet 	else
2121da177e4SLinus Torvalds 		kfree(idev);
2131da177e4SLinus Torvalds }
2149f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2151da177e4SLinus Torvalds 
21671e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds 	struct in_device *in_dev;
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	ASSERT_RTNL();
2211da177e4SLinus Torvalds 
2220da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2231da177e4SLinus Torvalds 	if (!in_dev)
2241da177e4SLinus Torvalds 		goto out;
225c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2269355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2271da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2281da177e4SLinus Torvalds 	in_dev->dev = dev;
2299f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2309f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2311da177e4SLinus Torvalds 		goto out_kfree;
2320187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2330187bdfbSBen Hutchings 		dev_disable_lro(dev);
2341da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2351da177e4SLinus Torvalds 	dev_hold(dev);
23630c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2371da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2381da177e4SLinus Torvalds 
23966f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2401da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2411da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2421da177e4SLinus Torvalds 		ip_mc_up(in_dev);
243483479ecSJarek Poplawski 
24430c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
24530c4cf57SDavid L Stevens 	rcu_assign_pointer(dev->ip_ptr, in_dev);
246483479ecSJarek Poplawski out:
2471da177e4SLinus Torvalds 	return in_dev;
2481da177e4SLinus Torvalds out_kfree:
2491da177e4SLinus Torvalds 	kfree(in_dev);
2501da177e4SLinus Torvalds 	in_dev = NULL;
2511da177e4SLinus Torvalds 	goto out;
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2551da177e4SLinus Torvalds {
2561da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2571da177e4SLinus Torvalds 	in_dev_put(idev);
2581da177e4SLinus Torvalds }
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2611da177e4SLinus Torvalds {
2621da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2631da177e4SLinus Torvalds 	struct net_device *dev;
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds 	ASSERT_RTNL();
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	dev = in_dev->dev;
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	in_dev->dead = 1;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2741da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2751da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2761da177e4SLinus Torvalds 	}
2771da177e4SLinus Torvalds 
27895ae6b22SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, NULL);
2791da177e4SLinus Torvalds 
28051602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
2811da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
2821da177e4SLinus Torvalds 	arp_ifdown(dev);
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds 
287ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
2881da177e4SLinus Torvalds {
2891da177e4SLinus Torvalds 	rcu_read_lock();
2901da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
2911da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
2921da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
2931da177e4SLinus Torvalds 				rcu_read_unlock();
2941da177e4SLinus Torvalds 				return 1;
2951da177e4SLinus Torvalds 			}
2961da177e4SLinus Torvalds 		}
2971da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
2981da177e4SLinus Torvalds 	rcu_read_unlock();
2991da177e4SLinus Torvalds 	return 0;
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
302d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
303d6062cbbSThomas Graf 			 int destroy, struct nlmsghdr *nlh, u32 pid)
3041da177e4SLinus Torvalds {
3058f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3060ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3070ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3080ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3090ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	ASSERT_RTNL();
3121da177e4SLinus Torvalds 
3138f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3148f937c60SHarald Welte 	 * unless alias promotion is set
3158f937c60SHarald Welte 	 **/
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3181da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3210ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3220ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3230ff60a45SJamal Hadi Salim 				last_prim = ifa;
3240ff60a45SJamal Hadi Salim 
3251da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3261da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3271da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3281da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3290ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3301da177e4SLinus Torvalds 				continue;
3311da177e4SLinus Torvalds 			}
3321da177e4SLinus Torvalds 
3330ff60a45SJamal Hadi Salim 			if (!do_promote) {
334fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3351da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3361da177e4SLinus Torvalds 
337d6062cbbSThomas Graf 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
338e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
339e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3401da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3418f937c60SHarald Welte 			} else {
3428f937c60SHarald Welte 				promote = ifa;
3438f937c60SHarald Welte 				break;
3448f937c60SHarald Welte 			}
3451da177e4SLinus Torvalds 		}
3461da177e4SLinus Torvalds 	}
3471da177e4SLinus Torvalds 
348*2d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
349*2d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
350*2d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
351*2d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
352*2d230e2bSJulian Anastasov 	 */
353*2d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
354*2d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
355*2d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
356*2d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
357*2d230e2bSJulian Anastasov 	}
358*2d230e2bSJulian Anastasov 
3591da177e4SLinus Torvalds 	/* 2. Unlink it */
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
362fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3651da177e4SLinus Torvalds 
3661da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3671da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3681da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3691da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3701da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3711da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3721da177e4SLinus Torvalds 	   So that, this order is correct.
3731da177e4SLinus Torvalds 	 */
374d6062cbbSThomas Graf 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
375e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3760ff60a45SJamal Hadi Salim 
3770ff60a45SJamal Hadi Salim 	if (promote) {
3780ff60a45SJamal Hadi Salim 
3790ff60a45SJamal Hadi Salim 		if (prev_prom) {
3800ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
3810ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
3820ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
3830ff60a45SJamal Hadi Salim 		}
3840ff60a45SJamal Hadi Salim 
3850ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
386d6062cbbSThomas Graf 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
387e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
388e041c683SAlan Stern 				NETDEV_UP, promote);
3890ff60a45SJamal Hadi Salim 		for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) {
3900ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
3910ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
3920ff60a45SJamal Hadi Salim 					continue;
3930ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
3940ff60a45SJamal Hadi Salim 		}
3950ff60a45SJamal Hadi Salim 
3960ff60a45SJamal Hadi Salim 	}
3976363097cSHerbert Xu 	if (destroy)
3981da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
401d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
402d6062cbbSThomas Graf 			 int destroy)
403d6062cbbSThomas Graf {
404d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
405d6062cbbSThomas Graf }
406d6062cbbSThomas Graf 
407d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
408d6062cbbSThomas Graf 			     u32 pid)
4091da177e4SLinus Torvalds {
4101da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4111da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	ASSERT_RTNL();
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4161da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4171da177e4SLinus Torvalds 		return 0;
4181da177e4SLinus Torvalds 	}
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4211da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4241da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4251da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4261da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4271da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4281da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4291da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4301da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4311da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4321da177e4SLinus Torvalds 				return -EEXIST;
4331da177e4SLinus Torvalds 			}
4341da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4351da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4361da177e4SLinus Torvalds 				return -EINVAL;
4371da177e4SLinus Torvalds 			}
4381da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4391da177e4SLinus Torvalds 		}
4401da177e4SLinus Torvalds 	}
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
4431da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4441da177e4SLinus Torvalds 		ifap = last_primary;
4451da177e4SLinus Torvalds 	}
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4481da177e4SLinus Torvalds 	*ifap = ifa;
4491da177e4SLinus Torvalds 
450fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
451fd23c3b3SDavid S. Miller 
4521da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4531da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4541da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
455d6062cbbSThomas Graf 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
456e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 	return 0;
4591da177e4SLinus Torvalds }
4601da177e4SLinus Torvalds 
461d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
462d6062cbbSThomas Graf {
463d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
464d6062cbbSThomas Graf }
465d6062cbbSThomas Graf 
4661da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4671da177e4SLinus Torvalds {
468e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	ASSERT_RTNL();
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 	if (!in_dev) {
4731da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4741da177e4SLinus Torvalds 		return -ENOBUFS;
4751da177e4SLinus Torvalds 	}
47671e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
4771da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
478547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
4791da177e4SLinus Torvalds 		in_dev_hold(in_dev);
4801da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
4811da177e4SLinus Torvalds 	}
482f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
4831da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
4841da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
4851da177e4SLinus Torvalds }
4861da177e4SLinus Torvalds 
4878723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
4888723e1b4SEric Dumazet  * We dont take a reference on found in_device
4898723e1b4SEric Dumazet  */
4907fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
4911da177e4SLinus Torvalds {
4921da177e4SLinus Torvalds 	struct net_device *dev;
4931da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
494c148fc2eSEric Dumazet 
495c148fc2eSEric Dumazet 	rcu_read_lock();
496c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
4971da177e4SLinus Torvalds 	if (dev)
4988723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
499c148fc2eSEric Dumazet 	rcu_read_unlock();
5001da177e4SLinus Torvalds 	return in_dev;
5011da177e4SLinus Torvalds }
5029f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5051da177e4SLinus Torvalds 
50660cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
50760cad5daSAl Viro 				    __be32 mask)
5081da177e4SLinus Torvalds {
5091da177e4SLinus Torvalds 	ASSERT_RTNL();
5101da177e4SLinus Torvalds 
5111da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5121da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5131da177e4SLinus Torvalds 			return ifa;
5141da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5151da177e4SLinus Torvalds 	return NULL;
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
5191da177e4SLinus Torvalds {
5203b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
521dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5221da177e4SLinus Torvalds 	struct in_device *in_dev;
523dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5241da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
525dfdd5fd4SThomas Graf 	int err = -EINVAL;
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds 	ASSERT_RTNL();
5281da177e4SLinus Torvalds 
529dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
530dfdd5fd4SThomas Graf 	if (err < 0)
531dfdd5fd4SThomas Graf 		goto errout;
532dfdd5fd4SThomas Graf 
533dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5347fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
535dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
536dfdd5fd4SThomas Graf 		err = -ENODEV;
537dfdd5fd4SThomas Graf 		goto errout;
538dfdd5fd4SThomas Graf 	}
539dfdd5fd4SThomas Graf 
5401da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5411da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
542dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
543a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5441da177e4SLinus Torvalds 			continue;
545dfdd5fd4SThomas Graf 
546dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
547dfdd5fd4SThomas Graf 			continue;
548dfdd5fd4SThomas Graf 
549dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
550dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
551a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
552dfdd5fd4SThomas Graf 			continue;
553dfdd5fd4SThomas Graf 
554d6062cbbSThomas Graf 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
5551da177e4SLinus Torvalds 		return 0;
5561da177e4SLinus Torvalds 	}
557dfdd5fd4SThomas Graf 
558dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
559dfdd5fd4SThomas Graf errout:
560dfdd5fd4SThomas Graf 	return err;
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds 
5634b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
5641da177e4SLinus Torvalds {
5655c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5665c753978SThomas Graf 	struct in_ifaddr *ifa;
5675c753978SThomas Graf 	struct ifaddrmsg *ifm;
5681da177e4SLinus Torvalds 	struct net_device *dev;
5691da177e4SLinus Torvalds 	struct in_device *in_dev;
5707b218574SDenis V. Lunev 	int err;
5711da177e4SLinus Torvalds 
5725c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
5735c753978SThomas Graf 	if (err < 0)
5745c753978SThomas Graf 		goto errout;
5751da177e4SLinus Torvalds 
5765c753978SThomas Graf 	ifm = nlmsg_data(nlh);
577c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
5787b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
5795c753978SThomas Graf 		goto errout;
5801da177e4SLinus Torvalds 
5814b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
5825c753978SThomas Graf 	err = -ENODEV;
5837b218574SDenis V. Lunev 	if (dev == NULL)
5845c753978SThomas Graf 		goto errout;
5851da177e4SLinus Torvalds 
5865c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
5875c753978SThomas Graf 	err = -ENOBUFS;
5887b218574SDenis V. Lunev 	if (in_dev == NULL)
5895c753978SThomas Graf 		goto errout;
59071e27da9SHerbert Xu 
5915c753978SThomas Graf 	ifa = inet_alloc_ifa();
5927b218574SDenis V. Lunev 	if (ifa == NULL)
5935c753978SThomas Graf 		/*
5945c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
5955c753978SThomas Graf 		 * assigned to its device and is destroy with it.
5965c753978SThomas Graf 		 */
5975c753978SThomas Graf 		goto errout;
5985c753978SThomas Graf 
599a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
6005c753978SThomas Graf 	in_dev_hold(in_dev);
6015c753978SThomas Graf 
6025c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
6035c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
6045c753978SThomas Graf 
605fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
6061da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
6071da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
6081da177e4SLinus Torvalds 	ifa->ifa_flags = ifm->ifa_flags;
6091da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
6101da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
6115c753978SThomas Graf 
612a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
613a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
6145c753978SThomas Graf 
6155c753978SThomas Graf 	if (tb[IFA_BROADCAST])
616a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
6175c753978SThomas Graf 
6185c753978SThomas Graf 	if (tb[IFA_LABEL])
6195c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
6201da177e4SLinus Torvalds 	else
6211da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
6221da177e4SLinus Torvalds 
6235c753978SThomas Graf 	return ifa;
6245c753978SThomas Graf 
6255c753978SThomas Graf errout:
6265c753978SThomas Graf 	return ERR_PTR(err);
6275c753978SThomas Graf }
6285c753978SThomas Graf 
6295c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
6305c753978SThomas Graf {
6313b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6325c753978SThomas Graf 	struct in_ifaddr *ifa;
6335c753978SThomas Graf 
6345c753978SThomas Graf 	ASSERT_RTNL();
6355c753978SThomas Graf 
6364b8aa9abSDenis V. Lunev 	ifa = rtm_to_ifaddr(net, nlh);
6375c753978SThomas Graf 	if (IS_ERR(ifa))
6385c753978SThomas Graf 		return PTR_ERR(ifa);
6395c753978SThomas Graf 
640d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
6411da177e4SLinus Torvalds }
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds /*
6441da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
6451da177e4SLinus Torvalds  */
6461da177e4SLinus Torvalds 
6479f9354b9SEric Dumazet static inline int inet_abc_len(__be32 addr)
6481da177e4SLinus Torvalds {
6491da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
6501da177e4SLinus Torvalds 
651f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
6521da177e4SLinus Torvalds 		rc = 0;
6531da177e4SLinus Torvalds 	else {
654714e85beSAl Viro 		__u32 haddr = ntohl(addr);
6551da177e4SLinus Torvalds 
656714e85beSAl Viro 		if (IN_CLASSA(haddr))
6571da177e4SLinus Torvalds 			rc = 8;
658714e85beSAl Viro 		else if (IN_CLASSB(haddr))
6591da177e4SLinus Torvalds 			rc = 16;
660714e85beSAl Viro 		else if (IN_CLASSC(haddr))
6611da177e4SLinus Torvalds 			rc = 24;
6621da177e4SLinus Torvalds 	}
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	return rc;
6651da177e4SLinus Torvalds }
6661da177e4SLinus Torvalds 
6671da177e4SLinus Torvalds 
668e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
6691da177e4SLinus Torvalds {
6701da177e4SLinus Torvalds 	struct ifreq ifr;
6711da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
6721da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
6731da177e4SLinus Torvalds 	struct in_device *in_dev;
6741da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
6751da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
6761da177e4SLinus Torvalds 	struct net_device *dev;
6771da177e4SLinus Torvalds 	char *colon;
6781da177e4SLinus Torvalds 	int ret = -EFAULT;
6791da177e4SLinus Torvalds 	int tryaddrmatch = 0;
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	/*
6821da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
6831da177e4SLinus Torvalds 	 */
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
6861da177e4SLinus Torvalds 		goto out;
6871da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	/* save original address for comparison */
6901da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
6931da177e4SLinus Torvalds 	if (colon)
6941da177e4SLinus Torvalds 		*colon = 0;
6951da177e4SLinus Torvalds 
696e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	switch (cmd) {
6991da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7001da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7011da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7021da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
7031da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
7041da177e4SLinus Torvalds 		   so that we do not impose a lock.
7051da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
7061da177e4SLinus Torvalds 		 */
7071da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
7081da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
7091da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
7101da177e4SLinus Torvalds 		break;
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
7131da177e4SLinus Torvalds 		ret = -EACCES;
7141da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
7151da177e4SLinus Torvalds 			goto out;
7161da177e4SLinus Torvalds 		break;
7171da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
7181da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
7191da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
7201da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
7211da177e4SLinus Torvalds 		ret = -EACCES;
7221da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
7231da177e4SLinus Torvalds 			goto out;
7241da177e4SLinus Torvalds 		ret = -EINVAL;
7251da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
7261da177e4SLinus Torvalds 			goto out;
7271da177e4SLinus Torvalds 		break;
7281da177e4SLinus Torvalds 	default:
7291da177e4SLinus Torvalds 		ret = -EINVAL;
7301da177e4SLinus Torvalds 		goto out;
7311da177e4SLinus Torvalds 	}
7321da177e4SLinus Torvalds 
7331da177e4SLinus Torvalds 	rtnl_lock();
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 	ret = -ENODEV;
7369f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
7379f9354b9SEric Dumazet 	if (!dev)
7381da177e4SLinus Torvalds 		goto done;
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds 	if (colon)
7411da177e4SLinus Torvalds 		*colon = ':';
7421da177e4SLinus Torvalds 
7439f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
7449f9354b9SEric Dumazet 	if (in_dev) {
7451da177e4SLinus Torvalds 		if (tryaddrmatch) {
7461da177e4SLinus Torvalds 			/* Matthias Andree */
7471da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
7481da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
7491da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
7501da177e4SLinus Torvalds 			   This is checked above. */
7511da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7521da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
7531da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
7541da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
7556c91afe1SDavid S. Miller 							ifa->ifa_local) {
7561da177e4SLinus Torvalds 					break; /* found */
7571da177e4SLinus Torvalds 				}
7581da177e4SLinus Torvalds 			}
7591da177e4SLinus Torvalds 		}
7601da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
7611da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
7621da177e4SLinus Torvalds 		   comparing just the label */
7631da177e4SLinus Torvalds 		if (!ifa) {
7641da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7651da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
7661da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
7671da177e4SLinus Torvalds 					break;
7681da177e4SLinus Torvalds 		}
7691da177e4SLinus Torvalds 	}
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
7721da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
7731da177e4SLinus Torvalds 		goto done;
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	switch (cmd) {
7761da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7771da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
7781da177e4SLinus Torvalds 		goto rarok;
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7811da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
7821da177e4SLinus Torvalds 		goto rarok;
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7851da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
7861da177e4SLinus Torvalds 		goto rarok;
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
7891da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
7901da177e4SLinus Torvalds 		goto rarok;
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
7931da177e4SLinus Torvalds 		if (colon) {
7941da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
7951da177e4SLinus Torvalds 			if (!ifa)
7961da177e4SLinus Torvalds 				break;
7971da177e4SLinus Torvalds 			ret = 0;
7981da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
7991da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
8001da177e4SLinus Torvalds 			break;
8011da177e4SLinus Torvalds 		}
8021da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
8031da177e4SLinus Torvalds 		break;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
8061da177e4SLinus Torvalds 		ret = -EINVAL;
8071da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8081da177e4SLinus Torvalds 			break;
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds 		if (!ifa) {
8111da177e4SLinus Torvalds 			ret = -ENOBUFS;
8129f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
813fd23c3b3SDavid S. Miller 			INIT_HLIST_NODE(&ifa->hash);
8149f9354b9SEric Dumazet 			if (!ifa)
8151da177e4SLinus Torvalds 				break;
8161da177e4SLinus Torvalds 			if (colon)
8171da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
8181da177e4SLinus Torvalds 			else
8191da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8201da177e4SLinus Torvalds 		} else {
8211da177e4SLinus Torvalds 			ret = 0;
8221da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
8231da177e4SLinus Torvalds 				break;
8241da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8251da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
826148f9729SBjorn Mork 			ifa->ifa_scope = 0;
8271da177e4SLinus Torvalds 		}
8281da177e4SLinus Torvalds 
8291da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
8321da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
8331da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
8341da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
8351da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
8361da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
8371da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
8381da177e4SLinus Torvalds 		} else {
8391da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
8401da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
8411da177e4SLinus Torvalds 		}
8421da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
8431da177e4SLinus Torvalds 		break;
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
8461da177e4SLinus Torvalds 		ret = 0;
8471da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
8481da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8491da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
8501da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
8511da177e4SLinus Torvalds 		}
8521da177e4SLinus Torvalds 		break;
8531da177e4SLinus Torvalds 
8541da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
8551da177e4SLinus Torvalds 		ret = 0;
8561da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
8571da177e4SLinus Torvalds 			break;
8581da177e4SLinus Torvalds 		ret = -EINVAL;
8591da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8601da177e4SLinus Torvalds 			break;
8611da177e4SLinus Torvalds 		ret = 0;
8621da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
8631da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
8641da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
8651da177e4SLinus Torvalds 		break;
8661da177e4SLinus Torvalds 
8671da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
8681da177e4SLinus Torvalds 
8691da177e4SLinus Torvalds 		/*
8701da177e4SLinus Torvalds 		 *	The mask we set must be legal.
8711da177e4SLinus Torvalds 		 */
8721da177e4SLinus Torvalds 		ret = -EINVAL;
8731da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
8741da177e4SLinus Torvalds 			break;
8751da177e4SLinus Torvalds 		ret = 0;
8761da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
877a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
8781da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8791da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
8801da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 			/* See if current broadcast address matches
8831da177e4SLinus Torvalds 			 * with current netmask, then recalculate
8841da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
8851da177e4SLinus Torvalds 			 * funny address, so don't touch it since
8861da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
8871da177e4SLinus Torvalds 			 */
8881da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
8891da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
8901da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
891dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
8921da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
8931da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
8941da177e4SLinus Torvalds 			}
8951da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
8961da177e4SLinus Torvalds 		}
8971da177e4SLinus Torvalds 		break;
8981da177e4SLinus Torvalds 	}
8991da177e4SLinus Torvalds done:
9001da177e4SLinus Torvalds 	rtnl_unlock();
9011da177e4SLinus Torvalds out:
9021da177e4SLinus Torvalds 	return ret;
9031da177e4SLinus Torvalds rarok:
9041da177e4SLinus Torvalds 	rtnl_unlock();
9051da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
9061da177e4SLinus Torvalds 	goto out;
9071da177e4SLinus Torvalds }
9081da177e4SLinus Torvalds 
9091da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
9101da177e4SLinus Torvalds {
911e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
9121da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
9131da177e4SLinus Torvalds 	struct ifreq ifr;
9141da177e4SLinus Torvalds 	int done = 0;
9151da177e4SLinus Torvalds 
9169f9354b9SEric Dumazet 	if (!in_dev)
9171da177e4SLinus Torvalds 		goto out;
9181da177e4SLinus Torvalds 
9199f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
9201da177e4SLinus Torvalds 		if (!buf) {
9211da177e4SLinus Torvalds 			done += sizeof(ifr);
9221da177e4SLinus Torvalds 			continue;
9231da177e4SLinus Torvalds 		}
9241da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
9251da177e4SLinus Torvalds 			break;
9261da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
9271da177e4SLinus Torvalds 		if (ifa->ifa_label)
9281da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, ifa->ifa_label);
9291da177e4SLinus Torvalds 		else
9301da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, dev->name);
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
9331da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
9341da177e4SLinus Torvalds 								ifa->ifa_local;
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
9371da177e4SLinus Torvalds 			done = -EFAULT;
9381da177e4SLinus Torvalds 			break;
9391da177e4SLinus Torvalds 		}
9401da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
9411da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
9421da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
9431da177e4SLinus Torvalds 	}
9441da177e4SLinus Torvalds out:
9451da177e4SLinus Torvalds 	return done;
9461da177e4SLinus Torvalds }
9471da177e4SLinus Torvalds 
948a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
9491da177e4SLinus Torvalds {
950a61ced5dSAl Viro 	__be32 addr = 0;
9511da177e4SLinus Torvalds 	struct in_device *in_dev;
952c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
9531da177e4SLinus Torvalds 
9541da177e4SLinus Torvalds 	rcu_read_lock();
955e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
9561da177e4SLinus Torvalds 	if (!in_dev)
9571da177e4SLinus Torvalds 		goto no_in_dev;
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
9601da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
9611da177e4SLinus Torvalds 			continue;
9621da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
9631da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9641da177e4SLinus Torvalds 			break;
9651da177e4SLinus Torvalds 		}
9661da177e4SLinus Torvalds 		if (!addr)
9671da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9681da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
9691da177e4SLinus Torvalds 
9701da177e4SLinus Torvalds 	if (addr)
971c6d14c84SEric Dumazet 		goto out_unlock;
9729f9354b9SEric Dumazet no_in_dev:
9731da177e4SLinus Torvalds 
9741da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
9751da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
9761da177e4SLinus Torvalds 	   in dev_base list.
9771da177e4SLinus Torvalds 	 */
978c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
9799f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
9809f9354b9SEric Dumazet 		if (!in_dev)
9811da177e4SLinus Torvalds 			continue;
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
9841da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
9851da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
9861da177e4SLinus Torvalds 				addr = ifa->ifa_local;
987c6d14c84SEric Dumazet 				goto out_unlock;
9881da177e4SLinus Torvalds 			}
9891da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
9901da177e4SLinus Torvalds 	}
991c6d14c84SEric Dumazet out_unlock:
9921da177e4SLinus Torvalds 	rcu_read_unlock();
9931da177e4SLinus Torvalds 	return addr;
9941da177e4SLinus Torvalds }
9959f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
9961da177e4SLinus Torvalds 
99760cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
99860cad5daSAl Viro 			      __be32 local, int scope)
9991da177e4SLinus Torvalds {
10001da177e4SLinus Torvalds 	int same = 0;
1001a144ea4bSAl Viro 	__be32 addr = 0;
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	for_ifa(in_dev) {
10041da177e4SLinus Torvalds 		if (!addr &&
10051da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
10061da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
10071da177e4SLinus Torvalds 			addr = ifa->ifa_local;
10081da177e4SLinus Torvalds 			if (same)
10091da177e4SLinus Torvalds 				break;
10101da177e4SLinus Torvalds 		}
10111da177e4SLinus Torvalds 		if (!same) {
10121da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
10131da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
10141da177e4SLinus Torvalds 			if (same && addr) {
10151da177e4SLinus Torvalds 				if (local || !dst)
10161da177e4SLinus Torvalds 					break;
10171da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
10181da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
10191da177e4SLinus Torvalds 					break;
10201da177e4SLinus Torvalds 				/* No, then can we use new local src? */
10211da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
10221da177e4SLinus Torvalds 					addr = ifa->ifa_local;
10231da177e4SLinus Torvalds 					break;
10241da177e4SLinus Torvalds 				}
10251da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
10261da177e4SLinus Torvalds 				same = 0;
10271da177e4SLinus Torvalds 			}
10281da177e4SLinus Torvalds 		}
10291da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds 	return same ? addr : 0;
10321da177e4SLinus Torvalds }
10331da177e4SLinus Torvalds 
10341da177e4SLinus Torvalds /*
10351da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
10369bd85e32SDenis V. Lunev  * - in_dev: only on this interface, 0=any interface
10371da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
10381da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
10391da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
10401da177e4SLinus Torvalds  */
10419bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev,
10429bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
10431da177e4SLinus Torvalds {
104460cad5daSAl Viro 	__be32 addr = 0;
10459bd85e32SDenis V. Lunev 	struct net_device *dev;
104639a6d063SDenis V. Lunev 	struct net *net;
10471da177e4SLinus Torvalds 
104839a6d063SDenis V. Lunev 	if (scope != RT_SCOPE_LINK)
10499bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
10501da177e4SLinus Torvalds 
1051c346dca1SYOSHIFUJI Hideaki 	net = dev_net(in_dev->dev);
10521da177e4SLinus Torvalds 	rcu_read_lock();
1053c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
10549f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
10559f9354b9SEric Dumazet 		if (in_dev) {
10561da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
10571da177e4SLinus Torvalds 			if (addr)
10581da177e4SLinus Torvalds 				break;
10591da177e4SLinus Torvalds 		}
10601da177e4SLinus Torvalds 	}
10611da177e4SLinus Torvalds 	rcu_read_unlock();
10621da177e4SLinus Torvalds 
10631da177e4SLinus Torvalds 	return addr;
10641da177e4SLinus Torvalds }
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds /*
10671da177e4SLinus Torvalds  *	Device notifier
10681da177e4SLinus Torvalds  */
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
10711da177e4SLinus Torvalds {
1072e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
10731da177e4SLinus Torvalds }
10749f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
10771da177e4SLinus Torvalds {
1078e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
10791da177e4SLinus Torvalds }
10809f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
10811da177e4SLinus Torvalds 
10829f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
10839f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
10841da177e4SLinus Torvalds */
10851da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
10861da177e4SLinus Torvalds {
10871da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
10881da177e4SLinus Torvalds 	int named = 0;
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
10911da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
10941da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
10951da177e4SLinus Torvalds 		if (named++ == 0)
1096573bf470SThomas Graf 			goto skip;
109744344b2aSMark McLoughlin 		dot = strchr(old, ':');
10981da177e4SLinus Torvalds 		if (dot == NULL) {
10991da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
11001da177e4SLinus Torvalds 			dot = old;
11011da177e4SLinus Torvalds 		}
11029f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
11031da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
11049f9354b9SEric Dumazet 		else
11051da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1106573bf470SThomas Graf skip:
1107573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
11081da177e4SLinus Torvalds 	}
11091da177e4SLinus Torvalds }
11101da177e4SLinus Torvalds 
111106770843SBreno Leitao static inline bool inetdev_valid_mtu(unsigned mtu)
111206770843SBreno Leitao {
111306770843SBreno Leitao 	return mtu >= 68;
111406770843SBreno Leitao }
111506770843SBreno Leitao 
1116d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1117d11327adSIan Campbell 					struct in_device *in_dev)
1118d11327adSIan Campbell 
1119d11327adSIan Campbell {
1120d11327adSIan Campbell 	struct in_ifaddr *ifa = in_dev->ifa_list;
1121d11327adSIan Campbell 
1122d11327adSIan Campbell 	if (!ifa)
1123d11327adSIan Campbell 		return;
1124d11327adSIan Campbell 
1125d11327adSIan Campbell 	arp_send(ARPOP_REQUEST, ETH_P_ARP,
11266c91afe1SDavid S. Miller 		 ifa->ifa_local, dev,
11276c91afe1SDavid S. Miller 		 ifa->ifa_local, NULL,
1128d11327adSIan Campbell 		 dev->dev_addr, NULL);
1129d11327adSIan Campbell }
1130d11327adSIan Campbell 
11311da177e4SLinus Torvalds /* Called only under RTNL semaphore */
11321da177e4SLinus Torvalds 
11331da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
11341da177e4SLinus Torvalds 			 void *ptr)
11351da177e4SLinus Torvalds {
11361da177e4SLinus Torvalds 	struct net_device *dev = ptr;
1137e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11381da177e4SLinus Torvalds 
11391da177e4SLinus Torvalds 	ASSERT_RTNL();
11401da177e4SLinus Torvalds 
11411da177e4SLinus Torvalds 	if (!in_dev) {
11428030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
11431da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
11448d76527eSHerbert Xu 			if (!in_dev)
1145b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
11460cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
114742f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
114842f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
11491da177e4SLinus Torvalds 			}
115006770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
115106770843SBreno Leitao 			/* Re-enabling IP */
115206770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
115306770843SBreno Leitao 				in_dev = inetdev_init(dev);
11548030f544SHerbert Xu 		}
11551da177e4SLinus Torvalds 		goto out;
11561da177e4SLinus Torvalds 	}
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	switch (event) {
11591da177e4SLinus Torvalds 	case NETDEV_REGISTER:
11601da177e4SLinus Torvalds 		printk(KERN_DEBUG "inetdev_event: bug\n");
116195ae6b22SEric Dumazet 		rcu_assign_pointer(dev->ip_ptr, NULL);
11621da177e4SLinus Torvalds 		break;
11631da177e4SLinus Torvalds 	case NETDEV_UP:
116406770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
11651da177e4SLinus Torvalds 			break;
11660cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
11679f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
11689f9354b9SEric Dumazet 
11699f9354b9SEric Dumazet 			if (ifa) {
1170fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
11711da177e4SLinus Torvalds 				ifa->ifa_local =
11721da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
11731da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
11741da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
11751da177e4SLinus Torvalds 				in_dev_hold(in_dev);
11761da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
11771da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
11781da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11791da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
11801da177e4SLinus Torvalds 			}
11811da177e4SLinus Torvalds 		}
11821da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1183eefef1cfSStephen Hemminger 		/* fall through */
1184eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1185d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1186d11327adSIan Campbell 			break;
1187d11327adSIan Campbell 		/* fall through */
1188d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1189a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1190d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
11911da177e4SLinus Torvalds 		break;
11921da177e4SLinus Torvalds 	case NETDEV_DOWN:
11931da177e4SLinus Torvalds 		ip_mc_down(in_dev);
11941da177e4SLinus Torvalds 		break;
119593d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
119675c78500SMoni Shoua 		ip_mc_unmap(in_dev);
119775c78500SMoni Shoua 		break;
119893d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
119975c78500SMoni Shoua 		ip_mc_remap(in_dev);
120075c78500SMoni Shoua 		break;
12011da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
120206770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
12031da177e4SLinus Torvalds 			break;
120406770843SBreno Leitao 		/* disable IP when MTU is not enough */
12051da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
12061da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
12071da177e4SLinus Torvalds 		break;
12081da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
12091da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
12101da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
12111da177e4SLinus Torvalds 		 */
12121da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
12131da177e4SLinus Torvalds 
121451602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
121566f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
12161da177e4SLinus Torvalds 		break;
12171da177e4SLinus Torvalds 	}
12181da177e4SLinus Torvalds out:
12191da177e4SLinus Torvalds 	return NOTIFY_DONE;
12201da177e4SLinus Torvalds }
12211da177e4SLinus Torvalds 
12221da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
12231da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
12241da177e4SLinus Torvalds };
12251da177e4SLinus Torvalds 
1226339bf98fSThomas Graf static inline size_t inet_nlmsg_size(void)
1227339bf98fSThomas Graf {
1228339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1229339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1230339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1231339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1232339bf98fSThomas Graf 	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1233339bf98fSThomas Graf }
1234339bf98fSThomas Graf 
12351da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1236b6544c0bSJamal Hadi Salim 			    u32 pid, u32 seq, int event, unsigned int flags)
12371da177e4SLinus Torvalds {
12381da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
12391da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
12401da177e4SLinus Torvalds 
124147f68512SThomas Graf 	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
124247f68512SThomas Graf 	if (nlh == NULL)
124326932566SPatrick McHardy 		return -EMSGSIZE;
124447f68512SThomas Graf 
124547f68512SThomas Graf 	ifm = nlmsg_data(nlh);
12461da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
12471da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
12481da177e4SLinus Torvalds 	ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
12491da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
12501da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
12511da177e4SLinus Torvalds 
125247f68512SThomas Graf 	if (ifa->ifa_address)
1253a7a628c4SAl Viro 		NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address);
125447f68512SThomas Graf 
125547f68512SThomas Graf 	if (ifa->ifa_local)
1256a7a628c4SAl Viro 		NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local);
125747f68512SThomas Graf 
125847f68512SThomas Graf 	if (ifa->ifa_broadcast)
1259a7a628c4SAl Viro 		NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast);
126047f68512SThomas Graf 
126147f68512SThomas Graf 	if (ifa->ifa_label[0])
126247f68512SThomas Graf 		NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label);
126347f68512SThomas Graf 
126447f68512SThomas Graf 	return nlmsg_end(skb, nlh);
126547f68512SThomas Graf 
126647f68512SThomas Graf nla_put_failure:
126726932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
126826932566SPatrick McHardy 	return -EMSGSIZE;
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds 
12711da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
12721da177e4SLinus Torvalds {
12733b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1274eec4df98SEric Dumazet 	int h, s_h;
1275eec4df98SEric Dumazet 	int idx, s_idx;
1276eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
12771da177e4SLinus Torvalds 	struct net_device *dev;
12781da177e4SLinus Torvalds 	struct in_device *in_dev;
12791da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1280eec4df98SEric Dumazet 	struct hlist_head *head;
1281eec4df98SEric Dumazet 	struct hlist_node *node;
12821da177e4SLinus Torvalds 
1283eec4df98SEric Dumazet 	s_h = cb->args[0];
1284eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1285eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1286eec4df98SEric Dumazet 
1287eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
12887562f876SPavel Emelianov 		idx = 0;
1289eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1290eec4df98SEric Dumazet 		rcu_read_lock();
1291eec4df98SEric Dumazet 		hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
12921da177e4SLinus Torvalds 			if (idx < s_idx)
12937562f876SPavel Emelianov 				goto cont;
12944b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
12951da177e4SLinus Torvalds 				s_ip_idx = 0;
1296eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
12979f9354b9SEric Dumazet 			if (!in_dev)
12987562f876SPavel Emelianov 				goto cont;
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
13011da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
13021da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1303596e4150SStephen Hemminger 					continue;
1304eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
1305eec4df98SEric Dumazet 					     NETLINK_CB(cb->skb).pid,
13061da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1307eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1308eec4df98SEric Dumazet 					rcu_read_unlock();
13091da177e4SLinus Torvalds 					goto done;
13101da177e4SLinus Torvalds 				}
1311eec4df98SEric Dumazet 			}
13127562f876SPavel Emelianov cont:
13137562f876SPavel Emelianov 			idx++;
13141da177e4SLinus Torvalds 		}
1315eec4df98SEric Dumazet 		rcu_read_unlock();
1316eec4df98SEric Dumazet 	}
13171da177e4SLinus Torvalds 
13181da177e4SLinus Torvalds done:
1319eec4df98SEric Dumazet 	cb->args[0] = h;
1320eec4df98SEric Dumazet 	cb->args[1] = idx;
1321eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
13221da177e4SLinus Torvalds 
13231da177e4SLinus Torvalds 	return skb->len;
13241da177e4SLinus Torvalds }
13251da177e4SLinus Torvalds 
1326d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
1327d6062cbbSThomas Graf 		      u32 pid)
13281da177e4SLinus Torvalds {
132947f68512SThomas Graf 	struct sk_buff *skb;
1330d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1331d6062cbbSThomas Graf 	int err = -ENOBUFS;
13324b8aa9abSDenis V. Lunev 	struct net *net;
13331da177e4SLinus Torvalds 
1334c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1335339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
133647f68512SThomas Graf 	if (skb == NULL)
1337d6062cbbSThomas Graf 		goto errout;
1338d6062cbbSThomas Graf 
1339d6062cbbSThomas Graf 	err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
134026932566SPatrick McHardy 	if (err < 0) {
134126932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
134226932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
134326932566SPatrick McHardy 		kfree_skb(skb);
134426932566SPatrick McHardy 		goto errout;
134526932566SPatrick McHardy 	}
13461ce85fe4SPablo Neira Ayuso 	rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
13471ce85fe4SPablo Neira Ayuso 	return;
1348d6062cbbSThomas Graf errout:
1349d6062cbbSThomas Graf 	if (err < 0)
13504b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
13511da177e4SLinus Torvalds }
13521da177e4SLinus Torvalds 
13539f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
13549f0f7272SThomas Graf {
1355f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13569f0f7272SThomas Graf 
13579f0f7272SThomas Graf 	if (!in_dev)
13589f0f7272SThomas Graf 		return 0;
13599f0f7272SThomas Graf 
13609f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
13619f0f7272SThomas Graf }
13629f0f7272SThomas Graf 
13639f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
13649f0f7272SThomas Graf {
1365f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13669f0f7272SThomas Graf 	struct nlattr *nla;
13679f0f7272SThomas Graf 	int i;
13689f0f7272SThomas Graf 
13699f0f7272SThomas Graf 	if (!in_dev)
13709f0f7272SThomas Graf 		return -ENODATA;
13719f0f7272SThomas Graf 
13729f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
13739f0f7272SThomas Graf 	if (nla == NULL)
13749f0f7272SThomas Graf 		return -EMSGSIZE;
13759f0f7272SThomas Graf 
13769f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
13779f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
13789f0f7272SThomas Graf 
13799f0f7272SThomas Graf 	return 0;
13809f0f7272SThomas Graf }
13819f0f7272SThomas Graf 
13829f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
13839f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
13849f0f7272SThomas Graf };
13859f0f7272SThomas Graf 
1386cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1387cf7afbfeSThomas Graf 				 const struct nlattr *nla)
13889f0f7272SThomas Graf {
13899f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
13909f0f7272SThomas Graf 	int err, rem;
13919f0f7272SThomas Graf 
1392f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1393cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
13949f0f7272SThomas Graf 
13959f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
13969f0f7272SThomas Graf 	if (err < 0)
13979f0f7272SThomas Graf 		return err;
13989f0f7272SThomas Graf 
13999f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14009f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
14019f0f7272SThomas Graf 			int cfgid = nla_type(a);
14029f0f7272SThomas Graf 
14039f0f7272SThomas Graf 			if (nla_len(a) < 4)
14049f0f7272SThomas Graf 				return -EINVAL;
14059f0f7272SThomas Graf 
14069f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
14079f0f7272SThomas Graf 				return -EINVAL;
14089f0f7272SThomas Graf 		}
14099f0f7272SThomas Graf 	}
14109f0f7272SThomas Graf 
1411cf7afbfeSThomas Graf 	return 0;
1412cf7afbfeSThomas Graf }
1413cf7afbfeSThomas Graf 
1414cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1415cf7afbfeSThomas Graf {
1416f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1417cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1418cf7afbfeSThomas Graf 	int rem;
1419cf7afbfeSThomas Graf 
1420cf7afbfeSThomas Graf 	if (!in_dev)
1421cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1422cf7afbfeSThomas Graf 
1423cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1424cf7afbfeSThomas Graf 		BUG();
1425cf7afbfeSThomas Graf 
14269f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14279f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
14289f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
14299f0f7272SThomas Graf 	}
14309f0f7272SThomas Graf 
14319f0f7272SThomas Graf 	return 0;
14329f0f7272SThomas Graf }
14339f0f7272SThomas Graf 
14341da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
14351da177e4SLinus Torvalds 
1436c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
143731be3085SHerbert Xu {
143831be3085SHerbert Xu 	struct net_device *dev;
143931be3085SHerbert Xu 
144031be3085SHerbert Xu 	rcu_read_lock();
1441c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1442c6d14c84SEric Dumazet 		struct in_device *in_dev;
1443c6d14c84SEric Dumazet 
144431be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
144531be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
14469355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1447c6d14c84SEric Dumazet 	}
144831be3085SHerbert Xu 	rcu_read_unlock();
144931be3085SHerbert Xu }
145031be3085SHerbert Xu 
1451c6d14c84SEric Dumazet /* called with RTNL locked */
1452c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
145368dd299bSPavel Emelyanov {
145468dd299bSPavel Emelyanov 	struct net_device *dev;
1455586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
145668dd299bSPavel Emelyanov 
1457586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
14589355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
145968dd299bSPavel Emelyanov 
1460c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
146168dd299bSPavel Emelyanov 		struct in_device *in_dev;
14620187bdfbSBen Hutchings 		if (on)
14630187bdfbSBen Hutchings 			dev_disable_lro(dev);
146468dd299bSPavel Emelyanov 		rcu_read_lock();
146568dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
146668dd299bSPavel Emelyanov 		if (in_dev)
146768dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
146868dd299bSPavel Emelyanov 		rcu_read_unlock();
146968dd299bSPavel Emelyanov 	}
147068dd299bSPavel Emelyanov }
147168dd299bSPavel Emelyanov 
147231be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write,
14738d65af78SAlexey Dobriyan 			     void __user *buffer,
147431be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
147531be3085SHerbert Xu {
14768d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
147731be3085SHerbert Xu 
147831be3085SHerbert Xu 	if (write) {
147931be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1480c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
148131be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
148231be3085SHerbert Xu 
148331be3085SHerbert Xu 		set_bit(i, cnf->state);
148431be3085SHerbert Xu 
14859355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1486c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
148731be3085SHerbert Xu 	}
148831be3085SHerbert Xu 
148931be3085SHerbert Xu 	return ret;
149031be3085SHerbert Xu }
149131be3085SHerbert Xu 
14921da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write,
14938d65af78SAlexey Dobriyan 				  void __user *buffer,
14941da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
14951da177e4SLinus Torvalds {
14961da177e4SLinus Torvalds 	int *valp = ctl->data;
14971da177e4SLinus Torvalds 	int val = *valp;
149888af182eSEric W. Biederman 	loff_t pos = *ppos;
14998d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
15001da177e4SLinus Torvalds 
15011da177e4SLinus Torvalds 	if (write && *valp != val) {
1502c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
1503c0ce9fb3SPavel Emelyanov 
15040187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
150588af182eSEric W. Biederman 			if (!rtnl_trylock()) {
150688af182eSEric W. Biederman 				/* Restore the original values before restarting */
150788af182eSEric W. Biederman 				*valp = val;
150888af182eSEric W. Biederman 				*ppos = pos;
15099b8adb5eSEric W. Biederman 				return restart_syscall();
151088af182eSEric W. Biederman 			}
15110187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1512c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
15130187bdfbSBen Hutchings 			} else if (*valp) {
15140187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
15150187bdfbSBen Hutchings 				struct in_device *idev =
15160187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
15170187bdfbSBen Hutchings 				dev_disable_lro(idev->dev);
15180187bdfbSBen Hutchings 			}
15190187bdfbSBen Hutchings 			rtnl_unlock();
152076e6ebfbSDenis V. Lunev 			rt_cache_flush(net, 0);
15211da177e4SLinus Torvalds 		}
15220187bdfbSBen Hutchings 	}
15231da177e4SLinus Torvalds 
15241da177e4SLinus Torvalds 	return ret;
15251da177e4SLinus Torvalds }
15261da177e4SLinus Torvalds 
1527323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write,
15288d65af78SAlexey Dobriyan 				void __user *buffer,
15291da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
15301da177e4SLinus Torvalds {
15311da177e4SLinus Torvalds 	int *valp = ctl->data;
15321da177e4SLinus Torvalds 	int val = *valp;
15338d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
153476e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
15351da177e4SLinus Torvalds 
15361da177e4SLinus Torvalds 	if (write && *valp != val)
153776e6ebfbSDenis V. Lunev 		rt_cache_flush(net, 0);
15381da177e4SLinus Torvalds 
15391da177e4SLinus Torvalds 	return ret;
15401da177e4SLinus Torvalds }
15411da177e4SLinus Torvalds 
1542f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
154342f811b8SHerbert Xu 	{ \
154442f811b8SHerbert Xu 		.procname	= name, \
154542f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
154602291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
154742f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
154842f811b8SHerbert Xu 		.mode		= mval, \
154942f811b8SHerbert Xu 		.proc_handler	= proc, \
155031be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
155142f811b8SHerbert Xu 	}
155242f811b8SHerbert Xu 
155342f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
1554f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
155542f811b8SHerbert Xu 
155642f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
1557f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
155842f811b8SHerbert Xu 
1559f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1560f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
156142f811b8SHerbert Xu 
156242f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
1563f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
156442f811b8SHerbert Xu 
15651da177e4SLinus Torvalds static struct devinet_sysctl_table {
15661da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
156702291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
1568bfada697SPavel Emelyanov 	char *dev_name;
15691da177e4SLinus Torvalds } devinet_sysctl = {
15701da177e4SLinus Torvalds 	.devinet_vars = {
157142f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
1572f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
157342f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
157442f811b8SHerbert Xu 
157542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
157642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
157742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
157842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
157942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
158042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
158142f811b8SHerbert Xu 					"accept_source_route"),
15828153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
158328f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
158442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
158542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
158642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
158742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
158842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
158942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
159042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
159142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
159242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
1593eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
159465324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
159542f811b8SHerbert Xu 
159642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
159742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
159842f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
159942f811b8SHerbert Xu 					      "force_igmp_version"),
160042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
160142f811b8SHerbert Xu 					      "promote_secondaries"),
16021da177e4SLinus Torvalds 	},
16031da177e4SLinus Torvalds };
16041da177e4SLinus Torvalds 
1605ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
1606f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
16071da177e4SLinus Torvalds {
16081da177e4SLinus Torvalds 	int i;
16099fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
16101da177e4SLinus Torvalds 
1611bfada697SPavel Emelyanov #define DEVINET_CTL_PATH_DEV	3
1612bfada697SPavel Emelyanov 
1613bfada697SPavel Emelyanov 	struct ctl_path devinet_ctl_path[] = {
1614f8572d8fSEric W. Biederman 		{ .procname = "net",  },
1615f8572d8fSEric W. Biederman 		{ .procname = "ipv4", },
1616f8572d8fSEric W. Biederman 		{ .procname = "conf", },
1617bfada697SPavel Emelyanov 		{ /* to be set */ },
1618bfada697SPavel Emelyanov 		{ },
1619bfada697SPavel Emelyanov 	};
1620bfada697SPavel Emelyanov 
16219fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
16221da177e4SLinus Torvalds 	if (!t)
16239fa89642SPavel Emelyanov 		goto out;
16249fa89642SPavel Emelyanov 
16251da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
16261da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
162731be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
1628c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
16291da177e4SLinus Torvalds 	}
16301da177e4SLinus Torvalds 
16311da177e4SLinus Torvalds 	/*
16321da177e4SLinus Torvalds 	 * Make a copy of dev_name, because '.procname' is regarded as const
16331da177e4SLinus Torvalds 	 * by sysctl and we wouldn't want anyone to change it under our feet
16341da177e4SLinus Torvalds 	 * (see SIOCSIFNAME).
16351da177e4SLinus Torvalds 	 */
1636bfada697SPavel Emelyanov 	t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1637bfada697SPavel Emelyanov 	if (!t->dev_name)
16381da177e4SLinus Torvalds 		goto free;
16391da177e4SLinus Torvalds 
1640bfada697SPavel Emelyanov 	devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
16411da177e4SLinus Torvalds 
1642752d14dcSPavel Emelyanov 	t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
1643bfada697SPavel Emelyanov 			t->devinet_vars);
16441da177e4SLinus Torvalds 	if (!t->sysctl_header)
16451da177e4SLinus Torvalds 		goto free_procname;
16461da177e4SLinus Torvalds 
16471da177e4SLinus Torvalds 	p->sysctl = t;
1648ea40b324SPavel Emelyanov 	return 0;
16491da177e4SLinus Torvalds 
16501da177e4SLinus Torvalds free_procname:
1651bfada697SPavel Emelyanov 	kfree(t->dev_name);
16521da177e4SLinus Torvalds free:
16531da177e4SLinus Torvalds 	kfree(t);
16549fa89642SPavel Emelyanov out:
1655ea40b324SPavel Emelyanov 	return -ENOBUFS;
16561da177e4SLinus Torvalds }
16571da177e4SLinus Torvalds 
165851602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
165966f27a52SPavel Emelyanov {
166051602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
166166f27a52SPavel Emelyanov 
166251602b2aSPavel Emelyanov 	if (t == NULL)
166351602b2aSPavel Emelyanov 		return;
166451602b2aSPavel Emelyanov 
166551602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
16661da177e4SLinus Torvalds 	unregister_sysctl_table(t->sysctl_header);
1667bfada697SPavel Emelyanov 	kfree(t->dev_name);
16681da177e4SLinus Torvalds 	kfree(t);
16691da177e4SLinus Torvalds }
167051602b2aSPavel Emelyanov 
167151602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
167251602b2aSPavel Emelyanov {
167354716e3bSEric W. Biederman 	neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
1674c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
1675f8572d8fSEric W. Biederman 					&idev->cnf);
167651602b2aSPavel Emelyanov }
167751602b2aSPavel Emelyanov 
167851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
167951602b2aSPavel Emelyanov {
168051602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
168151602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
16821da177e4SLinus Torvalds }
16831da177e4SLinus Torvalds 
168468dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
168568dd299bSPavel Emelyanov 	{
168668dd299bSPavel Emelyanov 		.procname	= "ip_forward",
168768dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
168802291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
168968dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
169068dd299bSPavel Emelyanov 		.mode		= 0644,
169168dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
169268dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
1693c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
169468dd299bSPavel Emelyanov 	},
169568dd299bSPavel Emelyanov 	{ },
169668dd299bSPavel Emelyanov };
169768dd299bSPavel Emelyanov 
1698752d14dcSPavel Emelyanov static __net_initdata struct ctl_path net_ipv4_path[] = {
1699f8572d8fSEric W. Biederman 	{ .procname = "net", },
1700f8572d8fSEric W. Biederman 	{ .procname = "ipv4", },
170168dd299bSPavel Emelyanov 	{ },
170268dd299bSPavel Emelyanov };
17032a75de0cSEric Dumazet #endif
170468dd299bSPavel Emelyanov 
1705752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
1706752d14dcSPavel Emelyanov {
1707752d14dcSPavel Emelyanov 	int err;
1708752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
17092a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
17102a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
1711752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
17122a75de0cSEric Dumazet #endif
1713752d14dcSPavel Emelyanov 
1714752d14dcSPavel Emelyanov 	err = -ENOMEM;
1715752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
1716752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
1717752d14dcSPavel Emelyanov 
171809ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
1719752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1720752d14dcSPavel Emelyanov 		if (all == NULL)
1721752d14dcSPavel Emelyanov 			goto err_alloc_all;
1722752d14dcSPavel Emelyanov 
1723752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1724752d14dcSPavel Emelyanov 		if (dflt == NULL)
1725752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
1726752d14dcSPavel Emelyanov 
17272a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1728752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1729752d14dcSPavel Emelyanov 		if (tbl == NULL)
1730752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
1731752d14dcSPavel Emelyanov 
173202291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
1733752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
1734752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
17352a75de0cSEric Dumazet #endif
1736752d14dcSPavel Emelyanov 	}
1737752d14dcSPavel Emelyanov 
1738752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1739f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
1740752d14dcSPavel Emelyanov 	if (err < 0)
1741752d14dcSPavel Emelyanov 		goto err_reg_all;
1742752d14dcSPavel Emelyanov 
1743f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
1744752d14dcSPavel Emelyanov 	if (err < 0)
1745752d14dcSPavel Emelyanov 		goto err_reg_dflt;
1746752d14dcSPavel Emelyanov 
1747752d14dcSPavel Emelyanov 	err = -ENOMEM;
1748752d14dcSPavel Emelyanov 	forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1749752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
1750752d14dcSPavel Emelyanov 		goto err_reg_ctl;
17512a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
1752752d14dcSPavel Emelyanov #endif
1753752d14dcSPavel Emelyanov 
1754752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
1755752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
1756752d14dcSPavel Emelyanov 	return 0;
1757752d14dcSPavel Emelyanov 
1758752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1759752d14dcSPavel Emelyanov err_reg_ctl:
1760752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
1761752d14dcSPavel Emelyanov err_reg_dflt:
1762752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
1763752d14dcSPavel Emelyanov err_reg_all:
1764752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
1765752d14dcSPavel Emelyanov 		kfree(tbl);
1766752d14dcSPavel Emelyanov err_alloc_ctl:
17672a75de0cSEric Dumazet #endif
1768752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
1769752d14dcSPavel Emelyanov 		kfree(dflt);
1770752d14dcSPavel Emelyanov err_alloc_dflt:
1771752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
1772752d14dcSPavel Emelyanov 		kfree(all);
1773752d14dcSPavel Emelyanov err_alloc_all:
1774752d14dcSPavel Emelyanov 	return err;
1775752d14dcSPavel Emelyanov }
1776752d14dcSPavel Emelyanov 
1777752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
1778752d14dcSPavel Emelyanov {
17792a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1780752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
1781752d14dcSPavel Emelyanov 
1782752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
1783752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
1784752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1785752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
1786752d14dcSPavel Emelyanov 	kfree(tbl);
17872a75de0cSEric Dumazet #endif
1788752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
1789752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
1790752d14dcSPavel Emelyanov }
1791752d14dcSPavel Emelyanov 
1792752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
1793752d14dcSPavel Emelyanov 	.init = devinet_init_net,
1794752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
1795752d14dcSPavel Emelyanov };
1796752d14dcSPavel Emelyanov 
17979f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
17989f0f7272SThomas Graf 	.family		  = AF_INET,
17999f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
18009f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
1801cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
1802cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
18039f0f7272SThomas Graf };
18049f0f7272SThomas Graf 
18051da177e4SLinus Torvalds void __init devinet_init(void)
18061da177e4SLinus Torvalds {
1807fd23c3b3SDavid S. Miller 	int i;
1808fd23c3b3SDavid S. Miller 
1809fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
1810fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
1811fd23c3b3SDavid S. Miller 
1812752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
1813752d14dcSPavel Emelyanov 
18141da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
18151da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
181663f3444fSThomas Graf 
18179f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
18189f0f7272SThomas Graf 
181963f3444fSThomas Graf 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
182063f3444fSThomas Graf 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
182163f3444fSThomas Graf 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
18221da177e4SLinus Torvalds }
18231da177e4SLinus Torvalds 
1824