xref: /linux/net/ipv4/devinet.c (revision 15e473046cb6e5d18a4d0057e61d76315230382b)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	NET3	IP device support routines.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
1002c30a84SJesper Juhl  * 		Authors:	Ross Biro
111da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
121da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Additional Authors:
151da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
161da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *	Changes:
191da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
201da177e4SLinus Torvalds  *					lists.
211da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
221da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
231da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
241da177e4SLinus Torvalds  *					fall back to comparing just the label
251da177e4SLinus Torvalds  *					if no match found.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <asm/uaccess.h>
301da177e4SLinus Torvalds #include <linux/bitops.h>
314fc268d2SRandy Dunlap #include <linux/capability.h>
321da177e4SLinus Torvalds #include <linux/module.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/kernel.h>
351da177e4SLinus Torvalds #include <linux/string.h>
361da177e4SLinus Torvalds #include <linux/mm.h>
371da177e4SLinus Torvalds #include <linux/socket.h>
381da177e4SLinus Torvalds #include <linux/sockios.h>
391da177e4SLinus Torvalds #include <linux/in.h>
401da177e4SLinus Torvalds #include <linux/errno.h>
411da177e4SLinus Torvalds #include <linux/interrupt.h>
421823730fSThomas Graf #include <linux/if_addr.h>
431da177e4SLinus Torvalds #include <linux/if_ether.h>
441da177e4SLinus Torvalds #include <linux/inet.h>
451da177e4SLinus Torvalds #include <linux/netdevice.h>
461da177e4SLinus Torvalds #include <linux/etherdevice.h>
471da177e4SLinus Torvalds #include <linux/skbuff.h>
481da177e4SLinus Torvalds #include <linux/init.h>
491da177e4SLinus Torvalds #include <linux/notifier.h>
501da177e4SLinus Torvalds #include <linux/inetdevice.h>
511da177e4SLinus Torvalds #include <linux/igmp.h>
525a0e3ad6STejun Heo #include <linux/slab.h>
53fd23c3b3SDavid S. Miller #include <linux/hash.h>
541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
551da177e4SLinus Torvalds #include <linux/sysctl.h>
561da177e4SLinus Torvalds #endif
571da177e4SLinus Torvalds #include <linux/kmod.h>
581da177e4SLinus Torvalds 
5914c85021SArnaldo Carvalho de Melo #include <net/arp.h>
601da177e4SLinus Torvalds #include <net/ip.h>
611da177e4SLinus Torvalds #include <net/route.h>
621da177e4SLinus Torvalds #include <net/ip_fib.h>
6363f3444fSThomas Graf #include <net/rtnetlink.h>
64752d14dcSPavel Emelyanov #include <net/net_namespace.h>
651da177e4SLinus Torvalds 
66406b6f97SDavid S. Miller #include "fib_lookup.h"
67406b6f97SDavid S. Miller 
680027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
6942f811b8SHerbert Xu 	.data = {
7002291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7102291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7202291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
7442f811b8SHerbert Xu 	},
751da177e4SLinus Torvalds };
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
7842f811b8SHerbert Xu 	.data = {
7902291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8002291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8102291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8202291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8302291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
8442f811b8SHerbert Xu 	},
851da177e4SLinus Torvalds };
861da177e4SLinus Torvalds 
879355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
889355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
8942f811b8SHerbert Xu 
90ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
915c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
925c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
935c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
945176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
955c753978SThomas Graf };
965c753978SThomas Graf 
9740384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
9840384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
9940384999SEric Dumazet 
100fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
101fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock);
102fd23c3b3SDavid S. Miller 
10340384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr)
104fd23c3b3SDavid S. Miller {
10540384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
106fd23c3b3SDavid S. Miller 
10740384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
108fd23c3b3SDavid S. Miller }
109fd23c3b3SDavid S. Miller 
110fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
111fd23c3b3SDavid S. Miller {
11240384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
113fd23c3b3SDavid S. Miller 
114fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
115fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
116fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
117fd23c3b3SDavid S. Miller }
118fd23c3b3SDavid S. Miller 
119fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
120fd23c3b3SDavid S. Miller {
121fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
122fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
123fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
124fd23c3b3SDavid S. Miller }
125fd23c3b3SDavid S. Miller 
1269435eb1cSDavid S. Miller /**
1279435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1289435eb1cSDavid S. Miller  * @net: the net namespace
1299435eb1cSDavid S. Miller  * @addr: the source address
1309435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1319435eb1cSDavid S. Miller  *
1329435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1339435eb1cSDavid S. Miller  */
1349435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1359435eb1cSDavid S. Miller {
13640384999SEric Dumazet 	u32 hash = inet_addr_hash(net, addr);
1379435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1389435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1399435eb1cSDavid S. Miller 	struct hlist_node *node;
1409435eb1cSDavid S. Miller 
1419435eb1cSDavid S. Miller 	rcu_read_lock();
1429435eb1cSDavid S. Miller 	hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
14340384999SEric Dumazet 		if (ifa->ifa_local == addr) {
1449435eb1cSDavid S. Miller 			struct net_device *dev = ifa->ifa_dev->dev;
1459435eb1cSDavid S. Miller 
1469435eb1cSDavid S. Miller 			if (!net_eq(dev_net(dev), net))
1479435eb1cSDavid S. Miller 				continue;
1489435eb1cSDavid S. Miller 			result = dev;
1499435eb1cSDavid S. Miller 			break;
1509435eb1cSDavid S. Miller 		}
1519435eb1cSDavid S. Miller 	}
152406b6f97SDavid S. Miller 	if (!result) {
153406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
154406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
155406b6f97SDavid S. Miller 		struct fib_table *local;
156406b6f97SDavid S. Miller 
157406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
158406b6f97SDavid S. Miller 		 * over loopback subnets work.
159406b6f97SDavid S. Miller 		 */
160406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
161406b6f97SDavid S. Miller 		if (local &&
162406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
163406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
164406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
165406b6f97SDavid S. Miller 	}
1669435eb1cSDavid S. Miller 	if (result && devref)
1679435eb1cSDavid S. Miller 		dev_hold(result);
1689435eb1cSDavid S. Miller 	rcu_read_unlock();
1699435eb1cSDavid S. Miller 	return result;
1709435eb1cSDavid S. Miller }
1719435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1729435eb1cSDavid S. Miller 
173d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1741da177e4SLinus Torvalds 
175e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1761da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1771da177e4SLinus Torvalds 			 int destroy);
1781da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
17966f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev);
18051602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
18151602b2aSPavel Emelyanov #else
18240384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev)
18351602b2aSPavel Emelyanov {
18451602b2aSPavel Emelyanov }
18540384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
18651602b2aSPavel Emelyanov {
18751602b2aSPavel Emelyanov }
1881da177e4SLinus Torvalds #endif
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds /* Locks all the inet devices. */
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1931da177e4SLinus Torvalds {
19493adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2001da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2011da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2021da177e4SLinus Torvalds 	kfree(ifa);
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds 
20540384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2061da177e4SLinus Torvalds {
2071da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2131da177e4SLinus Torvalds 
214547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
215547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2161da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
21791df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2181da177e4SLinus Torvalds #endif
2191da177e4SLinus Torvalds 	dev_put(dev);
2201da177e4SLinus Torvalds 	if (!idev->dead)
2219f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2229f9354b9SEric Dumazet 	else
2231da177e4SLinus Torvalds 		kfree(idev);
2241da177e4SLinus Torvalds }
2259f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2261da177e4SLinus Torvalds 
22771e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds 	struct in_device *in_dev;
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 	ASSERT_RTNL();
2321da177e4SLinus Torvalds 
2330da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2341da177e4SLinus Torvalds 	if (!in_dev)
2351da177e4SLinus Torvalds 		goto out;
236c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2379355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2381da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2391da177e4SLinus Torvalds 	in_dev->dev = dev;
2409f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2419f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2421da177e4SLinus Torvalds 		goto out_kfree;
2430187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2440187bdfbSBen Hutchings 		dev_disable_lro(dev);
2451da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2461da177e4SLinus Torvalds 	dev_hold(dev);
24730c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2481da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2491da177e4SLinus Torvalds 
25066f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2511da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2521da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2531da177e4SLinus Torvalds 		ip_mc_up(in_dev);
254483479ecSJarek Poplawski 
25530c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
256cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
257483479ecSJarek Poplawski out:
2581da177e4SLinus Torvalds 	return in_dev;
2591da177e4SLinus Torvalds out_kfree:
2601da177e4SLinus Torvalds 	kfree(in_dev);
2611da177e4SLinus Torvalds 	in_dev = NULL;
2621da177e4SLinus Torvalds 	goto out;
2631da177e4SLinus Torvalds }
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2661da177e4SLinus Torvalds {
2671da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2681da177e4SLinus Torvalds 	in_dev_put(idev);
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2741da177e4SLinus Torvalds 	struct net_device *dev;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	ASSERT_RTNL();
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	dev = in_dev->dev;
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	in_dev->dead = 1;
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2851da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2861da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2871da177e4SLinus Torvalds 	}
2881da177e4SLinus Torvalds 
289a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
2901da177e4SLinus Torvalds 
29151602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
2921da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
2931da177e4SLinus Torvalds 	arp_ifdown(dev);
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
298ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds 	rcu_read_lock();
3011da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3021da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3031da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3041da177e4SLinus Torvalds 				rcu_read_unlock();
3051da177e4SLinus Torvalds 				return 1;
3061da177e4SLinus Torvalds 			}
3071da177e4SLinus Torvalds 		}
3081da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3091da177e4SLinus Torvalds 	rcu_read_unlock();
3101da177e4SLinus Torvalds 	return 0;
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds 
313d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
314*15e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3151da177e4SLinus Torvalds {
3168f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3170ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3180ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3190ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3200ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	ASSERT_RTNL();
3231da177e4SLinus Torvalds 
3248f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3258f937c60SHarald Welte 	 * unless alias promotion is set
3268f937c60SHarald Welte 	 **/
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3291da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3320ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3330ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3340ff60a45SJamal Hadi Salim 				last_prim = ifa;
3350ff60a45SJamal Hadi Salim 
3361da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3371da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3381da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3391da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3400ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3411da177e4SLinus Torvalds 				continue;
3421da177e4SLinus Torvalds 			}
3431da177e4SLinus Torvalds 
3440ff60a45SJamal Hadi Salim 			if (!do_promote) {
345fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3461da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3471da177e4SLinus Torvalds 
348*15e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
349e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
350e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3511da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3528f937c60SHarald Welte 			} else {
3538f937c60SHarald Welte 				promote = ifa;
3548f937c60SHarald Welte 				break;
3558f937c60SHarald Welte 			}
3561da177e4SLinus Torvalds 		}
3571da177e4SLinus Torvalds 	}
3581da177e4SLinus Torvalds 
3592d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3602d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3612d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3622d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3632d230e2bSJulian Anastasov 	 */
3642d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3652d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3662d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3672d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3682d230e2bSJulian Anastasov 	}
3692d230e2bSJulian Anastasov 
3701da177e4SLinus Torvalds 	/* 2. Unlink it */
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
373fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3781da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3791da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3801da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3811da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3821da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3831da177e4SLinus Torvalds 	   So that, this order is correct.
3841da177e4SLinus Torvalds 	 */
385*15e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
386e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3870ff60a45SJamal Hadi Salim 
3880ff60a45SJamal Hadi Salim 	if (promote) {
38904024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
3900ff60a45SJamal Hadi Salim 
3910ff60a45SJamal Hadi Salim 		if (prev_prom) {
3920ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
3930ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
3940ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
3950ff60a45SJamal Hadi Salim 		}
3960ff60a45SJamal Hadi Salim 
3970ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
398*15e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
399e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
400e041c683SAlan Stern 				NETDEV_UP, promote);
40104024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4020ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4030ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4040ff60a45SJamal Hadi Salim 					continue;
4050ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4060ff60a45SJamal Hadi Salim 		}
4070ff60a45SJamal Hadi Salim 
4080ff60a45SJamal Hadi Salim 	}
4096363097cSHerbert Xu 	if (destroy)
4101da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
413d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
414d6062cbbSThomas Graf 			 int destroy)
415d6062cbbSThomas Graf {
416d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
417d6062cbbSThomas Graf }
418d6062cbbSThomas Graf 
419d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
420*15e47304SEric W. Biederman 			     u32 portid)
4211da177e4SLinus Torvalds {
4221da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4231da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds 	ASSERT_RTNL();
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4281da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4291da177e4SLinus Torvalds 		return 0;
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4331da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4361da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4371da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4381da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4391da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4401da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4411da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4421da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4431da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4441da177e4SLinus Torvalds 				return -EEXIST;
4451da177e4SLinus Torvalds 			}
4461da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4471da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4481da177e4SLinus Torvalds 				return -EINVAL;
4491da177e4SLinus Torvalds 			}
4501da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4511da177e4SLinus Torvalds 		}
4521da177e4SLinus Torvalds 	}
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
4551da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4561da177e4SLinus Torvalds 		ifap = last_primary;
4571da177e4SLinus Torvalds 	}
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4601da177e4SLinus Torvalds 	*ifap = ifa;
4611da177e4SLinus Torvalds 
462fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
463fd23c3b3SDavid S. Miller 
4641da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4651da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4661da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
467*15e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
468e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	return 0;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
473d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
474d6062cbbSThomas Graf {
475d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
476d6062cbbSThomas Graf }
477d6062cbbSThomas Graf 
4781da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4791da177e4SLinus Torvalds {
480e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 	ASSERT_RTNL();
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds 	if (!in_dev) {
4851da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4861da177e4SLinus Torvalds 		return -ENOBUFS;
4871da177e4SLinus Torvalds 	}
48871e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
4891da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
490547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
4911da177e4SLinus Torvalds 		in_dev_hold(in_dev);
4921da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
4931da177e4SLinus Torvalds 	}
494f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
4951da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
4961da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
4971da177e4SLinus Torvalds }
4981da177e4SLinus Torvalds 
4998723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5008723e1b4SEric Dumazet  * We dont take a reference on found in_device
5018723e1b4SEric Dumazet  */
5027fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5031da177e4SLinus Torvalds {
5041da177e4SLinus Torvalds 	struct net_device *dev;
5051da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
506c148fc2eSEric Dumazet 
507c148fc2eSEric Dumazet 	rcu_read_lock();
508c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5091da177e4SLinus Torvalds 	if (dev)
5108723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
511c148fc2eSEric Dumazet 	rcu_read_unlock();
5121da177e4SLinus Torvalds 	return in_dev;
5131da177e4SLinus Torvalds }
5149f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5171da177e4SLinus Torvalds 
51860cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
51960cad5daSAl Viro 				    __be32 mask)
5201da177e4SLinus Torvalds {
5211da177e4SLinus Torvalds 	ASSERT_RTNL();
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5241da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5251da177e4SLinus Torvalds 			return ifa;
5261da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5271da177e4SLinus Torvalds 	return NULL;
5281da177e4SLinus Torvalds }
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
5311da177e4SLinus Torvalds {
5323b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
533dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5341da177e4SLinus Torvalds 	struct in_device *in_dev;
535dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5361da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
537dfdd5fd4SThomas Graf 	int err = -EINVAL;
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds 	ASSERT_RTNL();
5401da177e4SLinus Torvalds 
541dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
542dfdd5fd4SThomas Graf 	if (err < 0)
543dfdd5fd4SThomas Graf 		goto errout;
544dfdd5fd4SThomas Graf 
545dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5467fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
547dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
548dfdd5fd4SThomas Graf 		err = -ENODEV;
549dfdd5fd4SThomas Graf 		goto errout;
550dfdd5fd4SThomas Graf 	}
551dfdd5fd4SThomas Graf 
5521da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5531da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
554dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
555a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5561da177e4SLinus Torvalds 			continue;
557dfdd5fd4SThomas Graf 
558dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
559dfdd5fd4SThomas Graf 			continue;
560dfdd5fd4SThomas Graf 
561dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
562dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
563a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
564dfdd5fd4SThomas Graf 			continue;
565dfdd5fd4SThomas Graf 
566*15e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
5671da177e4SLinus Torvalds 		return 0;
5681da177e4SLinus Torvalds 	}
569dfdd5fd4SThomas Graf 
570dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
571dfdd5fd4SThomas Graf errout:
572dfdd5fd4SThomas Graf 	return err;
5731da177e4SLinus Torvalds }
5741da177e4SLinus Torvalds 
5754b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
5761da177e4SLinus Torvalds {
5775c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5785c753978SThomas Graf 	struct in_ifaddr *ifa;
5795c753978SThomas Graf 	struct ifaddrmsg *ifm;
5801da177e4SLinus Torvalds 	struct net_device *dev;
5811da177e4SLinus Torvalds 	struct in_device *in_dev;
5827b218574SDenis V. Lunev 	int err;
5831da177e4SLinus Torvalds 
5845c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
5855c753978SThomas Graf 	if (err < 0)
5865c753978SThomas Graf 		goto errout;
5871da177e4SLinus Torvalds 
5885c753978SThomas Graf 	ifm = nlmsg_data(nlh);
589c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
5907b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
5915c753978SThomas Graf 		goto errout;
5921da177e4SLinus Torvalds 
5934b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
5945c753978SThomas Graf 	err = -ENODEV;
5957b218574SDenis V. Lunev 	if (dev == NULL)
5965c753978SThomas Graf 		goto errout;
5971da177e4SLinus Torvalds 
5985c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
5995c753978SThomas Graf 	err = -ENOBUFS;
6007b218574SDenis V. Lunev 	if (in_dev == NULL)
6015c753978SThomas Graf 		goto errout;
60271e27da9SHerbert Xu 
6035c753978SThomas Graf 	ifa = inet_alloc_ifa();
6047b218574SDenis V. Lunev 	if (ifa == NULL)
6055c753978SThomas Graf 		/*
6065c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
6075c753978SThomas Graf 		 * assigned to its device and is destroy with it.
6085c753978SThomas Graf 		 */
6095c753978SThomas Graf 		goto errout;
6105c753978SThomas Graf 
611a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
6125c753978SThomas Graf 	in_dev_hold(in_dev);
6135c753978SThomas Graf 
6145c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
6155c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
6165c753978SThomas Graf 
617fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
6181da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
6191da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
6201da177e4SLinus Torvalds 	ifa->ifa_flags = ifm->ifa_flags;
6211da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
6221da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
6235c753978SThomas Graf 
624a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
625a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
6265c753978SThomas Graf 
6275c753978SThomas Graf 	if (tb[IFA_BROADCAST])
628a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
6295c753978SThomas Graf 
6305c753978SThomas Graf 	if (tb[IFA_LABEL])
6315c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
6321da177e4SLinus Torvalds 	else
6331da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
6341da177e4SLinus Torvalds 
6355c753978SThomas Graf 	return ifa;
6365c753978SThomas Graf 
6375c753978SThomas Graf errout:
6385c753978SThomas Graf 	return ERR_PTR(err);
6395c753978SThomas Graf }
6405c753978SThomas Graf 
6415c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
6425c753978SThomas Graf {
6433b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6445c753978SThomas Graf 	struct in_ifaddr *ifa;
6455c753978SThomas Graf 
6465c753978SThomas Graf 	ASSERT_RTNL();
6475c753978SThomas Graf 
6484b8aa9abSDenis V. Lunev 	ifa = rtm_to_ifaddr(net, nlh);
6495c753978SThomas Graf 	if (IS_ERR(ifa))
6505c753978SThomas Graf 		return PTR_ERR(ifa);
6515c753978SThomas Graf 
652*15e47304SEric W. Biederman 	return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds /*
6561da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
6571da177e4SLinus Torvalds  */
6581da177e4SLinus Torvalds 
65940384999SEric Dumazet static int inet_abc_len(__be32 addr)
6601da177e4SLinus Torvalds {
6611da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
6621da177e4SLinus Torvalds 
663f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
6641da177e4SLinus Torvalds 		rc = 0;
6651da177e4SLinus Torvalds 	else {
666714e85beSAl Viro 		__u32 haddr = ntohl(addr);
6671da177e4SLinus Torvalds 
668714e85beSAl Viro 		if (IN_CLASSA(haddr))
6691da177e4SLinus Torvalds 			rc = 8;
670714e85beSAl Viro 		else if (IN_CLASSB(haddr))
6711da177e4SLinus Torvalds 			rc = 16;
672714e85beSAl Viro 		else if (IN_CLASSC(haddr))
6731da177e4SLinus Torvalds 			rc = 24;
6741da177e4SLinus Torvalds 	}
6751da177e4SLinus Torvalds 
6761da177e4SLinus Torvalds 	return rc;
6771da177e4SLinus Torvalds }
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds 
680e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
6811da177e4SLinus Torvalds {
6821da177e4SLinus Torvalds 	struct ifreq ifr;
6831da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
6841da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
6851da177e4SLinus Torvalds 	struct in_device *in_dev;
6861da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
6871da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
6881da177e4SLinus Torvalds 	struct net_device *dev;
6891da177e4SLinus Torvalds 	char *colon;
6901da177e4SLinus Torvalds 	int ret = -EFAULT;
6911da177e4SLinus Torvalds 	int tryaddrmatch = 0;
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds 	/*
6941da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
6951da177e4SLinus Torvalds 	 */
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
6981da177e4SLinus Torvalds 		goto out;
6991da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
7001da177e4SLinus Torvalds 
7011da177e4SLinus Torvalds 	/* save original address for comparison */
7021da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
7051da177e4SLinus Torvalds 	if (colon)
7061da177e4SLinus Torvalds 		*colon = 0;
7071da177e4SLinus Torvalds 
708e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
7091da177e4SLinus Torvalds 
7101da177e4SLinus Torvalds 	switch (cmd) {
7111da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7121da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7131da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7141da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
7151da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
7161da177e4SLinus Torvalds 		   so that we do not impose a lock.
7171da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
7181da177e4SLinus Torvalds 		 */
7191da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
7201da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
7211da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
7221da177e4SLinus Torvalds 		break;
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
7251da177e4SLinus Torvalds 		ret = -EACCES;
7261da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
7271da177e4SLinus Torvalds 			goto out;
7281da177e4SLinus Torvalds 		break;
7291da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
7301da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
7311da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
7321da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
7331da177e4SLinus Torvalds 		ret = -EACCES;
7341da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
7351da177e4SLinus Torvalds 			goto out;
7361da177e4SLinus Torvalds 		ret = -EINVAL;
7371da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
7381da177e4SLinus Torvalds 			goto out;
7391da177e4SLinus Torvalds 		break;
7401da177e4SLinus Torvalds 	default:
7411da177e4SLinus Torvalds 		ret = -EINVAL;
7421da177e4SLinus Torvalds 		goto out;
7431da177e4SLinus Torvalds 	}
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	rtnl_lock();
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds 	ret = -ENODEV;
7489f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
7499f9354b9SEric Dumazet 	if (!dev)
7501da177e4SLinus Torvalds 		goto done;
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	if (colon)
7531da177e4SLinus Torvalds 		*colon = ':';
7541da177e4SLinus Torvalds 
7559f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
7569f9354b9SEric Dumazet 	if (in_dev) {
7571da177e4SLinus Torvalds 		if (tryaddrmatch) {
7581da177e4SLinus Torvalds 			/* Matthias Andree */
7591da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
7601da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
7611da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
7621da177e4SLinus Torvalds 			   This is checked above. */
7631da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7641da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
7651da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
7661da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
7676c91afe1SDavid S. Miller 							ifa->ifa_local) {
7681da177e4SLinus Torvalds 					break; /* found */
7691da177e4SLinus Torvalds 				}
7701da177e4SLinus Torvalds 			}
7711da177e4SLinus Torvalds 		}
7721da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
7731da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
7741da177e4SLinus Torvalds 		   comparing just the label */
7751da177e4SLinus Torvalds 		if (!ifa) {
7761da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7771da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
7781da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
7791da177e4SLinus Torvalds 					break;
7801da177e4SLinus Torvalds 		}
7811da177e4SLinus Torvalds 	}
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
7841da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
7851da177e4SLinus Torvalds 		goto done;
7861da177e4SLinus Torvalds 
7871da177e4SLinus Torvalds 	switch (cmd) {
7881da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7891da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
7901da177e4SLinus Torvalds 		goto rarok;
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7931da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
7941da177e4SLinus Torvalds 		goto rarok;
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7971da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
7981da177e4SLinus Torvalds 		goto rarok;
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
8011da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
8021da177e4SLinus Torvalds 		goto rarok;
8031da177e4SLinus Torvalds 
8041da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
8051da177e4SLinus Torvalds 		if (colon) {
8061da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
8071da177e4SLinus Torvalds 			if (!ifa)
8081da177e4SLinus Torvalds 				break;
8091da177e4SLinus Torvalds 			ret = 0;
8101da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
8111da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
8121da177e4SLinus Torvalds 			break;
8131da177e4SLinus Torvalds 		}
8141da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
8151da177e4SLinus Torvalds 		break;
8161da177e4SLinus Torvalds 
8171da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
8181da177e4SLinus Torvalds 		ret = -EINVAL;
8191da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8201da177e4SLinus Torvalds 			break;
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds 		if (!ifa) {
8231da177e4SLinus Torvalds 			ret = -ENOBUFS;
8249f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
825fd23c3b3SDavid S. Miller 			INIT_HLIST_NODE(&ifa->hash);
8269f9354b9SEric Dumazet 			if (!ifa)
8271da177e4SLinus Torvalds 				break;
8281da177e4SLinus Torvalds 			if (colon)
8291da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
8301da177e4SLinus Torvalds 			else
8311da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8321da177e4SLinus Torvalds 		} else {
8331da177e4SLinus Torvalds 			ret = 0;
8341da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
8351da177e4SLinus Torvalds 				break;
8361da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8371da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
838148f9729SBjorn Mork 			ifa->ifa_scope = 0;
8391da177e4SLinus Torvalds 		}
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
8421da177e4SLinus Torvalds 
8431da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
8441da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
8451da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
8461da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
8471da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
8481da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
8491da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
8501da177e4SLinus Torvalds 		} else {
8511da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
8521da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
8531da177e4SLinus Torvalds 		}
8541da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
8551da177e4SLinus Torvalds 		break;
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
8581da177e4SLinus Torvalds 		ret = 0;
8591da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
8601da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8611da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
8621da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
8631da177e4SLinus Torvalds 		}
8641da177e4SLinus Torvalds 		break;
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
8671da177e4SLinus Torvalds 		ret = 0;
8681da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
8691da177e4SLinus Torvalds 			break;
8701da177e4SLinus Torvalds 		ret = -EINVAL;
8711da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8721da177e4SLinus Torvalds 			break;
8731da177e4SLinus Torvalds 		ret = 0;
8741da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
8751da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
8761da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
8771da177e4SLinus Torvalds 		break;
8781da177e4SLinus Torvalds 
8791da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
8801da177e4SLinus Torvalds 
8811da177e4SLinus Torvalds 		/*
8821da177e4SLinus Torvalds 		 *	The mask we set must be legal.
8831da177e4SLinus Torvalds 		 */
8841da177e4SLinus Torvalds 		ret = -EINVAL;
8851da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
8861da177e4SLinus Torvalds 			break;
8871da177e4SLinus Torvalds 		ret = 0;
8881da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
889a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
8901da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8911da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
8921da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 			/* See if current broadcast address matches
8951da177e4SLinus Torvalds 			 * with current netmask, then recalculate
8961da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
8971da177e4SLinus Torvalds 			 * funny address, so don't touch it since
8981da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
8991da177e4SLinus Torvalds 			 */
9001da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
9011da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
9021da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
903dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
9041da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
9051da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
9061da177e4SLinus Torvalds 			}
9071da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
9081da177e4SLinus Torvalds 		}
9091da177e4SLinus Torvalds 		break;
9101da177e4SLinus Torvalds 	}
9111da177e4SLinus Torvalds done:
9121da177e4SLinus Torvalds 	rtnl_unlock();
9131da177e4SLinus Torvalds out:
9141da177e4SLinus Torvalds 	return ret;
9151da177e4SLinus Torvalds rarok:
9161da177e4SLinus Torvalds 	rtnl_unlock();
9171da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
9181da177e4SLinus Torvalds 	goto out;
9191da177e4SLinus Torvalds }
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
9221da177e4SLinus Torvalds {
923e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
9241da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
9251da177e4SLinus Torvalds 	struct ifreq ifr;
9261da177e4SLinus Torvalds 	int done = 0;
9271da177e4SLinus Torvalds 
9289f9354b9SEric Dumazet 	if (!in_dev)
9291da177e4SLinus Torvalds 		goto out;
9301da177e4SLinus Torvalds 
9319f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
9321da177e4SLinus Torvalds 		if (!buf) {
9331da177e4SLinus Torvalds 			done += sizeof(ifr);
9341da177e4SLinus Torvalds 			continue;
9351da177e4SLinus Torvalds 		}
9361da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
9371da177e4SLinus Torvalds 			break;
9381da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
9391da177e4SLinus Torvalds 		if (ifa->ifa_label)
9401da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, ifa->ifa_label);
9411da177e4SLinus Torvalds 		else
9421da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, dev->name);
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
9451da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
9461da177e4SLinus Torvalds 								ifa->ifa_local;
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
9491da177e4SLinus Torvalds 			done = -EFAULT;
9501da177e4SLinus Torvalds 			break;
9511da177e4SLinus Torvalds 		}
9521da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
9531da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
9541da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
9551da177e4SLinus Torvalds 	}
9561da177e4SLinus Torvalds out:
9571da177e4SLinus Torvalds 	return done;
9581da177e4SLinus Torvalds }
9591da177e4SLinus Torvalds 
960a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
9611da177e4SLinus Torvalds {
962a61ced5dSAl Viro 	__be32 addr = 0;
9631da177e4SLinus Torvalds 	struct in_device *in_dev;
964c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds 	rcu_read_lock();
967e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
9681da177e4SLinus Torvalds 	if (!in_dev)
9691da177e4SLinus Torvalds 		goto no_in_dev;
9701da177e4SLinus Torvalds 
9711da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
9721da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
9731da177e4SLinus Torvalds 			continue;
9741da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
9751da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9761da177e4SLinus Torvalds 			break;
9771da177e4SLinus Torvalds 		}
9781da177e4SLinus Torvalds 		if (!addr)
9791da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9801da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 	if (addr)
983c6d14c84SEric Dumazet 		goto out_unlock;
9849f9354b9SEric Dumazet no_in_dev:
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
9871da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
9881da177e4SLinus Torvalds 	   in dev_base list.
9891da177e4SLinus Torvalds 	 */
990c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
9919f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
9929f9354b9SEric Dumazet 		if (!in_dev)
9931da177e4SLinus Torvalds 			continue;
9941da177e4SLinus Torvalds 
9951da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
9961da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
9971da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
9981da177e4SLinus Torvalds 				addr = ifa->ifa_local;
999c6d14c84SEric Dumazet 				goto out_unlock;
10001da177e4SLinus Torvalds 			}
10011da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
10021da177e4SLinus Torvalds 	}
1003c6d14c84SEric Dumazet out_unlock:
10041da177e4SLinus Torvalds 	rcu_read_unlock();
10051da177e4SLinus Torvalds 	return addr;
10061da177e4SLinus Torvalds }
10079f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
10081da177e4SLinus Torvalds 
100960cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
101060cad5daSAl Viro 			      __be32 local, int scope)
10111da177e4SLinus Torvalds {
10121da177e4SLinus Torvalds 	int same = 0;
1013a144ea4bSAl Viro 	__be32 addr = 0;
10141da177e4SLinus Torvalds 
10151da177e4SLinus Torvalds 	for_ifa(in_dev) {
10161da177e4SLinus Torvalds 		if (!addr &&
10171da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
10181da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
10191da177e4SLinus Torvalds 			addr = ifa->ifa_local;
10201da177e4SLinus Torvalds 			if (same)
10211da177e4SLinus Torvalds 				break;
10221da177e4SLinus Torvalds 		}
10231da177e4SLinus Torvalds 		if (!same) {
10241da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
10251da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
10261da177e4SLinus Torvalds 			if (same && addr) {
10271da177e4SLinus Torvalds 				if (local || !dst)
10281da177e4SLinus Torvalds 					break;
10291da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
10301da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
10311da177e4SLinus Torvalds 					break;
10321da177e4SLinus Torvalds 				/* No, then can we use new local src? */
10331da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
10341da177e4SLinus Torvalds 					addr = ifa->ifa_local;
10351da177e4SLinus Torvalds 					break;
10361da177e4SLinus Torvalds 				}
10371da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
10381da177e4SLinus Torvalds 				same = 0;
10391da177e4SLinus Torvalds 			}
10401da177e4SLinus Torvalds 		}
10411da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
10421da177e4SLinus Torvalds 
10431da177e4SLinus Torvalds 	return same ? addr : 0;
10441da177e4SLinus Torvalds }
10451da177e4SLinus Torvalds 
10461da177e4SLinus Torvalds /*
10471da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
10489bd85e32SDenis V. Lunev  * - in_dev: only on this interface, 0=any interface
10491da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
10501da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
10511da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
10521da177e4SLinus Torvalds  */
10539bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev,
10549bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
10551da177e4SLinus Torvalds {
105660cad5daSAl Viro 	__be32 addr = 0;
10579bd85e32SDenis V. Lunev 	struct net_device *dev;
105839a6d063SDenis V. Lunev 	struct net *net;
10591da177e4SLinus Torvalds 
106039a6d063SDenis V. Lunev 	if (scope != RT_SCOPE_LINK)
10619bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
10621da177e4SLinus Torvalds 
1063c346dca1SYOSHIFUJI Hideaki 	net = dev_net(in_dev->dev);
10641da177e4SLinus Torvalds 	rcu_read_lock();
1065c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
10669f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
10679f9354b9SEric Dumazet 		if (in_dev) {
10681da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
10691da177e4SLinus Torvalds 			if (addr)
10701da177e4SLinus Torvalds 				break;
10711da177e4SLinus Torvalds 		}
10721da177e4SLinus Torvalds 	}
10731da177e4SLinus Torvalds 	rcu_read_unlock();
10741da177e4SLinus Torvalds 
10751da177e4SLinus Torvalds 	return addr;
10761da177e4SLinus Torvalds }
1077eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds /*
10801da177e4SLinus Torvalds  *	Device notifier
10811da177e4SLinus Torvalds  */
10821da177e4SLinus Torvalds 
10831da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
10841da177e4SLinus Torvalds {
1085e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
10861da177e4SLinus Torvalds }
10879f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
10881da177e4SLinus Torvalds 
10891da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
10901da177e4SLinus Torvalds {
1091e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
10921da177e4SLinus Torvalds }
10939f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
10941da177e4SLinus Torvalds 
10959f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
10969f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
10971da177e4SLinus Torvalds */
10981da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
10991da177e4SLinus Torvalds {
11001da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
11011da177e4SLinus Torvalds 	int named = 0;
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
11041da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
11051da177e4SLinus Torvalds 
11061da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
11071da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11081da177e4SLinus Torvalds 		if (named++ == 0)
1109573bf470SThomas Graf 			goto skip;
111044344b2aSMark McLoughlin 		dot = strchr(old, ':');
11111da177e4SLinus Torvalds 		if (dot == NULL) {
11121da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
11131da177e4SLinus Torvalds 			dot = old;
11141da177e4SLinus Torvalds 		}
11159f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
11161da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
11179f9354b9SEric Dumazet 		else
11181da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1119573bf470SThomas Graf skip:
1120573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
11211da177e4SLinus Torvalds 	}
11221da177e4SLinus Torvalds }
11231da177e4SLinus Torvalds 
112440384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
112506770843SBreno Leitao {
112606770843SBreno Leitao 	return mtu >= 68;
112706770843SBreno Leitao }
112806770843SBreno Leitao 
1129d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1130d11327adSIan Campbell 					struct in_device *in_dev)
1131d11327adSIan Campbell 
1132d11327adSIan Campbell {
1133b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1134d11327adSIan Campbell 
1135b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1136b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1137d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
11386c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
11396c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1140d11327adSIan Campbell 			 dev->dev_addr, NULL);
1141d11327adSIan Campbell 	}
1142b76d0789SZoltan Kiss }
1143d11327adSIan Campbell 
11441da177e4SLinus Torvalds /* Called only under RTNL semaphore */
11451da177e4SLinus Torvalds 
11461da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
11471da177e4SLinus Torvalds 			 void *ptr)
11481da177e4SLinus Torvalds {
11491da177e4SLinus Torvalds 	struct net_device *dev = ptr;
1150748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	ASSERT_RTNL();
11531da177e4SLinus Torvalds 
11541da177e4SLinus Torvalds 	if (!in_dev) {
11558030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
11561da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
11578d76527eSHerbert Xu 			if (!in_dev)
1158b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
11590cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
116042f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
116142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
11621da177e4SLinus Torvalds 			}
116306770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
116406770843SBreno Leitao 			/* Re-enabling IP */
116506770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
116606770843SBreno Leitao 				in_dev = inetdev_init(dev);
11678030f544SHerbert Xu 		}
11681da177e4SLinus Torvalds 		goto out;
11691da177e4SLinus Torvalds 	}
11701da177e4SLinus Torvalds 
11711da177e4SLinus Torvalds 	switch (event) {
11721da177e4SLinus Torvalds 	case NETDEV_REGISTER:
117391df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1174a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
11751da177e4SLinus Torvalds 		break;
11761da177e4SLinus Torvalds 	case NETDEV_UP:
117706770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
11781da177e4SLinus Torvalds 			break;
11790cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
11809f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
11819f9354b9SEric Dumazet 
11829f9354b9SEric Dumazet 			if (ifa) {
1183fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
11841da177e4SLinus Torvalds 				ifa->ifa_local =
11851da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
11861da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
11871da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
11881da177e4SLinus Torvalds 				in_dev_hold(in_dev);
11891da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
11901da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
11911da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11921da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
11931da177e4SLinus Torvalds 			}
11941da177e4SLinus Torvalds 		}
11951da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1196eefef1cfSStephen Hemminger 		/* fall through */
1197eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1198d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1199d11327adSIan Campbell 			break;
1200d11327adSIan Campbell 		/* fall through */
1201d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1202a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1203d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
12041da177e4SLinus Torvalds 		break;
12051da177e4SLinus Torvalds 	case NETDEV_DOWN:
12061da177e4SLinus Torvalds 		ip_mc_down(in_dev);
12071da177e4SLinus Torvalds 		break;
120893d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
120975c78500SMoni Shoua 		ip_mc_unmap(in_dev);
121075c78500SMoni Shoua 		break;
121193d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
121275c78500SMoni Shoua 		ip_mc_remap(in_dev);
121375c78500SMoni Shoua 		break;
12141da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
121506770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
12161da177e4SLinus Torvalds 			break;
121706770843SBreno Leitao 		/* disable IP when MTU is not enough */
12181da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
12191da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
12201da177e4SLinus Torvalds 		break;
12211da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
12221da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
12231da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
12241da177e4SLinus Torvalds 		 */
12251da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
12261da177e4SLinus Torvalds 
122751602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
122866f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
12291da177e4SLinus Torvalds 		break;
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds out:
12321da177e4SLinus Torvalds 	return NOTIFY_DONE;
12331da177e4SLinus Torvalds }
12341da177e4SLinus Torvalds 
12351da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
12361da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
12371da177e4SLinus Torvalds };
12381da177e4SLinus Torvalds 
123940384999SEric Dumazet static size_t inet_nlmsg_size(void)
1240339bf98fSThomas Graf {
1241339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1242339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1243339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1244339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1245339bf98fSThomas Graf 	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1246339bf98fSThomas Graf }
1247339bf98fSThomas Graf 
12481da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1249*15e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
12501da177e4SLinus Torvalds {
12511da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
12521da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
12531da177e4SLinus Torvalds 
1254*15e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
125547f68512SThomas Graf 	if (nlh == NULL)
125626932566SPatrick McHardy 		return -EMSGSIZE;
125747f68512SThomas Graf 
125847f68512SThomas Graf 	ifm = nlmsg_data(nlh);
12591da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
12601da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
12611da177e4SLinus Torvalds 	ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
12621da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
12631da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
12641da177e4SLinus Torvalds 
1265f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1266f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1267f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1268f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) ||
1269f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1270f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1271f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
1272f3756b79SDavid S. Miller 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)))
1273f3756b79SDavid S. Miller 		goto nla_put_failure;
127447f68512SThomas Graf 
127547f68512SThomas Graf 	return nlmsg_end(skb, nlh);
127647f68512SThomas Graf 
127747f68512SThomas Graf nla_put_failure:
127826932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
127926932566SPatrick McHardy 	return -EMSGSIZE;
12801da177e4SLinus Torvalds }
12811da177e4SLinus Torvalds 
12821da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
12831da177e4SLinus Torvalds {
12843b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1285eec4df98SEric Dumazet 	int h, s_h;
1286eec4df98SEric Dumazet 	int idx, s_idx;
1287eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
12881da177e4SLinus Torvalds 	struct net_device *dev;
12891da177e4SLinus Torvalds 	struct in_device *in_dev;
12901da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1291eec4df98SEric Dumazet 	struct hlist_head *head;
1292eec4df98SEric Dumazet 	struct hlist_node *node;
12931da177e4SLinus Torvalds 
1294eec4df98SEric Dumazet 	s_h = cb->args[0];
1295eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1296eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1297eec4df98SEric Dumazet 
1298eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
12997562f876SPavel Emelianov 		idx = 0;
1300eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1301eec4df98SEric Dumazet 		rcu_read_lock();
1302eec4df98SEric Dumazet 		hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
13031da177e4SLinus Torvalds 			if (idx < s_idx)
13047562f876SPavel Emelianov 				goto cont;
13054b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
13061da177e4SLinus Torvalds 				s_ip_idx = 0;
1307eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
13089f9354b9SEric Dumazet 			if (!in_dev)
13097562f876SPavel Emelianov 				goto cont;
13101da177e4SLinus Torvalds 
13111da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
13121da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
13131da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1314596e4150SStephen Hemminger 					continue;
1315eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
1316*15e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
13171da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1318eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1319eec4df98SEric Dumazet 					rcu_read_unlock();
13201da177e4SLinus Torvalds 					goto done;
13211da177e4SLinus Torvalds 				}
1322eec4df98SEric Dumazet 			}
13237562f876SPavel Emelianov cont:
13247562f876SPavel Emelianov 			idx++;
13251da177e4SLinus Torvalds 		}
1326eec4df98SEric Dumazet 		rcu_read_unlock();
1327eec4df98SEric Dumazet 	}
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds done:
1330eec4df98SEric Dumazet 	cb->args[0] = h;
1331eec4df98SEric Dumazet 	cb->args[1] = idx;
1332eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds 	return skb->len;
13351da177e4SLinus Torvalds }
13361da177e4SLinus Torvalds 
1337d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
1338*15e47304SEric W. Biederman 		      u32 portid)
13391da177e4SLinus Torvalds {
134047f68512SThomas Graf 	struct sk_buff *skb;
1341d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1342d6062cbbSThomas Graf 	int err = -ENOBUFS;
13434b8aa9abSDenis V. Lunev 	struct net *net;
13441da177e4SLinus Torvalds 
1345c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1346339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
134747f68512SThomas Graf 	if (skb == NULL)
1348d6062cbbSThomas Graf 		goto errout;
1349d6062cbbSThomas Graf 
1350*15e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
135126932566SPatrick McHardy 	if (err < 0) {
135226932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
135326932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
135426932566SPatrick McHardy 		kfree_skb(skb);
135526932566SPatrick McHardy 		goto errout;
135626932566SPatrick McHardy 	}
1357*15e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
13581ce85fe4SPablo Neira Ayuso 	return;
1359d6062cbbSThomas Graf errout:
1360d6062cbbSThomas Graf 	if (err < 0)
13614b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
13621da177e4SLinus Torvalds }
13631da177e4SLinus Torvalds 
13649f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
13659f0f7272SThomas Graf {
13661fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
13679f0f7272SThomas Graf 
13689f0f7272SThomas Graf 	if (!in_dev)
13699f0f7272SThomas Graf 		return 0;
13709f0f7272SThomas Graf 
13719f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
13729f0f7272SThomas Graf }
13739f0f7272SThomas Graf 
13749f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
13759f0f7272SThomas Graf {
13761fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
13779f0f7272SThomas Graf 	struct nlattr *nla;
13789f0f7272SThomas Graf 	int i;
13799f0f7272SThomas Graf 
13809f0f7272SThomas Graf 	if (!in_dev)
13819f0f7272SThomas Graf 		return -ENODATA;
13829f0f7272SThomas Graf 
13839f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
13849f0f7272SThomas Graf 	if (nla == NULL)
13859f0f7272SThomas Graf 		return -EMSGSIZE;
13869f0f7272SThomas Graf 
13879f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
13889f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
13899f0f7272SThomas Graf 
13909f0f7272SThomas Graf 	return 0;
13919f0f7272SThomas Graf }
13929f0f7272SThomas Graf 
13939f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
13949f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
13959f0f7272SThomas Graf };
13969f0f7272SThomas Graf 
1397cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1398cf7afbfeSThomas Graf 				 const struct nlattr *nla)
13999f0f7272SThomas Graf {
14009f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
14019f0f7272SThomas Graf 	int err, rem;
14029f0f7272SThomas Graf 
1403f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1404cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
14059f0f7272SThomas Graf 
14069f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
14079f0f7272SThomas Graf 	if (err < 0)
14089f0f7272SThomas Graf 		return err;
14099f0f7272SThomas Graf 
14109f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14119f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
14129f0f7272SThomas Graf 			int cfgid = nla_type(a);
14139f0f7272SThomas Graf 
14149f0f7272SThomas Graf 			if (nla_len(a) < 4)
14159f0f7272SThomas Graf 				return -EINVAL;
14169f0f7272SThomas Graf 
14179f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
14189f0f7272SThomas Graf 				return -EINVAL;
14199f0f7272SThomas Graf 		}
14209f0f7272SThomas Graf 	}
14219f0f7272SThomas Graf 
1422cf7afbfeSThomas Graf 	return 0;
1423cf7afbfeSThomas Graf }
1424cf7afbfeSThomas Graf 
1425cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1426cf7afbfeSThomas Graf {
1427f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1428cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1429cf7afbfeSThomas Graf 	int rem;
1430cf7afbfeSThomas Graf 
1431cf7afbfeSThomas Graf 	if (!in_dev)
1432cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1433cf7afbfeSThomas Graf 
1434cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1435cf7afbfeSThomas Graf 		BUG();
1436cf7afbfeSThomas Graf 
14379f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14389f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
14399f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
14409f0f7272SThomas Graf 	}
14419f0f7272SThomas Graf 
14429f0f7272SThomas Graf 	return 0;
14439f0f7272SThomas Graf }
14449f0f7272SThomas Graf 
14451da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
14461da177e4SLinus Torvalds 
1447c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
144831be3085SHerbert Xu {
144931be3085SHerbert Xu 	struct net_device *dev;
145031be3085SHerbert Xu 
145131be3085SHerbert Xu 	rcu_read_lock();
1452c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1453c6d14c84SEric Dumazet 		struct in_device *in_dev;
1454c6d14c84SEric Dumazet 
145531be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
145631be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
14579355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1458c6d14c84SEric Dumazet 	}
145931be3085SHerbert Xu 	rcu_read_unlock();
146031be3085SHerbert Xu }
146131be3085SHerbert Xu 
1462c6d14c84SEric Dumazet /* called with RTNL locked */
1463c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
146468dd299bSPavel Emelyanov {
146568dd299bSPavel Emelyanov 	struct net_device *dev;
1466586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
146768dd299bSPavel Emelyanov 
1468586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
14699355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
147068dd299bSPavel Emelyanov 
1471c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
147268dd299bSPavel Emelyanov 		struct in_device *in_dev;
14730187bdfbSBen Hutchings 		if (on)
14740187bdfbSBen Hutchings 			dev_disable_lro(dev);
147568dd299bSPavel Emelyanov 		rcu_read_lock();
147668dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
147768dd299bSPavel Emelyanov 		if (in_dev)
147868dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
147968dd299bSPavel Emelyanov 		rcu_read_unlock();
148068dd299bSPavel Emelyanov 	}
148168dd299bSPavel Emelyanov }
148268dd299bSPavel Emelyanov 
148331be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write,
14848d65af78SAlexey Dobriyan 			     void __user *buffer,
148531be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
148631be3085SHerbert Xu {
1487d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
14888d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
1489d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
149031be3085SHerbert Xu 
149131be3085SHerbert Xu 	if (write) {
149231be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1493c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
149431be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
149531be3085SHerbert Xu 
149631be3085SHerbert Xu 		set_bit(i, cnf->state);
149731be3085SHerbert Xu 
14989355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1499c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
1500d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
1501d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
1502d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
15034ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
150431be3085SHerbert Xu 	}
150531be3085SHerbert Xu 
150631be3085SHerbert Xu 	return ret;
150731be3085SHerbert Xu }
150831be3085SHerbert Xu 
15091da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write,
15108d65af78SAlexey Dobriyan 				  void __user *buffer,
15111da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
15121da177e4SLinus Torvalds {
15131da177e4SLinus Torvalds 	int *valp = ctl->data;
15141da177e4SLinus Torvalds 	int val = *valp;
151588af182eSEric W. Biederman 	loff_t pos = *ppos;
15168d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
15171da177e4SLinus Torvalds 
15181da177e4SLinus Torvalds 	if (write && *valp != val) {
1519c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
1520c0ce9fb3SPavel Emelyanov 
15210187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
152288af182eSEric W. Biederman 			if (!rtnl_trylock()) {
152388af182eSEric W. Biederman 				/* Restore the original values before restarting */
152488af182eSEric W. Biederman 				*valp = val;
152588af182eSEric W. Biederman 				*ppos = pos;
15269b8adb5eSEric W. Biederman 				return restart_syscall();
152788af182eSEric W. Biederman 			}
15280187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1529c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
15300187bdfbSBen Hutchings 			} else if (*valp) {
15310187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
15320187bdfbSBen Hutchings 				struct in_device *idev =
15330187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
15340187bdfbSBen Hutchings 				dev_disable_lro(idev->dev);
15350187bdfbSBen Hutchings 			}
15360187bdfbSBen Hutchings 			rtnl_unlock();
15374ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
15381da177e4SLinus Torvalds 		}
15390187bdfbSBen Hutchings 	}
15401da177e4SLinus Torvalds 
15411da177e4SLinus Torvalds 	return ret;
15421da177e4SLinus Torvalds }
15431da177e4SLinus Torvalds 
1544323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write,
15458d65af78SAlexey Dobriyan 				void __user *buffer,
15461da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
15471da177e4SLinus Torvalds {
15481da177e4SLinus Torvalds 	int *valp = ctl->data;
15491da177e4SLinus Torvalds 	int val = *valp;
15508d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
155176e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
15521da177e4SLinus Torvalds 
15531da177e4SLinus Torvalds 	if (write && *valp != val)
15544ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
15551da177e4SLinus Torvalds 
15561da177e4SLinus Torvalds 	return ret;
15571da177e4SLinus Torvalds }
15581da177e4SLinus Torvalds 
1559f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
156042f811b8SHerbert Xu 	{ \
156142f811b8SHerbert Xu 		.procname	= name, \
156242f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
156302291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
156442f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
156542f811b8SHerbert Xu 		.mode		= mval, \
156642f811b8SHerbert Xu 		.proc_handler	= proc, \
156731be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
156842f811b8SHerbert Xu 	}
156942f811b8SHerbert Xu 
157042f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
1571f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
157242f811b8SHerbert Xu 
157342f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
1574f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
157542f811b8SHerbert Xu 
1576f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1577f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
157842f811b8SHerbert Xu 
157942f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
1580f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
158142f811b8SHerbert Xu 
15821da177e4SLinus Torvalds static struct devinet_sysctl_table {
15831da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
158402291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
15851da177e4SLinus Torvalds } devinet_sysctl = {
15861da177e4SLinus Torvalds 	.devinet_vars = {
158742f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
1588f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
158942f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
159042f811b8SHerbert Xu 
159142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
159242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
159342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
159442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
159542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
159642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
159742f811b8SHerbert Xu 					"accept_source_route"),
15988153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
159928f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
160042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
160142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
160242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
160342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
160442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
160542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
160642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
160742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
160842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
1609eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
161065324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
161142f811b8SHerbert Xu 
161242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
161342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
161442f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
161542f811b8SHerbert Xu 					      "force_igmp_version"),
161642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
161742f811b8SHerbert Xu 					      "promote_secondaries"),
1618d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
1619d0daebc3SThomas Graf 					      "route_localnet"),
16201da177e4SLinus Torvalds 	},
16211da177e4SLinus Torvalds };
16221da177e4SLinus Torvalds 
1623ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
1624f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
16251da177e4SLinus Torvalds {
16261da177e4SLinus Torvalds 	int i;
16279fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
16288607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
1629bfada697SPavel Emelyanov 
16309fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
16311da177e4SLinus Torvalds 	if (!t)
16329fa89642SPavel Emelyanov 		goto out;
16339fa89642SPavel Emelyanov 
16341da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
16351da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
163631be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
1637c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
16381da177e4SLinus Torvalds 	}
16391da177e4SLinus Torvalds 
16408607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
16411da177e4SLinus Torvalds 
16428607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
16431da177e4SLinus Torvalds 	if (!t->sysctl_header)
16448607ddb8SEric W. Biederman 		goto free;
16451da177e4SLinus Torvalds 
16461da177e4SLinus Torvalds 	p->sysctl = t;
1647ea40b324SPavel Emelyanov 	return 0;
16481da177e4SLinus Torvalds 
16491da177e4SLinus Torvalds free:
16501da177e4SLinus Torvalds 	kfree(t);
16519fa89642SPavel Emelyanov out:
1652ea40b324SPavel Emelyanov 	return -ENOBUFS;
16531da177e4SLinus Torvalds }
16541da177e4SLinus Torvalds 
165551602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
165666f27a52SPavel Emelyanov {
165751602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
165866f27a52SPavel Emelyanov 
165951602b2aSPavel Emelyanov 	if (t == NULL)
166051602b2aSPavel Emelyanov 		return;
166151602b2aSPavel Emelyanov 
166251602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
1663ff538818SLucian Adrian Grijincu 	unregister_net_sysctl_table(t->sysctl_header);
16641da177e4SLinus Torvalds 	kfree(t);
16651da177e4SLinus Torvalds }
166651602b2aSPavel Emelyanov 
166751602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
166851602b2aSPavel Emelyanov {
166954716e3bSEric W. Biederman 	neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
1670c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
1671f8572d8fSEric W. Biederman 					&idev->cnf);
167251602b2aSPavel Emelyanov }
167351602b2aSPavel Emelyanov 
167451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
167551602b2aSPavel Emelyanov {
167651602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
167751602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
16781da177e4SLinus Torvalds }
16791da177e4SLinus Torvalds 
168068dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
168168dd299bSPavel Emelyanov 	{
168268dd299bSPavel Emelyanov 		.procname	= "ip_forward",
168368dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
168402291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
168568dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
168668dd299bSPavel Emelyanov 		.mode		= 0644,
168768dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
168868dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
1689c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
169068dd299bSPavel Emelyanov 	},
169168dd299bSPavel Emelyanov 	{ },
169268dd299bSPavel Emelyanov };
16932a75de0cSEric Dumazet #endif
169468dd299bSPavel Emelyanov 
1695752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
1696752d14dcSPavel Emelyanov {
1697752d14dcSPavel Emelyanov 	int err;
1698752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
16992a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
17002a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
1701752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
17022a75de0cSEric Dumazet #endif
1703752d14dcSPavel Emelyanov 
1704752d14dcSPavel Emelyanov 	err = -ENOMEM;
1705752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
1706752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
1707752d14dcSPavel Emelyanov 
170809ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
1709752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1710752d14dcSPavel Emelyanov 		if (all == NULL)
1711752d14dcSPavel Emelyanov 			goto err_alloc_all;
1712752d14dcSPavel Emelyanov 
1713752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1714752d14dcSPavel Emelyanov 		if (dflt == NULL)
1715752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
1716752d14dcSPavel Emelyanov 
17172a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1718752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1719752d14dcSPavel Emelyanov 		if (tbl == NULL)
1720752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
1721752d14dcSPavel Emelyanov 
172202291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
1723752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
1724752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
17252a75de0cSEric Dumazet #endif
1726752d14dcSPavel Emelyanov 	}
1727752d14dcSPavel Emelyanov 
1728752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1729f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
1730752d14dcSPavel Emelyanov 	if (err < 0)
1731752d14dcSPavel Emelyanov 		goto err_reg_all;
1732752d14dcSPavel Emelyanov 
1733f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
1734752d14dcSPavel Emelyanov 	if (err < 0)
1735752d14dcSPavel Emelyanov 		goto err_reg_dflt;
1736752d14dcSPavel Emelyanov 
1737752d14dcSPavel Emelyanov 	err = -ENOMEM;
17388607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
1739752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
1740752d14dcSPavel Emelyanov 		goto err_reg_ctl;
17412a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
1742752d14dcSPavel Emelyanov #endif
1743752d14dcSPavel Emelyanov 
1744752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
1745752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
1746752d14dcSPavel Emelyanov 	return 0;
1747752d14dcSPavel Emelyanov 
1748752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1749752d14dcSPavel Emelyanov err_reg_ctl:
1750752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
1751752d14dcSPavel Emelyanov err_reg_dflt:
1752752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
1753752d14dcSPavel Emelyanov err_reg_all:
1754752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
1755752d14dcSPavel Emelyanov 		kfree(tbl);
1756752d14dcSPavel Emelyanov err_alloc_ctl:
17572a75de0cSEric Dumazet #endif
1758752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
1759752d14dcSPavel Emelyanov 		kfree(dflt);
1760752d14dcSPavel Emelyanov err_alloc_dflt:
1761752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
1762752d14dcSPavel Emelyanov 		kfree(all);
1763752d14dcSPavel Emelyanov err_alloc_all:
1764752d14dcSPavel Emelyanov 	return err;
1765752d14dcSPavel Emelyanov }
1766752d14dcSPavel Emelyanov 
1767752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
1768752d14dcSPavel Emelyanov {
17692a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1770752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
1771752d14dcSPavel Emelyanov 
1772752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
1773752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
1774752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1775752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
1776752d14dcSPavel Emelyanov 	kfree(tbl);
17772a75de0cSEric Dumazet #endif
1778752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
1779752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
1780752d14dcSPavel Emelyanov }
1781752d14dcSPavel Emelyanov 
1782752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
1783752d14dcSPavel Emelyanov 	.init = devinet_init_net,
1784752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
1785752d14dcSPavel Emelyanov };
1786752d14dcSPavel Emelyanov 
17879f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
17889f0f7272SThomas Graf 	.family		  = AF_INET,
17899f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
17909f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
1791cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
1792cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
17939f0f7272SThomas Graf };
17949f0f7272SThomas Graf 
17951da177e4SLinus Torvalds void __init devinet_init(void)
17961da177e4SLinus Torvalds {
1797fd23c3b3SDavid S. Miller 	int i;
1798fd23c3b3SDavid S. Miller 
1799fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
1800fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
1801fd23c3b3SDavid S. Miller 
1802752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
1803752d14dcSPavel Emelyanov 
18041da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
18051da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
180663f3444fSThomas Graf 
18079f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
18089f0f7272SThomas Graf 
1809c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
1810c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
1811c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
18121da177e4SLinus Torvalds }
18131da177e4SLinus Torvalds 
1814