xref: /openbmc/linux/net/ipv4/devinet.c (revision fd23c3b3)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	NET3	IP device support routines.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
1002c30a84SJesper Juhl  * 		Authors:	Ross Biro
111da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
121da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Additional Authors:
151da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
161da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *	Changes:
191da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
201da177e4SLinus Torvalds  *					lists.
211da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
221da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
231da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
241da177e4SLinus Torvalds  *					fall back to comparing just the label
251da177e4SLinus Torvalds  *					if no match found.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <asm/uaccess.h>
301da177e4SLinus Torvalds #include <asm/system.h>
311da177e4SLinus Torvalds #include <linux/bitops.h>
324fc268d2SRandy Dunlap #include <linux/capability.h>
331da177e4SLinus Torvalds #include <linux/module.h>
341da177e4SLinus Torvalds #include <linux/types.h>
351da177e4SLinus Torvalds #include <linux/kernel.h>
361da177e4SLinus Torvalds #include <linux/string.h>
371da177e4SLinus Torvalds #include <linux/mm.h>
381da177e4SLinus Torvalds #include <linux/socket.h>
391da177e4SLinus Torvalds #include <linux/sockios.h>
401da177e4SLinus Torvalds #include <linux/in.h>
411da177e4SLinus Torvalds #include <linux/errno.h>
421da177e4SLinus Torvalds #include <linux/interrupt.h>
431823730fSThomas Graf #include <linux/if_addr.h>
441da177e4SLinus Torvalds #include <linux/if_ether.h>
451da177e4SLinus Torvalds #include <linux/inet.h>
461da177e4SLinus Torvalds #include <linux/netdevice.h>
471da177e4SLinus Torvalds #include <linux/etherdevice.h>
481da177e4SLinus Torvalds #include <linux/skbuff.h>
491da177e4SLinus Torvalds #include <linux/init.h>
501da177e4SLinus Torvalds #include <linux/notifier.h>
511da177e4SLinus Torvalds #include <linux/inetdevice.h>
521da177e4SLinus Torvalds #include <linux/igmp.h>
535a0e3ad6STejun Heo #include <linux/slab.h>
54fd23c3b3SDavid S. Miller #include <linux/hash.h>
551da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
561da177e4SLinus Torvalds #include <linux/sysctl.h>
571da177e4SLinus Torvalds #endif
581da177e4SLinus Torvalds #include <linux/kmod.h>
591da177e4SLinus Torvalds 
6014c85021SArnaldo Carvalho de Melo #include <net/arp.h>
611da177e4SLinus Torvalds #include <net/ip.h>
621da177e4SLinus Torvalds #include <net/route.h>
631da177e4SLinus Torvalds #include <net/ip_fib.h>
6463f3444fSThomas Graf #include <net/rtnetlink.h>
65752d14dcSPavel Emelyanov #include <net/net_namespace.h>
661da177e4SLinus Torvalds 
670027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
6842f811b8SHerbert Xu 	.data = {
6902291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7002291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7102291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7202291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
7342f811b8SHerbert Xu 	},
741da177e4SLinus Torvalds };
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
7742f811b8SHerbert Xu 	.data = {
7802291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7902291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8002291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8102291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
8342f811b8SHerbert Xu 	},
841da177e4SLinus Torvalds };
851da177e4SLinus Torvalds 
869355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
879355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
8842f811b8SHerbert Xu 
89ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
905c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
915c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
925c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
935176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
945c753978SThomas Graf };
955c753978SThomas Graf 
96fd23c3b3SDavid S. Miller /* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE
97fd23c3b3SDavid S. Miller  * value.  So if you change this define, make appropriate changes to
98fd23c3b3SDavid S. Miller  * inet_addr_hash as well.
99fd23c3b3SDavid S. Miller  */
100fd23c3b3SDavid S. Miller #define IN4_ADDR_HSIZE	256
101fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
102fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock);
103fd23c3b3SDavid S. Miller 
104fd23c3b3SDavid S. Miller static inline unsigned int inet_addr_hash(struct net *net, __be32 addr)
105fd23c3b3SDavid S. Miller {
106fd23c3b3SDavid S. Miller 	u32 val = (__force u32) addr ^ hash_ptr(net, 8);
107fd23c3b3SDavid S. Miller 
108fd23c3b3SDavid S. Miller 	return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) &
109fd23c3b3SDavid S. Miller 		(IN4_ADDR_HSIZE - 1));
110fd23c3b3SDavid S. Miller }
111fd23c3b3SDavid S. Miller 
112fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
113fd23c3b3SDavid S. Miller {
114fd23c3b3SDavid S. Miller 	unsigned int hash = inet_addr_hash(net, ifa->ifa_address);
115fd23c3b3SDavid S. Miller 
116fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
117fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
118fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
119fd23c3b3SDavid S. Miller }
120fd23c3b3SDavid S. Miller 
121fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
122fd23c3b3SDavid S. Miller {
123fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
124fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
125fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
126fd23c3b3SDavid S. Miller }
127fd23c3b3SDavid S. Miller 
128d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1291da177e4SLinus Torvalds 
130e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1311da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1321da177e4SLinus Torvalds 			 int destroy);
1331da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
13466f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev);
13551602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
13651602b2aSPavel Emelyanov #else
13751602b2aSPavel Emelyanov static inline void devinet_sysctl_register(struct in_device *idev)
13851602b2aSPavel Emelyanov {
13951602b2aSPavel Emelyanov }
14051602b2aSPavel Emelyanov static inline void devinet_sysctl_unregister(struct in_device *idev)
14151602b2aSPavel Emelyanov {
14251602b2aSPavel Emelyanov }
1431da177e4SLinus Torvalds #endif
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds /* Locks all the inet devices. */
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1481da177e4SLinus Torvalds {
14993adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
1531da177e4SLinus Torvalds {
1541da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
1551da177e4SLinus Torvalds 	if (ifa->ifa_dev)
1561da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
1571da177e4SLinus Torvalds 	kfree(ifa);
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds static inline void inet_free_ifa(struct in_ifaddr *ifa)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
1661da177e4SLinus Torvalds {
1671da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
1681da177e4SLinus Torvalds 
169547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
170547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
1711da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
1721da177e4SLinus Torvalds 	printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
1731da177e4SLinus Torvalds 	       idev, dev ? dev->name : "NIL");
1741da177e4SLinus Torvalds #endif
1751da177e4SLinus Torvalds 	dev_put(dev);
1761da177e4SLinus Torvalds 	if (!idev->dead)
1779f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
1789f9354b9SEric Dumazet 	else
1791da177e4SLinus Torvalds 		kfree(idev);
1801da177e4SLinus Torvalds }
1819f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
1821da177e4SLinus Torvalds 
18371e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
1841da177e4SLinus Torvalds {
1851da177e4SLinus Torvalds 	struct in_device *in_dev;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	ASSERT_RTNL();
1881da177e4SLinus Torvalds 
1890da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
1901da177e4SLinus Torvalds 	if (!in_dev)
1911da177e4SLinus Torvalds 		goto out;
192c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
1939355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
1941da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
1951da177e4SLinus Torvalds 	in_dev->dev = dev;
1969f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
1979f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
1981da177e4SLinus Torvalds 		goto out_kfree;
1990187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2000187bdfbSBen Hutchings 		dev_disable_lro(dev);
2011da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2021da177e4SLinus Torvalds 	dev_hold(dev);
20330c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2041da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2051da177e4SLinus Torvalds 
20666f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2071da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2081da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2091da177e4SLinus Torvalds 		ip_mc_up(in_dev);
210483479ecSJarek Poplawski 
21130c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
21230c4cf57SDavid L Stevens 	rcu_assign_pointer(dev->ip_ptr, in_dev);
213483479ecSJarek Poplawski out:
2141da177e4SLinus Torvalds 	return in_dev;
2151da177e4SLinus Torvalds out_kfree:
2161da177e4SLinus Torvalds 	kfree(in_dev);
2171da177e4SLinus Torvalds 	in_dev = NULL;
2181da177e4SLinus Torvalds 	goto out;
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2241da177e4SLinus Torvalds 	in_dev_put(idev);
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2301da177e4SLinus Torvalds 	struct net_device *dev;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 	ASSERT_RTNL();
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	dev = in_dev->dev;
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	in_dev->dead = 1;
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2411da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2421da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2431da177e4SLinus Torvalds 	}
2441da177e4SLinus Torvalds 
24595ae6b22SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, NULL);
2461da177e4SLinus Torvalds 
24751602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
2481da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
2491da177e4SLinus Torvalds 	arp_ifdown(dev);
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds 
254ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
2551da177e4SLinus Torvalds {
2561da177e4SLinus Torvalds 	rcu_read_lock();
2571da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
2581da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
2591da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
2601da177e4SLinus Torvalds 				rcu_read_unlock();
2611da177e4SLinus Torvalds 				return 1;
2621da177e4SLinus Torvalds 			}
2631da177e4SLinus Torvalds 		}
2641da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
2651da177e4SLinus Torvalds 	rcu_read_unlock();
2661da177e4SLinus Torvalds 	return 0;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
269d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
270d6062cbbSThomas Graf 			 int destroy, struct nlmsghdr *nlh, u32 pid)
2711da177e4SLinus Torvalds {
2728f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
2730ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
2740ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
2750ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
2760ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	ASSERT_RTNL();
2791da177e4SLinus Torvalds 
2808f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
2818f937c60SHarald Welte 	 * unless alias promotion is set
2828f937c60SHarald Welte 	 **/
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
2851da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
2880ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
2890ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
2900ff60a45SJamal Hadi Salim 				last_prim = ifa;
2910ff60a45SJamal Hadi Salim 
2921da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
2931da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
2941da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
2951da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
2960ff60a45SJamal Hadi Salim 				prev_prom = ifa;
2971da177e4SLinus Torvalds 				continue;
2981da177e4SLinus Torvalds 			}
2991da177e4SLinus Torvalds 
3000ff60a45SJamal Hadi Salim 			if (!do_promote) {
301fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3021da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3031da177e4SLinus Torvalds 
304d6062cbbSThomas Graf 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
305e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
306e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3071da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3088f937c60SHarald Welte 			} else {
3098f937c60SHarald Welte 				promote = ifa;
3108f937c60SHarald Welte 				break;
3118f937c60SHarald Welte 			}
3121da177e4SLinus Torvalds 		}
3131da177e4SLinus Torvalds 	}
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	/* 2. Unlink it */
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
318fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3231da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3241da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3251da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3261da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3271da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3281da177e4SLinus Torvalds 	   So that, this order is correct.
3291da177e4SLinus Torvalds 	 */
330d6062cbbSThomas Graf 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
331e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3320ff60a45SJamal Hadi Salim 
3330ff60a45SJamal Hadi Salim 	if (promote) {
3340ff60a45SJamal Hadi Salim 
3350ff60a45SJamal Hadi Salim 		if (prev_prom) {
3360ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
3370ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
3380ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
3390ff60a45SJamal Hadi Salim 		}
3400ff60a45SJamal Hadi Salim 
3410ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
342d6062cbbSThomas Graf 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
343e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
344e041c683SAlan Stern 				NETDEV_UP, promote);
3450ff60a45SJamal Hadi Salim 		for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) {
3460ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
3470ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
3480ff60a45SJamal Hadi Salim 					continue;
3490ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
3500ff60a45SJamal Hadi Salim 		}
3510ff60a45SJamal Hadi Salim 
3520ff60a45SJamal Hadi Salim 	}
3536363097cSHerbert Xu 	if (destroy)
3541da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
357d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
358d6062cbbSThomas Graf 			 int destroy)
359d6062cbbSThomas Graf {
360d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
361d6062cbbSThomas Graf }
362d6062cbbSThomas Graf 
363d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
364d6062cbbSThomas Graf 			     u32 pid)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
3671da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 	ASSERT_RTNL();
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
3721da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3731da177e4SLinus Torvalds 		return 0;
3741da177e4SLinus Torvalds 	}
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
3771da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
3801da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
3811da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
3821da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
3831da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
3841da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3851da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
3861da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
3871da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3881da177e4SLinus Torvalds 				return -EEXIST;
3891da177e4SLinus Torvalds 			}
3901da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
3911da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3921da177e4SLinus Torvalds 				return -EINVAL;
3931da177e4SLinus Torvalds 			}
3941da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
3951da177e4SLinus Torvalds 		}
3961da177e4SLinus Torvalds 	}
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
3991da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4001da177e4SLinus Torvalds 		ifap = last_primary;
4011da177e4SLinus Torvalds 	}
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4041da177e4SLinus Torvalds 	*ifap = ifa;
4051da177e4SLinus Torvalds 
406fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
407fd23c3b3SDavid S. Miller 
4081da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4091da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4101da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
411d6062cbbSThomas Graf 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
412e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 	return 0;
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds 
417d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
418d6062cbbSThomas Graf {
419d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
420d6062cbbSThomas Graf }
421d6062cbbSThomas Graf 
4221da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4231da177e4SLinus Torvalds {
424e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	ASSERT_RTNL();
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	if (!in_dev) {
4291da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4301da177e4SLinus Torvalds 		return -ENOBUFS;
4311da177e4SLinus Torvalds 	}
43271e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
4331da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
434547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
4351da177e4SLinus Torvalds 		in_dev_hold(in_dev);
4361da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
4371da177e4SLinus Torvalds 	}
438f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
4391da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
4401da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds 
4438723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
4448723e1b4SEric Dumazet  * We dont take a reference on found in_device
4458723e1b4SEric Dumazet  */
4467fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
4471da177e4SLinus Torvalds {
4481da177e4SLinus Torvalds 	struct net_device *dev;
4491da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
450c148fc2eSEric Dumazet 
451c148fc2eSEric Dumazet 	rcu_read_lock();
452c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
4531da177e4SLinus Torvalds 	if (dev)
4548723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
455c148fc2eSEric Dumazet 	rcu_read_unlock();
4561da177e4SLinus Torvalds 	return in_dev;
4571da177e4SLinus Torvalds }
4589f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
4611da177e4SLinus Torvalds 
46260cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
46360cad5daSAl Viro 				    __be32 mask)
4641da177e4SLinus Torvalds {
4651da177e4SLinus Torvalds 	ASSERT_RTNL();
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
4681da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
4691da177e4SLinus Torvalds 			return ifa;
4701da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
4711da177e4SLinus Torvalds 	return NULL;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
4751da177e4SLinus Torvalds {
4763b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
477dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
4781da177e4SLinus Torvalds 	struct in_device *in_dev;
479dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
4801da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
481dfdd5fd4SThomas Graf 	int err = -EINVAL;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	ASSERT_RTNL();
4841da177e4SLinus Torvalds 
485dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
486dfdd5fd4SThomas Graf 	if (err < 0)
487dfdd5fd4SThomas Graf 		goto errout;
488dfdd5fd4SThomas Graf 
489dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
4907fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
491dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
492dfdd5fd4SThomas Graf 		err = -ENODEV;
493dfdd5fd4SThomas Graf 		goto errout;
494dfdd5fd4SThomas Graf 	}
495dfdd5fd4SThomas Graf 
4961da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
4971da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
498dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
499a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5001da177e4SLinus Torvalds 			continue;
501dfdd5fd4SThomas Graf 
502dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
503dfdd5fd4SThomas Graf 			continue;
504dfdd5fd4SThomas Graf 
505dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
506dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
507a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
508dfdd5fd4SThomas Graf 			continue;
509dfdd5fd4SThomas Graf 
510d6062cbbSThomas Graf 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
5111da177e4SLinus Torvalds 		return 0;
5121da177e4SLinus Torvalds 	}
513dfdd5fd4SThomas Graf 
514dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
515dfdd5fd4SThomas Graf errout:
516dfdd5fd4SThomas Graf 	return err;
5171da177e4SLinus Torvalds }
5181da177e4SLinus Torvalds 
5194b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
5201da177e4SLinus Torvalds {
5215c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5225c753978SThomas Graf 	struct in_ifaddr *ifa;
5235c753978SThomas Graf 	struct ifaddrmsg *ifm;
5241da177e4SLinus Torvalds 	struct net_device *dev;
5251da177e4SLinus Torvalds 	struct in_device *in_dev;
5267b218574SDenis V. Lunev 	int err;
5271da177e4SLinus Torvalds 
5285c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
5295c753978SThomas Graf 	if (err < 0)
5305c753978SThomas Graf 		goto errout;
5311da177e4SLinus Torvalds 
5325c753978SThomas Graf 	ifm = nlmsg_data(nlh);
533c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
5347b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
5355c753978SThomas Graf 		goto errout;
5361da177e4SLinus Torvalds 
5374b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
5385c753978SThomas Graf 	err = -ENODEV;
5397b218574SDenis V. Lunev 	if (dev == NULL)
5405c753978SThomas Graf 		goto errout;
5411da177e4SLinus Torvalds 
5425c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
5435c753978SThomas Graf 	err = -ENOBUFS;
5447b218574SDenis V. Lunev 	if (in_dev == NULL)
5455c753978SThomas Graf 		goto errout;
54671e27da9SHerbert Xu 
5475c753978SThomas Graf 	ifa = inet_alloc_ifa();
5487b218574SDenis V. Lunev 	if (ifa == NULL)
5495c753978SThomas Graf 		/*
5505c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
5515c753978SThomas Graf 		 * assigned to its device and is destroy with it.
5525c753978SThomas Graf 		 */
5535c753978SThomas Graf 		goto errout;
5545c753978SThomas Graf 
555a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
5565c753978SThomas Graf 	in_dev_hold(in_dev);
5575c753978SThomas Graf 
5585c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
5595c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
5605c753978SThomas Graf 
561fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
5621da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
5631da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
5641da177e4SLinus Torvalds 	ifa->ifa_flags = ifm->ifa_flags;
5651da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
5661da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
5675c753978SThomas Graf 
568a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
569a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
5705c753978SThomas Graf 
5715c753978SThomas Graf 	if (tb[IFA_BROADCAST])
572a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
5735c753978SThomas Graf 
5745c753978SThomas Graf 	if (tb[IFA_LABEL])
5755c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
5761da177e4SLinus Torvalds 	else
5771da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
5781da177e4SLinus Torvalds 
5795c753978SThomas Graf 	return ifa;
5805c753978SThomas Graf 
5815c753978SThomas Graf errout:
5825c753978SThomas Graf 	return ERR_PTR(err);
5835c753978SThomas Graf }
5845c753978SThomas Graf 
5855c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
5865c753978SThomas Graf {
5873b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
5885c753978SThomas Graf 	struct in_ifaddr *ifa;
5895c753978SThomas Graf 
5905c753978SThomas Graf 	ASSERT_RTNL();
5915c753978SThomas Graf 
5924b8aa9abSDenis V. Lunev 	ifa = rtm_to_ifaddr(net, nlh);
5935c753978SThomas Graf 	if (IS_ERR(ifa))
5945c753978SThomas Graf 		return PTR_ERR(ifa);
5955c753978SThomas Graf 
596d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds /*
6001da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
6011da177e4SLinus Torvalds  */
6021da177e4SLinus Torvalds 
6039f9354b9SEric Dumazet static inline int inet_abc_len(__be32 addr)
6041da177e4SLinus Torvalds {
6051da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
6061da177e4SLinus Torvalds 
607f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
6081da177e4SLinus Torvalds 		rc = 0;
6091da177e4SLinus Torvalds 	else {
610714e85beSAl Viro 		__u32 haddr = ntohl(addr);
6111da177e4SLinus Torvalds 
612714e85beSAl Viro 		if (IN_CLASSA(haddr))
6131da177e4SLinus Torvalds 			rc = 8;
614714e85beSAl Viro 		else if (IN_CLASSB(haddr))
6151da177e4SLinus Torvalds 			rc = 16;
616714e85beSAl Viro 		else if (IN_CLASSC(haddr))
6171da177e4SLinus Torvalds 			rc = 24;
6181da177e4SLinus Torvalds 	}
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds 	return rc;
6211da177e4SLinus Torvalds }
6221da177e4SLinus Torvalds 
6231da177e4SLinus Torvalds 
624e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
6251da177e4SLinus Torvalds {
6261da177e4SLinus Torvalds 	struct ifreq ifr;
6271da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
6281da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
6291da177e4SLinus Torvalds 	struct in_device *in_dev;
6301da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
6311da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
6321da177e4SLinus Torvalds 	struct net_device *dev;
6331da177e4SLinus Torvalds 	char *colon;
6341da177e4SLinus Torvalds 	int ret = -EFAULT;
6351da177e4SLinus Torvalds 	int tryaddrmatch = 0;
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 	/*
6381da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
6391da177e4SLinus Torvalds 	 */
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
6421da177e4SLinus Torvalds 		goto out;
6431da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds 	/* save original address for comparison */
6461da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
6491da177e4SLinus Torvalds 	if (colon)
6501da177e4SLinus Torvalds 		*colon = 0;
6511da177e4SLinus Torvalds 
652e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds 	switch (cmd) {
6551da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
6561da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
6571da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
6581da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
6591da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
6601da177e4SLinus Torvalds 		   so that we do not impose a lock.
6611da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
6621da177e4SLinus Torvalds 		 */
6631da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
6641da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
6651da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
6661da177e4SLinus Torvalds 		break;
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
6691da177e4SLinus Torvalds 		ret = -EACCES;
6701da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
6711da177e4SLinus Torvalds 			goto out;
6721da177e4SLinus Torvalds 		break;
6731da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
6741da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
6751da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
6761da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
6771da177e4SLinus Torvalds 		ret = -EACCES;
6781da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
6791da177e4SLinus Torvalds 			goto out;
6801da177e4SLinus Torvalds 		ret = -EINVAL;
6811da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
6821da177e4SLinus Torvalds 			goto out;
6831da177e4SLinus Torvalds 		break;
6841da177e4SLinus Torvalds 	default:
6851da177e4SLinus Torvalds 		ret = -EINVAL;
6861da177e4SLinus Torvalds 		goto out;
6871da177e4SLinus Torvalds 	}
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	rtnl_lock();
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	ret = -ENODEV;
6929f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
6939f9354b9SEric Dumazet 	if (!dev)
6941da177e4SLinus Torvalds 		goto done;
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 	if (colon)
6971da177e4SLinus Torvalds 		*colon = ':';
6981da177e4SLinus Torvalds 
6999f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
7009f9354b9SEric Dumazet 	if (in_dev) {
7011da177e4SLinus Torvalds 		if (tryaddrmatch) {
7021da177e4SLinus Torvalds 			/* Matthias Andree */
7031da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
7041da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
7051da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
7061da177e4SLinus Torvalds 			   This is checked above. */
7071da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7081da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
7091da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
7101da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
7111da177e4SLinus Torvalds 							ifa->ifa_address) {
7121da177e4SLinus Torvalds 					break; /* found */
7131da177e4SLinus Torvalds 				}
7141da177e4SLinus Torvalds 			}
7151da177e4SLinus Torvalds 		}
7161da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
7171da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
7181da177e4SLinus Torvalds 		   comparing just the label */
7191da177e4SLinus Torvalds 		if (!ifa) {
7201da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7211da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
7221da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
7231da177e4SLinus Torvalds 					break;
7241da177e4SLinus Torvalds 		}
7251da177e4SLinus Torvalds 	}
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
7281da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
7291da177e4SLinus Torvalds 		goto done;
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds 	switch (cmd) {
7321da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7331da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
7341da177e4SLinus Torvalds 		goto rarok;
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7371da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
7381da177e4SLinus Torvalds 		goto rarok;
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7411da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
7421da177e4SLinus Torvalds 		goto rarok;
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
7451da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
7461da177e4SLinus Torvalds 		goto rarok;
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
7491da177e4SLinus Torvalds 		if (colon) {
7501da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
7511da177e4SLinus Torvalds 			if (!ifa)
7521da177e4SLinus Torvalds 				break;
7531da177e4SLinus Torvalds 			ret = 0;
7541da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
7551da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
7561da177e4SLinus Torvalds 			break;
7571da177e4SLinus Torvalds 		}
7581da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
7591da177e4SLinus Torvalds 		break;
7601da177e4SLinus Torvalds 
7611da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
7621da177e4SLinus Torvalds 		ret = -EINVAL;
7631da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
7641da177e4SLinus Torvalds 			break;
7651da177e4SLinus Torvalds 
7661da177e4SLinus Torvalds 		if (!ifa) {
7671da177e4SLinus Torvalds 			ret = -ENOBUFS;
7689f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
769fd23c3b3SDavid S. Miller 			INIT_HLIST_NODE(&ifa->hash);
7709f9354b9SEric Dumazet 			if (!ifa)
7711da177e4SLinus Torvalds 				break;
7721da177e4SLinus Torvalds 			if (colon)
7731da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
7741da177e4SLinus Torvalds 			else
7751da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
7761da177e4SLinus Torvalds 		} else {
7771da177e4SLinus Torvalds 			ret = 0;
7781da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
7791da177e4SLinus Torvalds 				break;
7801da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
7811da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
782148f9729SBjorn Mork 			ifa->ifa_scope = 0;
7831da177e4SLinus Torvalds 		}
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
7861da177e4SLinus Torvalds 
7871da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
7881da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
7891da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
7901da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
7911da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
7921da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
7931da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
7941da177e4SLinus Torvalds 		} else {
7951da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
7961da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
7971da177e4SLinus Torvalds 		}
7981da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
7991da177e4SLinus Torvalds 		break;
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
8021da177e4SLinus Torvalds 		ret = 0;
8031da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
8041da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8051da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
8061da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
8071da177e4SLinus Torvalds 		}
8081da177e4SLinus Torvalds 		break;
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
8111da177e4SLinus Torvalds 		ret = 0;
8121da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
8131da177e4SLinus Torvalds 			break;
8141da177e4SLinus Torvalds 		ret = -EINVAL;
8151da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8161da177e4SLinus Torvalds 			break;
8171da177e4SLinus Torvalds 		ret = 0;
8181da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
8191da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
8201da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
8211da177e4SLinus Torvalds 		break;
8221da177e4SLinus Torvalds 
8231da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
8241da177e4SLinus Torvalds 
8251da177e4SLinus Torvalds 		/*
8261da177e4SLinus Torvalds 		 *	The mask we set must be legal.
8271da177e4SLinus Torvalds 		 */
8281da177e4SLinus Torvalds 		ret = -EINVAL;
8291da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
8301da177e4SLinus Torvalds 			break;
8311da177e4SLinus Torvalds 		ret = 0;
8321da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
833a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
8341da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8351da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
8361da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
8371da177e4SLinus Torvalds 
8381da177e4SLinus Torvalds 			/* See if current broadcast address matches
8391da177e4SLinus Torvalds 			 * with current netmask, then recalculate
8401da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
8411da177e4SLinus Torvalds 			 * funny address, so don't touch it since
8421da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
8431da177e4SLinus Torvalds 			 */
8441da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
8451da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
8461da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
847dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
8481da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
8491da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
8501da177e4SLinus Torvalds 			}
8511da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
8521da177e4SLinus Torvalds 		}
8531da177e4SLinus Torvalds 		break;
8541da177e4SLinus Torvalds 	}
8551da177e4SLinus Torvalds done:
8561da177e4SLinus Torvalds 	rtnl_unlock();
8571da177e4SLinus Torvalds out:
8581da177e4SLinus Torvalds 	return ret;
8591da177e4SLinus Torvalds rarok:
8601da177e4SLinus Torvalds 	rtnl_unlock();
8611da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
8621da177e4SLinus Torvalds 	goto out;
8631da177e4SLinus Torvalds }
8641da177e4SLinus Torvalds 
8651da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
8661da177e4SLinus Torvalds {
867e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
8681da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
8691da177e4SLinus Torvalds 	struct ifreq ifr;
8701da177e4SLinus Torvalds 	int done = 0;
8711da177e4SLinus Torvalds 
8729f9354b9SEric Dumazet 	if (!in_dev)
8731da177e4SLinus Torvalds 		goto out;
8741da177e4SLinus Torvalds 
8759f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
8761da177e4SLinus Torvalds 		if (!buf) {
8771da177e4SLinus Torvalds 			done += sizeof(ifr);
8781da177e4SLinus Torvalds 			continue;
8791da177e4SLinus Torvalds 		}
8801da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
8811da177e4SLinus Torvalds 			break;
8821da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
8831da177e4SLinus Torvalds 		if (ifa->ifa_label)
8841da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, ifa->ifa_label);
8851da177e4SLinus Torvalds 		else
8861da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, dev->name);
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
8891da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
8901da177e4SLinus Torvalds 								ifa->ifa_local;
8911da177e4SLinus Torvalds 
8921da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
8931da177e4SLinus Torvalds 			done = -EFAULT;
8941da177e4SLinus Torvalds 			break;
8951da177e4SLinus Torvalds 		}
8961da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
8971da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
8981da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
8991da177e4SLinus Torvalds 	}
9001da177e4SLinus Torvalds out:
9011da177e4SLinus Torvalds 	return done;
9021da177e4SLinus Torvalds }
9031da177e4SLinus Torvalds 
904a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
9051da177e4SLinus Torvalds {
906a61ced5dSAl Viro 	__be32 addr = 0;
9071da177e4SLinus Torvalds 	struct in_device *in_dev;
908c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
9091da177e4SLinus Torvalds 
9101da177e4SLinus Torvalds 	rcu_read_lock();
911e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
9121da177e4SLinus Torvalds 	if (!in_dev)
9131da177e4SLinus Torvalds 		goto no_in_dev;
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
9161da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
9171da177e4SLinus Torvalds 			continue;
9181da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
9191da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9201da177e4SLinus Torvalds 			break;
9211da177e4SLinus Torvalds 		}
9221da177e4SLinus Torvalds 		if (!addr)
9231da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9241da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
9251da177e4SLinus Torvalds 
9261da177e4SLinus Torvalds 	if (addr)
927c6d14c84SEric Dumazet 		goto out_unlock;
9289f9354b9SEric Dumazet no_in_dev:
9291da177e4SLinus Torvalds 
9301da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
9311da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
9321da177e4SLinus Torvalds 	   in dev_base list.
9331da177e4SLinus Torvalds 	 */
934c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
9359f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
9369f9354b9SEric Dumazet 		if (!in_dev)
9371da177e4SLinus Torvalds 			continue;
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
9401da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
9411da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
9421da177e4SLinus Torvalds 				addr = ifa->ifa_local;
943c6d14c84SEric Dumazet 				goto out_unlock;
9441da177e4SLinus Torvalds 			}
9451da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
9461da177e4SLinus Torvalds 	}
947c6d14c84SEric Dumazet out_unlock:
9481da177e4SLinus Torvalds 	rcu_read_unlock();
9491da177e4SLinus Torvalds 	return addr;
9501da177e4SLinus Torvalds }
9519f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
9521da177e4SLinus Torvalds 
95360cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
95460cad5daSAl Viro 			      __be32 local, int scope)
9551da177e4SLinus Torvalds {
9561da177e4SLinus Torvalds 	int same = 0;
957a144ea4bSAl Viro 	__be32 addr = 0;
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds 	for_ifa(in_dev) {
9601da177e4SLinus Torvalds 		if (!addr &&
9611da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
9621da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
9631da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9641da177e4SLinus Torvalds 			if (same)
9651da177e4SLinus Torvalds 				break;
9661da177e4SLinus Torvalds 		}
9671da177e4SLinus Torvalds 		if (!same) {
9681da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
9691da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
9701da177e4SLinus Torvalds 			if (same && addr) {
9711da177e4SLinus Torvalds 				if (local || !dst)
9721da177e4SLinus Torvalds 					break;
9731da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
9741da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
9751da177e4SLinus Torvalds 					break;
9761da177e4SLinus Torvalds 				/* No, then can we use new local src? */
9771da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
9781da177e4SLinus Torvalds 					addr = ifa->ifa_local;
9791da177e4SLinus Torvalds 					break;
9801da177e4SLinus Torvalds 				}
9811da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
9821da177e4SLinus Torvalds 				same = 0;
9831da177e4SLinus Torvalds 			}
9841da177e4SLinus Torvalds 		}
9851da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
9861da177e4SLinus Torvalds 
9871da177e4SLinus Torvalds 	return same ? addr : 0;
9881da177e4SLinus Torvalds }
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds /*
9911da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
9929bd85e32SDenis V. Lunev  * - in_dev: only on this interface, 0=any interface
9931da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
9941da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
9951da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
9961da177e4SLinus Torvalds  */
9979bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev,
9989bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
9991da177e4SLinus Torvalds {
100060cad5daSAl Viro 	__be32 addr = 0;
10019bd85e32SDenis V. Lunev 	struct net_device *dev;
100239a6d063SDenis V. Lunev 	struct net *net;
10031da177e4SLinus Torvalds 
100439a6d063SDenis V. Lunev 	if (scope != RT_SCOPE_LINK)
10059bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
10061da177e4SLinus Torvalds 
1007c346dca1SYOSHIFUJI Hideaki 	net = dev_net(in_dev->dev);
10081da177e4SLinus Torvalds 	rcu_read_lock();
1009c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
10109f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
10119f9354b9SEric Dumazet 		if (in_dev) {
10121da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
10131da177e4SLinus Torvalds 			if (addr)
10141da177e4SLinus Torvalds 				break;
10151da177e4SLinus Torvalds 		}
10161da177e4SLinus Torvalds 	}
10171da177e4SLinus Torvalds 	rcu_read_unlock();
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds 	return addr;
10201da177e4SLinus Torvalds }
10211da177e4SLinus Torvalds 
10221da177e4SLinus Torvalds /*
10231da177e4SLinus Torvalds  *	Device notifier
10241da177e4SLinus Torvalds  */
10251da177e4SLinus Torvalds 
10261da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
10271da177e4SLinus Torvalds {
1028e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
10291da177e4SLinus Torvalds }
10309f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
10311da177e4SLinus Torvalds 
10321da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
10331da177e4SLinus Torvalds {
1034e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
10351da177e4SLinus Torvalds }
10369f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
10371da177e4SLinus Torvalds 
10389f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
10399f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
10401da177e4SLinus Torvalds */
10411da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
10421da177e4SLinus Torvalds {
10431da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
10441da177e4SLinus Torvalds 	int named = 0;
10451da177e4SLinus Torvalds 
10461da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
10471da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
10481da177e4SLinus Torvalds 
10491da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
10501da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
10511da177e4SLinus Torvalds 		if (named++ == 0)
1052573bf470SThomas Graf 			goto skip;
105344344b2aSMark McLoughlin 		dot = strchr(old, ':');
10541da177e4SLinus Torvalds 		if (dot == NULL) {
10551da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
10561da177e4SLinus Torvalds 			dot = old;
10571da177e4SLinus Torvalds 		}
10589f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
10591da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
10609f9354b9SEric Dumazet 		else
10611da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1062573bf470SThomas Graf skip:
1063573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
10641da177e4SLinus Torvalds 	}
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds 
106706770843SBreno Leitao static inline bool inetdev_valid_mtu(unsigned mtu)
106806770843SBreno Leitao {
106906770843SBreno Leitao 	return mtu >= 68;
107006770843SBreno Leitao }
107106770843SBreno Leitao 
10721da177e4SLinus Torvalds /* Called only under RTNL semaphore */
10731da177e4SLinus Torvalds 
10741da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
10751da177e4SLinus Torvalds 			 void *ptr)
10761da177e4SLinus Torvalds {
10771da177e4SLinus Torvalds 	struct net_device *dev = ptr;
1078e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds 	ASSERT_RTNL();
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds 	if (!in_dev) {
10838030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
10841da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
10858d76527eSHerbert Xu 			if (!in_dev)
1086b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
10870cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
108842f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
108942f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
10901da177e4SLinus Torvalds 			}
109106770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
109206770843SBreno Leitao 			/* Re-enabling IP */
109306770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
109406770843SBreno Leitao 				in_dev = inetdev_init(dev);
10958030f544SHerbert Xu 		}
10961da177e4SLinus Torvalds 		goto out;
10971da177e4SLinus Torvalds 	}
10981da177e4SLinus Torvalds 
10991da177e4SLinus Torvalds 	switch (event) {
11001da177e4SLinus Torvalds 	case NETDEV_REGISTER:
11011da177e4SLinus Torvalds 		printk(KERN_DEBUG "inetdev_event: bug\n");
110295ae6b22SEric Dumazet 		rcu_assign_pointer(dev->ip_ptr, NULL);
11031da177e4SLinus Torvalds 		break;
11041da177e4SLinus Torvalds 	case NETDEV_UP:
110506770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
11061da177e4SLinus Torvalds 			break;
11070cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
11089f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
11099f9354b9SEric Dumazet 
11109f9354b9SEric Dumazet 			if (ifa) {
1111fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
11121da177e4SLinus Torvalds 				ifa->ifa_local =
11131da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
11141da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
11151da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
11161da177e4SLinus Torvalds 				in_dev_hold(in_dev);
11171da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
11181da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
11191da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11201da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
11211da177e4SLinus Torvalds 			}
11221da177e4SLinus Torvalds 		}
11231da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1124eefef1cfSStephen Hemminger 		/* fall through */
112506c4648dSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1126eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1127a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1128a21090cfSStephen Hemminger 		if (IN_DEV_ARP_NOTIFY(in_dev)) {
1129a21090cfSStephen Hemminger 			struct in_ifaddr *ifa = in_dev->ifa_list;
1130a21090cfSStephen Hemminger 
1131a21090cfSStephen Hemminger 			if (ifa)
1132eefef1cfSStephen Hemminger 				arp_send(ARPOP_REQUEST, ETH_P_ARP,
1133a21090cfSStephen Hemminger 					 ifa->ifa_address, dev,
1134a21090cfSStephen Hemminger 					 ifa->ifa_address, NULL,
1135a21090cfSStephen Hemminger 					 dev->dev_addr, NULL);
1136a21090cfSStephen Hemminger 		}
11371da177e4SLinus Torvalds 		break;
11381da177e4SLinus Torvalds 	case NETDEV_DOWN:
11391da177e4SLinus Torvalds 		ip_mc_down(in_dev);
11401da177e4SLinus Torvalds 		break;
114193d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
114275c78500SMoni Shoua 		ip_mc_unmap(in_dev);
114375c78500SMoni Shoua 		break;
114493d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
114575c78500SMoni Shoua 		ip_mc_remap(in_dev);
114675c78500SMoni Shoua 		break;
11471da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
114806770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
11491da177e4SLinus Torvalds 			break;
115006770843SBreno Leitao 		/* disable IP when MTU is not enough */
11511da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
11521da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
11531da177e4SLinus Torvalds 		break;
11541da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
11551da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
11561da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
11571da177e4SLinus Torvalds 		 */
11581da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
11591da177e4SLinus Torvalds 
116051602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
116166f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
11621da177e4SLinus Torvalds 		break;
11631da177e4SLinus Torvalds 	}
11641da177e4SLinus Torvalds out:
11651da177e4SLinus Torvalds 	return NOTIFY_DONE;
11661da177e4SLinus Torvalds }
11671da177e4SLinus Torvalds 
11681da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
11691da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
11701da177e4SLinus Torvalds };
11711da177e4SLinus Torvalds 
1172339bf98fSThomas Graf static inline size_t inet_nlmsg_size(void)
1173339bf98fSThomas Graf {
1174339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1175339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1176339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1177339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1178339bf98fSThomas Graf 	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1179339bf98fSThomas Graf }
1180339bf98fSThomas Graf 
11811da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1182b6544c0bSJamal Hadi Salim 			    u32 pid, u32 seq, int event, unsigned int flags)
11831da177e4SLinus Torvalds {
11841da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
11851da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
11861da177e4SLinus Torvalds 
118747f68512SThomas Graf 	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
118847f68512SThomas Graf 	if (nlh == NULL)
118926932566SPatrick McHardy 		return -EMSGSIZE;
119047f68512SThomas Graf 
119147f68512SThomas Graf 	ifm = nlmsg_data(nlh);
11921da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
11931da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
11941da177e4SLinus Torvalds 	ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
11951da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
11961da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
11971da177e4SLinus Torvalds 
119847f68512SThomas Graf 	if (ifa->ifa_address)
1199a7a628c4SAl Viro 		NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address);
120047f68512SThomas Graf 
120147f68512SThomas Graf 	if (ifa->ifa_local)
1202a7a628c4SAl Viro 		NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local);
120347f68512SThomas Graf 
120447f68512SThomas Graf 	if (ifa->ifa_broadcast)
1205a7a628c4SAl Viro 		NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast);
120647f68512SThomas Graf 
120747f68512SThomas Graf 	if (ifa->ifa_label[0])
120847f68512SThomas Graf 		NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label);
120947f68512SThomas Graf 
121047f68512SThomas Graf 	return nlmsg_end(skb, nlh);
121147f68512SThomas Graf 
121247f68512SThomas Graf nla_put_failure:
121326932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
121426932566SPatrick McHardy 	return -EMSGSIZE;
12151da177e4SLinus Torvalds }
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
12181da177e4SLinus Torvalds {
12193b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1220eec4df98SEric Dumazet 	int h, s_h;
1221eec4df98SEric Dumazet 	int idx, s_idx;
1222eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
12231da177e4SLinus Torvalds 	struct net_device *dev;
12241da177e4SLinus Torvalds 	struct in_device *in_dev;
12251da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1226eec4df98SEric Dumazet 	struct hlist_head *head;
1227eec4df98SEric Dumazet 	struct hlist_node *node;
12281da177e4SLinus Torvalds 
1229eec4df98SEric Dumazet 	s_h = cb->args[0];
1230eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1231eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1232eec4df98SEric Dumazet 
1233eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
12347562f876SPavel Emelianov 		idx = 0;
1235eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1236eec4df98SEric Dumazet 		rcu_read_lock();
1237eec4df98SEric Dumazet 		hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
12381da177e4SLinus Torvalds 			if (idx < s_idx)
12397562f876SPavel Emelianov 				goto cont;
12404b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
12411da177e4SLinus Torvalds 				s_ip_idx = 0;
1242eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
12439f9354b9SEric Dumazet 			if (!in_dev)
12447562f876SPavel Emelianov 				goto cont;
12451da177e4SLinus Torvalds 
12461da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
12471da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
12481da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1249596e4150SStephen Hemminger 					continue;
1250eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
1251eec4df98SEric Dumazet 					     NETLINK_CB(cb->skb).pid,
12521da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1253eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1254eec4df98SEric Dumazet 					rcu_read_unlock();
12551da177e4SLinus Torvalds 					goto done;
12561da177e4SLinus Torvalds 				}
1257eec4df98SEric Dumazet 			}
12587562f876SPavel Emelianov cont:
12597562f876SPavel Emelianov 			idx++;
12601da177e4SLinus Torvalds 		}
1261eec4df98SEric Dumazet 		rcu_read_unlock();
1262eec4df98SEric Dumazet 	}
12631da177e4SLinus Torvalds 
12641da177e4SLinus Torvalds done:
1265eec4df98SEric Dumazet 	cb->args[0] = h;
1266eec4df98SEric Dumazet 	cb->args[1] = idx;
1267eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
12681da177e4SLinus Torvalds 
12691da177e4SLinus Torvalds 	return skb->len;
12701da177e4SLinus Torvalds }
12711da177e4SLinus Torvalds 
1272d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
1273d6062cbbSThomas Graf 		      u32 pid)
12741da177e4SLinus Torvalds {
127547f68512SThomas Graf 	struct sk_buff *skb;
1276d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1277d6062cbbSThomas Graf 	int err = -ENOBUFS;
12784b8aa9abSDenis V. Lunev 	struct net *net;
12791da177e4SLinus Torvalds 
1280c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1281339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
128247f68512SThomas Graf 	if (skb == NULL)
1283d6062cbbSThomas Graf 		goto errout;
1284d6062cbbSThomas Graf 
1285d6062cbbSThomas Graf 	err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
128626932566SPatrick McHardy 	if (err < 0) {
128726932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
128826932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
128926932566SPatrick McHardy 		kfree_skb(skb);
129026932566SPatrick McHardy 		goto errout;
129126932566SPatrick McHardy 	}
12921ce85fe4SPablo Neira Ayuso 	rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
12931ce85fe4SPablo Neira Ayuso 	return;
1294d6062cbbSThomas Graf errout:
1295d6062cbbSThomas Graf 	if (err < 0)
12964b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
12971da177e4SLinus Torvalds }
12981da177e4SLinus Torvalds 
12999f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
13009f0f7272SThomas Graf {
1301f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13029f0f7272SThomas Graf 
13039f0f7272SThomas Graf 	if (!in_dev)
13049f0f7272SThomas Graf 		return 0;
13059f0f7272SThomas Graf 
13069f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
13079f0f7272SThomas Graf }
13089f0f7272SThomas Graf 
13099f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
13109f0f7272SThomas Graf {
1311f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13129f0f7272SThomas Graf 	struct nlattr *nla;
13139f0f7272SThomas Graf 	int i;
13149f0f7272SThomas Graf 
13159f0f7272SThomas Graf 	if (!in_dev)
13169f0f7272SThomas Graf 		return -ENODATA;
13179f0f7272SThomas Graf 
13189f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
13199f0f7272SThomas Graf 	if (nla == NULL)
13209f0f7272SThomas Graf 		return -EMSGSIZE;
13219f0f7272SThomas Graf 
13229f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
13239f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
13249f0f7272SThomas Graf 
13259f0f7272SThomas Graf 	return 0;
13269f0f7272SThomas Graf }
13279f0f7272SThomas Graf 
13289f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
13299f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
13309f0f7272SThomas Graf };
13319f0f7272SThomas Graf 
1332cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1333cf7afbfeSThomas Graf 				 const struct nlattr *nla)
13349f0f7272SThomas Graf {
13359f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
13369f0f7272SThomas Graf 	int err, rem;
13379f0f7272SThomas Graf 
1338f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1339cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
13409f0f7272SThomas Graf 
13419f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
13429f0f7272SThomas Graf 	if (err < 0)
13439f0f7272SThomas Graf 		return err;
13449f0f7272SThomas Graf 
13459f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
13469f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
13479f0f7272SThomas Graf 			int cfgid = nla_type(a);
13489f0f7272SThomas Graf 
13499f0f7272SThomas Graf 			if (nla_len(a) < 4)
13509f0f7272SThomas Graf 				return -EINVAL;
13519f0f7272SThomas Graf 
13529f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
13539f0f7272SThomas Graf 				return -EINVAL;
13549f0f7272SThomas Graf 		}
13559f0f7272SThomas Graf 	}
13569f0f7272SThomas Graf 
1357cf7afbfeSThomas Graf 	return 0;
1358cf7afbfeSThomas Graf }
1359cf7afbfeSThomas Graf 
1360cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1361cf7afbfeSThomas Graf {
1362f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1363cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1364cf7afbfeSThomas Graf 	int rem;
1365cf7afbfeSThomas Graf 
1366cf7afbfeSThomas Graf 	if (!in_dev)
1367cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1368cf7afbfeSThomas Graf 
1369cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1370cf7afbfeSThomas Graf 		BUG();
1371cf7afbfeSThomas Graf 
13729f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
13739f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
13749f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
13759f0f7272SThomas Graf 	}
13769f0f7272SThomas Graf 
13779f0f7272SThomas Graf 	return 0;
13789f0f7272SThomas Graf }
13799f0f7272SThomas Graf 
13801da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
13811da177e4SLinus Torvalds 
1382c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
138331be3085SHerbert Xu {
138431be3085SHerbert Xu 	struct net_device *dev;
138531be3085SHerbert Xu 
138631be3085SHerbert Xu 	rcu_read_lock();
1387c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1388c6d14c84SEric Dumazet 		struct in_device *in_dev;
1389c6d14c84SEric Dumazet 
139031be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
139131be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
13929355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1393c6d14c84SEric Dumazet 	}
139431be3085SHerbert Xu 	rcu_read_unlock();
139531be3085SHerbert Xu }
139631be3085SHerbert Xu 
1397c6d14c84SEric Dumazet /* called with RTNL locked */
1398c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
139968dd299bSPavel Emelyanov {
140068dd299bSPavel Emelyanov 	struct net_device *dev;
1401586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
140268dd299bSPavel Emelyanov 
1403586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
14049355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
140568dd299bSPavel Emelyanov 
1406c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
140768dd299bSPavel Emelyanov 		struct in_device *in_dev;
14080187bdfbSBen Hutchings 		if (on)
14090187bdfbSBen Hutchings 			dev_disable_lro(dev);
141068dd299bSPavel Emelyanov 		rcu_read_lock();
141168dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
141268dd299bSPavel Emelyanov 		if (in_dev)
141368dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
141468dd299bSPavel Emelyanov 		rcu_read_unlock();
141568dd299bSPavel Emelyanov 	}
141668dd299bSPavel Emelyanov }
141768dd299bSPavel Emelyanov 
141831be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write,
14198d65af78SAlexey Dobriyan 			     void __user *buffer,
142031be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
142131be3085SHerbert Xu {
14228d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
142331be3085SHerbert Xu 
142431be3085SHerbert Xu 	if (write) {
142531be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1426c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
142731be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
142831be3085SHerbert Xu 
142931be3085SHerbert Xu 		set_bit(i, cnf->state);
143031be3085SHerbert Xu 
14319355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1432c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
143331be3085SHerbert Xu 	}
143431be3085SHerbert Xu 
143531be3085SHerbert Xu 	return ret;
143631be3085SHerbert Xu }
143731be3085SHerbert Xu 
14381da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write,
14398d65af78SAlexey Dobriyan 				  void __user *buffer,
14401da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
14411da177e4SLinus Torvalds {
14421da177e4SLinus Torvalds 	int *valp = ctl->data;
14431da177e4SLinus Torvalds 	int val = *valp;
144488af182eSEric W. Biederman 	loff_t pos = *ppos;
14458d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
14461da177e4SLinus Torvalds 
14471da177e4SLinus Torvalds 	if (write && *valp != val) {
1448c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
1449c0ce9fb3SPavel Emelyanov 
14500187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
145188af182eSEric W. Biederman 			if (!rtnl_trylock()) {
145288af182eSEric W. Biederman 				/* Restore the original values before restarting */
145388af182eSEric W. Biederman 				*valp = val;
145488af182eSEric W. Biederman 				*ppos = pos;
14559b8adb5eSEric W. Biederman 				return restart_syscall();
145688af182eSEric W. Biederman 			}
14570187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1458c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
14590187bdfbSBen Hutchings 			} else if (*valp) {
14600187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
14610187bdfbSBen Hutchings 				struct in_device *idev =
14620187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
14630187bdfbSBen Hutchings 				dev_disable_lro(idev->dev);
14640187bdfbSBen Hutchings 			}
14650187bdfbSBen Hutchings 			rtnl_unlock();
146676e6ebfbSDenis V. Lunev 			rt_cache_flush(net, 0);
14671da177e4SLinus Torvalds 		}
14680187bdfbSBen Hutchings 	}
14691da177e4SLinus Torvalds 
14701da177e4SLinus Torvalds 	return ret;
14711da177e4SLinus Torvalds }
14721da177e4SLinus Torvalds 
1473323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write,
14748d65af78SAlexey Dobriyan 				void __user *buffer,
14751da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
14761da177e4SLinus Torvalds {
14771da177e4SLinus Torvalds 	int *valp = ctl->data;
14781da177e4SLinus Torvalds 	int val = *valp;
14798d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
148076e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
14811da177e4SLinus Torvalds 
14821da177e4SLinus Torvalds 	if (write && *valp != val)
148376e6ebfbSDenis V. Lunev 		rt_cache_flush(net, 0);
14841da177e4SLinus Torvalds 
14851da177e4SLinus Torvalds 	return ret;
14861da177e4SLinus Torvalds }
14871da177e4SLinus Torvalds 
1488f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
148942f811b8SHerbert Xu 	{ \
149042f811b8SHerbert Xu 		.procname	= name, \
149142f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
149202291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
149342f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
149442f811b8SHerbert Xu 		.mode		= mval, \
149542f811b8SHerbert Xu 		.proc_handler	= proc, \
149631be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
149742f811b8SHerbert Xu 	}
149842f811b8SHerbert Xu 
149942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
1500f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
150142f811b8SHerbert Xu 
150242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
1503f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
150442f811b8SHerbert Xu 
1505f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1506f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
150742f811b8SHerbert Xu 
150842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
1509f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
151042f811b8SHerbert Xu 
15111da177e4SLinus Torvalds static struct devinet_sysctl_table {
15121da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
151302291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
1514bfada697SPavel Emelyanov 	char *dev_name;
15151da177e4SLinus Torvalds } devinet_sysctl = {
15161da177e4SLinus Torvalds 	.devinet_vars = {
151742f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
1518f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
151942f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
152042f811b8SHerbert Xu 
152142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
152242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
152342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
152442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
152542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
152642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
152742f811b8SHerbert Xu 					"accept_source_route"),
15288153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
152928f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
153042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
153142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
153242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
153342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
153442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
153542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
153642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
153742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
153842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
1539eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
154065324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
154142f811b8SHerbert Xu 
154242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
154342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
154442f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
154542f811b8SHerbert Xu 					      "force_igmp_version"),
154642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
154742f811b8SHerbert Xu 					      "promote_secondaries"),
15481da177e4SLinus Torvalds 	},
15491da177e4SLinus Torvalds };
15501da177e4SLinus Torvalds 
1551ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
1552f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
15531da177e4SLinus Torvalds {
15541da177e4SLinus Torvalds 	int i;
15559fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
15561da177e4SLinus Torvalds 
1557bfada697SPavel Emelyanov #define DEVINET_CTL_PATH_DEV	3
1558bfada697SPavel Emelyanov 
1559bfada697SPavel Emelyanov 	struct ctl_path devinet_ctl_path[] = {
1560f8572d8fSEric W. Biederman 		{ .procname = "net",  },
1561f8572d8fSEric W. Biederman 		{ .procname = "ipv4", },
1562f8572d8fSEric W. Biederman 		{ .procname = "conf", },
1563bfada697SPavel Emelyanov 		{ /* to be set */ },
1564bfada697SPavel Emelyanov 		{ },
1565bfada697SPavel Emelyanov 	};
1566bfada697SPavel Emelyanov 
15679fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
15681da177e4SLinus Torvalds 	if (!t)
15699fa89642SPavel Emelyanov 		goto out;
15709fa89642SPavel Emelyanov 
15711da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
15721da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
157331be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
1574c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
15751da177e4SLinus Torvalds 	}
15761da177e4SLinus Torvalds 
15771da177e4SLinus Torvalds 	/*
15781da177e4SLinus Torvalds 	 * Make a copy of dev_name, because '.procname' is regarded as const
15791da177e4SLinus Torvalds 	 * by sysctl and we wouldn't want anyone to change it under our feet
15801da177e4SLinus Torvalds 	 * (see SIOCSIFNAME).
15811da177e4SLinus Torvalds 	 */
1582bfada697SPavel Emelyanov 	t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1583bfada697SPavel Emelyanov 	if (!t->dev_name)
15841da177e4SLinus Torvalds 		goto free;
15851da177e4SLinus Torvalds 
1586bfada697SPavel Emelyanov 	devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
15871da177e4SLinus Torvalds 
1588752d14dcSPavel Emelyanov 	t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
1589bfada697SPavel Emelyanov 			t->devinet_vars);
15901da177e4SLinus Torvalds 	if (!t->sysctl_header)
15911da177e4SLinus Torvalds 		goto free_procname;
15921da177e4SLinus Torvalds 
15931da177e4SLinus Torvalds 	p->sysctl = t;
1594ea40b324SPavel Emelyanov 	return 0;
15951da177e4SLinus Torvalds 
15961da177e4SLinus Torvalds free_procname:
1597bfada697SPavel Emelyanov 	kfree(t->dev_name);
15981da177e4SLinus Torvalds free:
15991da177e4SLinus Torvalds 	kfree(t);
16009fa89642SPavel Emelyanov out:
1601ea40b324SPavel Emelyanov 	return -ENOBUFS;
16021da177e4SLinus Torvalds }
16031da177e4SLinus Torvalds 
160451602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
160566f27a52SPavel Emelyanov {
160651602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
160766f27a52SPavel Emelyanov 
160851602b2aSPavel Emelyanov 	if (t == NULL)
160951602b2aSPavel Emelyanov 		return;
161051602b2aSPavel Emelyanov 
161151602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
16121da177e4SLinus Torvalds 	unregister_sysctl_table(t->sysctl_header);
1613bfada697SPavel Emelyanov 	kfree(t->dev_name);
16141da177e4SLinus Torvalds 	kfree(t);
16151da177e4SLinus Torvalds }
161651602b2aSPavel Emelyanov 
161751602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
161851602b2aSPavel Emelyanov {
161954716e3bSEric W. Biederman 	neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
1620c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
1621f8572d8fSEric W. Biederman 					&idev->cnf);
162251602b2aSPavel Emelyanov }
162351602b2aSPavel Emelyanov 
162451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
162551602b2aSPavel Emelyanov {
162651602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
162751602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
16281da177e4SLinus Torvalds }
16291da177e4SLinus Torvalds 
163068dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
163168dd299bSPavel Emelyanov 	{
163268dd299bSPavel Emelyanov 		.procname	= "ip_forward",
163368dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
163402291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
163568dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
163668dd299bSPavel Emelyanov 		.mode		= 0644,
163768dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
163868dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
1639c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
164068dd299bSPavel Emelyanov 	},
164168dd299bSPavel Emelyanov 	{ },
164268dd299bSPavel Emelyanov };
164368dd299bSPavel Emelyanov 
1644752d14dcSPavel Emelyanov static __net_initdata struct ctl_path net_ipv4_path[] = {
1645f8572d8fSEric W. Biederman 	{ .procname = "net", },
1646f8572d8fSEric W. Biederman 	{ .procname = "ipv4", },
164768dd299bSPavel Emelyanov 	{ },
164868dd299bSPavel Emelyanov };
16492a75de0cSEric Dumazet #endif
165068dd299bSPavel Emelyanov 
1651752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
1652752d14dcSPavel Emelyanov {
1653752d14dcSPavel Emelyanov 	int err;
1654752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
16552a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
16562a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
1657752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
16582a75de0cSEric Dumazet #endif
1659752d14dcSPavel Emelyanov 
1660752d14dcSPavel Emelyanov 	err = -ENOMEM;
1661752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
1662752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
1663752d14dcSPavel Emelyanov 
166409ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
1665752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1666752d14dcSPavel Emelyanov 		if (all == NULL)
1667752d14dcSPavel Emelyanov 			goto err_alloc_all;
1668752d14dcSPavel Emelyanov 
1669752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1670752d14dcSPavel Emelyanov 		if (dflt == NULL)
1671752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
1672752d14dcSPavel Emelyanov 
16732a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1674752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1675752d14dcSPavel Emelyanov 		if (tbl == NULL)
1676752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
1677752d14dcSPavel Emelyanov 
167802291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
1679752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
1680752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
16812a75de0cSEric Dumazet #endif
1682752d14dcSPavel Emelyanov 	}
1683752d14dcSPavel Emelyanov 
1684752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1685f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
1686752d14dcSPavel Emelyanov 	if (err < 0)
1687752d14dcSPavel Emelyanov 		goto err_reg_all;
1688752d14dcSPavel Emelyanov 
1689f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
1690752d14dcSPavel Emelyanov 	if (err < 0)
1691752d14dcSPavel Emelyanov 		goto err_reg_dflt;
1692752d14dcSPavel Emelyanov 
1693752d14dcSPavel Emelyanov 	err = -ENOMEM;
1694752d14dcSPavel Emelyanov 	forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1695752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
1696752d14dcSPavel Emelyanov 		goto err_reg_ctl;
16972a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
1698752d14dcSPavel Emelyanov #endif
1699752d14dcSPavel Emelyanov 
1700752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
1701752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
1702752d14dcSPavel Emelyanov 	return 0;
1703752d14dcSPavel Emelyanov 
1704752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1705752d14dcSPavel Emelyanov err_reg_ctl:
1706752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
1707752d14dcSPavel Emelyanov err_reg_dflt:
1708752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
1709752d14dcSPavel Emelyanov err_reg_all:
1710752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
1711752d14dcSPavel Emelyanov 		kfree(tbl);
1712752d14dcSPavel Emelyanov err_alloc_ctl:
17132a75de0cSEric Dumazet #endif
1714752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
1715752d14dcSPavel Emelyanov 		kfree(dflt);
1716752d14dcSPavel Emelyanov err_alloc_dflt:
1717752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
1718752d14dcSPavel Emelyanov 		kfree(all);
1719752d14dcSPavel Emelyanov err_alloc_all:
1720752d14dcSPavel Emelyanov 	return err;
1721752d14dcSPavel Emelyanov }
1722752d14dcSPavel Emelyanov 
1723752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
1724752d14dcSPavel Emelyanov {
17252a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1726752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
1727752d14dcSPavel Emelyanov 
1728752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
1729752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
1730752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1731752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
1732752d14dcSPavel Emelyanov 	kfree(tbl);
17332a75de0cSEric Dumazet #endif
1734752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
1735752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
1736752d14dcSPavel Emelyanov }
1737752d14dcSPavel Emelyanov 
1738752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
1739752d14dcSPavel Emelyanov 	.init = devinet_init_net,
1740752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
1741752d14dcSPavel Emelyanov };
1742752d14dcSPavel Emelyanov 
17439f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
17449f0f7272SThomas Graf 	.family		  = AF_INET,
17459f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
17469f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
1747cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
1748cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
17499f0f7272SThomas Graf };
17509f0f7272SThomas Graf 
17511da177e4SLinus Torvalds void __init devinet_init(void)
17521da177e4SLinus Torvalds {
1753fd23c3b3SDavid S. Miller 	int i;
1754fd23c3b3SDavid S. Miller 
1755fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
1756fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
1757fd23c3b3SDavid S. Miller 
1758752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
1759752d14dcSPavel Emelyanov 
17601da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
17611da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
176263f3444fSThomas Graf 
17639f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
17649f0f7272SThomas Graf 
176563f3444fSThomas Graf 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
176663f3444fSThomas Graf 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
176763f3444fSThomas Graf 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
17681da177e4SLinus Torvalds }
17691da177e4SLinus Torvalds 
1770