xref: /openbmc/linux/net/ipv4/devinet.c (revision f3756b79)
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 
67406b6f97SDavid S. Miller #include "fib_lookup.h"
68406b6f97SDavid S. Miller 
690027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7042f811b8SHerbert Xu 	.data = {
7102291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7202291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
7542f811b8SHerbert Xu 	},
761da177e4SLinus Torvalds };
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
7942f811b8SHerbert Xu 	.data = {
8002291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8102291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8202291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8302291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
8542f811b8SHerbert Xu 	},
861da177e4SLinus Torvalds };
871da177e4SLinus Torvalds 
889355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
899355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9042f811b8SHerbert Xu 
91ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
925c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
935c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
945c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
955176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
965c753978SThomas Graf };
975c753978SThomas Graf 
98fd23c3b3SDavid S. Miller /* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE
99fd23c3b3SDavid S. Miller  * value.  So if you change this define, make appropriate changes to
100fd23c3b3SDavid S. Miller  * inet_addr_hash as well.
101fd23c3b3SDavid S. Miller  */
102fd23c3b3SDavid S. Miller #define IN4_ADDR_HSIZE	256
103fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
104fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock);
105fd23c3b3SDavid S. Miller 
106fd23c3b3SDavid S. Miller static inline unsigned int inet_addr_hash(struct net *net, __be32 addr)
107fd23c3b3SDavid S. Miller {
108fd23c3b3SDavid S. Miller 	u32 val = (__force u32) addr ^ hash_ptr(net, 8);
109fd23c3b3SDavid S. Miller 
110fd23c3b3SDavid S. Miller 	return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) &
111fd23c3b3SDavid S. Miller 		(IN4_ADDR_HSIZE - 1));
112fd23c3b3SDavid S. Miller }
113fd23c3b3SDavid S. Miller 
114fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
115fd23c3b3SDavid S. Miller {
116e066008bSDavid S. Miller 	unsigned int hash = inet_addr_hash(net, ifa->ifa_local);
117fd23c3b3SDavid S. Miller 
118fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
119fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
120fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
121fd23c3b3SDavid S. Miller }
122fd23c3b3SDavid S. Miller 
123fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
124fd23c3b3SDavid S. Miller {
125fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
126fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
127fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
128fd23c3b3SDavid S. Miller }
129fd23c3b3SDavid S. Miller 
1309435eb1cSDavid S. Miller /**
1319435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1329435eb1cSDavid S. Miller  * @net: the net namespace
1339435eb1cSDavid S. Miller  * @addr: the source address
1349435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1359435eb1cSDavid S. Miller  *
1369435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1379435eb1cSDavid S. Miller  */
1389435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1399435eb1cSDavid S. Miller {
1409435eb1cSDavid S. Miller 	unsigned int hash = inet_addr_hash(net, addr);
1419435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1429435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1439435eb1cSDavid S. Miller 	struct hlist_node *node;
1449435eb1cSDavid S. Miller 
1459435eb1cSDavid S. Miller 	rcu_read_lock();
1469435eb1cSDavid S. Miller 	hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
1479435eb1cSDavid S. Miller 		struct net_device *dev = ifa->ifa_dev->dev;
1489435eb1cSDavid S. Miller 
1499435eb1cSDavid S. Miller 		if (!net_eq(dev_net(dev), net))
1509435eb1cSDavid S. Miller 			continue;
151e066008bSDavid S. Miller 		if (ifa->ifa_local == addr) {
1529435eb1cSDavid S. Miller 			result = dev;
1539435eb1cSDavid S. Miller 			break;
1549435eb1cSDavid S. Miller 		}
1559435eb1cSDavid S. Miller 	}
156406b6f97SDavid S. Miller 	if (!result) {
157406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
158406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
159406b6f97SDavid S. Miller 		struct fib_table *local;
160406b6f97SDavid S. Miller 
161406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
162406b6f97SDavid S. Miller 		 * over loopback subnets work.
163406b6f97SDavid S. Miller 		 */
164406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
165406b6f97SDavid S. Miller 		if (local &&
166406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
167406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
168406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
169406b6f97SDavid S. Miller 	}
1709435eb1cSDavid S. Miller 	if (result && devref)
1719435eb1cSDavid S. Miller 		dev_hold(result);
1729435eb1cSDavid S. Miller 	rcu_read_unlock();
1739435eb1cSDavid S. Miller 	return result;
1749435eb1cSDavid S. Miller }
1759435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1769435eb1cSDavid S. Miller 
177d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1781da177e4SLinus Torvalds 
179e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1801da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1811da177e4SLinus Torvalds 			 int destroy);
1821da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
18366f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev);
18451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
18551602b2aSPavel Emelyanov #else
18651602b2aSPavel Emelyanov static inline void devinet_sysctl_register(struct in_device *idev)
18751602b2aSPavel Emelyanov {
18851602b2aSPavel Emelyanov }
18951602b2aSPavel Emelyanov static inline void devinet_sysctl_unregister(struct in_device *idev)
19051602b2aSPavel Emelyanov {
19151602b2aSPavel Emelyanov }
1921da177e4SLinus Torvalds #endif
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds /* Locks all the inet devices. */
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1971da177e4SLinus Torvalds {
19893adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2021da177e4SLinus Torvalds {
2031da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2041da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2051da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2061da177e4SLinus Torvalds 	kfree(ifa);
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds static inline void inet_free_ifa(struct in_ifaddr *ifa)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2171da177e4SLinus Torvalds 
218547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
219547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2201da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
2211da177e4SLinus Torvalds 	printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
2221da177e4SLinus Torvalds 	       idev, dev ? dev->name : "NIL");
2231da177e4SLinus Torvalds #endif
2241da177e4SLinus Torvalds 	dev_put(dev);
2251da177e4SLinus Torvalds 	if (!idev->dead)
2269f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2279f9354b9SEric Dumazet 	else
2281da177e4SLinus Torvalds 		kfree(idev);
2291da177e4SLinus Torvalds }
2309f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2311da177e4SLinus Torvalds 
23271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	struct in_device *in_dev;
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	ASSERT_RTNL();
2371da177e4SLinus Torvalds 
2380da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2391da177e4SLinus Torvalds 	if (!in_dev)
2401da177e4SLinus Torvalds 		goto out;
241c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2429355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2431da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2441da177e4SLinus Torvalds 	in_dev->dev = dev;
2459f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2469f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2471da177e4SLinus Torvalds 		goto out_kfree;
2480187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2490187bdfbSBen Hutchings 		dev_disable_lro(dev);
2501da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2511da177e4SLinus Torvalds 	dev_hold(dev);
25230c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2531da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2541da177e4SLinus Torvalds 
25566f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2561da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2571da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2581da177e4SLinus Torvalds 		ip_mc_up(in_dev);
259483479ecSJarek Poplawski 
26030c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
261cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
262483479ecSJarek Poplawski out:
2631da177e4SLinus Torvalds 	return in_dev;
2641da177e4SLinus Torvalds out_kfree:
2651da177e4SLinus Torvalds 	kfree(in_dev);
2661da177e4SLinus Torvalds 	in_dev = NULL;
2671da177e4SLinus Torvalds 	goto out;
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2711da177e4SLinus Torvalds {
2721da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2731da177e4SLinus Torvalds 	in_dev_put(idev);
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2771da177e4SLinus Torvalds {
2781da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2791da177e4SLinus Torvalds 	struct net_device *dev;
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	ASSERT_RTNL();
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	dev = in_dev->dev;
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	in_dev->dead = 1;
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2901da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2911da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2921da177e4SLinus Torvalds 	}
2931da177e4SLinus Torvalds 
294a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
2951da177e4SLinus Torvalds 
29651602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
2971da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
2981da177e4SLinus Torvalds 	arp_ifdown(dev);
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3011da177e4SLinus Torvalds }
3021da177e4SLinus Torvalds 
303ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3041da177e4SLinus Torvalds {
3051da177e4SLinus Torvalds 	rcu_read_lock();
3061da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3071da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3081da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3091da177e4SLinus Torvalds 				rcu_read_unlock();
3101da177e4SLinus Torvalds 				return 1;
3111da177e4SLinus Torvalds 			}
3121da177e4SLinus Torvalds 		}
3131da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3141da177e4SLinus Torvalds 	rcu_read_unlock();
3151da177e4SLinus Torvalds 	return 0;
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds 
318d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
319d6062cbbSThomas Graf 			 int destroy, struct nlmsghdr *nlh, u32 pid)
3201da177e4SLinus Torvalds {
3218f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3220ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3230ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3240ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3250ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds 	ASSERT_RTNL();
3281da177e4SLinus Torvalds 
3298f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3308f937c60SHarald Welte 	 * unless alias promotion is set
3318f937c60SHarald Welte 	 **/
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3341da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3370ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3380ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3390ff60a45SJamal Hadi Salim 				last_prim = ifa;
3400ff60a45SJamal Hadi Salim 
3411da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3421da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3431da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3441da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3450ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3461da177e4SLinus Torvalds 				continue;
3471da177e4SLinus Torvalds 			}
3481da177e4SLinus Torvalds 
3490ff60a45SJamal Hadi Salim 			if (!do_promote) {
350fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3511da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3521da177e4SLinus Torvalds 
353d6062cbbSThomas Graf 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
354e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
355e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3561da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3578f937c60SHarald Welte 			} else {
3588f937c60SHarald Welte 				promote = ifa;
3598f937c60SHarald Welte 				break;
3608f937c60SHarald Welte 			}
3611da177e4SLinus Torvalds 		}
3621da177e4SLinus Torvalds 	}
3631da177e4SLinus Torvalds 
3642d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3652d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3662d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3672d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3682d230e2bSJulian Anastasov 	 */
3692d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3702d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3712d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3722d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3732d230e2bSJulian Anastasov 	}
3742d230e2bSJulian Anastasov 
3751da177e4SLinus Torvalds 	/* 2. Unlink it */
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
378fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3831da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3841da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3851da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3861da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3871da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3881da177e4SLinus Torvalds 	   So that, this order is correct.
3891da177e4SLinus Torvalds 	 */
390d6062cbbSThomas Graf 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
391e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3920ff60a45SJamal Hadi Salim 
3930ff60a45SJamal Hadi Salim 	if (promote) {
39404024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
3950ff60a45SJamal Hadi Salim 
3960ff60a45SJamal Hadi Salim 		if (prev_prom) {
3970ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
3980ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
3990ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4000ff60a45SJamal Hadi Salim 		}
4010ff60a45SJamal Hadi Salim 
4020ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
403d6062cbbSThomas Graf 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
404e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
405e041c683SAlan Stern 				NETDEV_UP, promote);
40604024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4070ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4080ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4090ff60a45SJamal Hadi Salim 					continue;
4100ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4110ff60a45SJamal Hadi Salim 		}
4120ff60a45SJamal Hadi Salim 
4130ff60a45SJamal Hadi Salim 	}
4146363097cSHerbert Xu 	if (destroy)
4151da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds 
418d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
419d6062cbbSThomas Graf 			 int destroy)
420d6062cbbSThomas Graf {
421d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
422d6062cbbSThomas Graf }
423d6062cbbSThomas Graf 
424d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
425d6062cbbSThomas Graf 			     u32 pid)
4261da177e4SLinus Torvalds {
4271da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4281da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	ASSERT_RTNL();
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4331da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4341da177e4SLinus Torvalds 		return 0;
4351da177e4SLinus Torvalds 	}
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4381da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4411da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4421da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4431da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4441da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4451da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4461da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4471da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4481da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4491da177e4SLinus Torvalds 				return -EEXIST;
4501da177e4SLinus Torvalds 			}
4511da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4521da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4531da177e4SLinus Torvalds 				return -EINVAL;
4541da177e4SLinus Torvalds 			}
4551da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4561da177e4SLinus Torvalds 		}
4571da177e4SLinus Torvalds 	}
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
4601da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4611da177e4SLinus Torvalds 		ifap = last_primary;
4621da177e4SLinus Torvalds 	}
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4651da177e4SLinus Torvalds 	*ifap = ifa;
4661da177e4SLinus Torvalds 
467fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
468fd23c3b3SDavid S. Miller 
4691da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4701da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4711da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
472d6062cbbSThomas Graf 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
473e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	return 0;
4761da177e4SLinus Torvalds }
4771da177e4SLinus Torvalds 
478d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
479d6062cbbSThomas Graf {
480d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
481d6062cbbSThomas Graf }
482d6062cbbSThomas Graf 
4831da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4841da177e4SLinus Torvalds {
485e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	ASSERT_RTNL();
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	if (!in_dev) {
4901da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4911da177e4SLinus Torvalds 		return -ENOBUFS;
4921da177e4SLinus Torvalds 	}
49371e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
4941da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
495547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
4961da177e4SLinus Torvalds 		in_dev_hold(in_dev);
4971da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
4981da177e4SLinus Torvalds 	}
499f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5001da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5011da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5021da177e4SLinus Torvalds }
5031da177e4SLinus Torvalds 
5048723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5058723e1b4SEric Dumazet  * We dont take a reference on found in_device
5068723e1b4SEric Dumazet  */
5077fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5081da177e4SLinus Torvalds {
5091da177e4SLinus Torvalds 	struct net_device *dev;
5101da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
511c148fc2eSEric Dumazet 
512c148fc2eSEric Dumazet 	rcu_read_lock();
513c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5141da177e4SLinus Torvalds 	if (dev)
5158723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
516c148fc2eSEric Dumazet 	rcu_read_unlock();
5171da177e4SLinus Torvalds 	return in_dev;
5181da177e4SLinus Torvalds }
5199f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5221da177e4SLinus Torvalds 
52360cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
52460cad5daSAl Viro 				    __be32 mask)
5251da177e4SLinus Torvalds {
5261da177e4SLinus Torvalds 	ASSERT_RTNL();
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5291da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5301da177e4SLinus Torvalds 			return ifa;
5311da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5321da177e4SLinus Torvalds 	return NULL;
5331da177e4SLinus Torvalds }
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
5361da177e4SLinus Torvalds {
5373b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
538dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5391da177e4SLinus Torvalds 	struct in_device *in_dev;
540dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5411da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
542dfdd5fd4SThomas Graf 	int err = -EINVAL;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	ASSERT_RTNL();
5451da177e4SLinus Torvalds 
546dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
547dfdd5fd4SThomas Graf 	if (err < 0)
548dfdd5fd4SThomas Graf 		goto errout;
549dfdd5fd4SThomas Graf 
550dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5517fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
552dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
553dfdd5fd4SThomas Graf 		err = -ENODEV;
554dfdd5fd4SThomas Graf 		goto errout;
555dfdd5fd4SThomas Graf 	}
556dfdd5fd4SThomas Graf 
5571da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5581da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
559dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
560a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5611da177e4SLinus Torvalds 			continue;
562dfdd5fd4SThomas Graf 
563dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
564dfdd5fd4SThomas Graf 			continue;
565dfdd5fd4SThomas Graf 
566dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
567dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
568a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
569dfdd5fd4SThomas Graf 			continue;
570dfdd5fd4SThomas Graf 
571d6062cbbSThomas Graf 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
5721da177e4SLinus Torvalds 		return 0;
5731da177e4SLinus Torvalds 	}
574dfdd5fd4SThomas Graf 
575dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
576dfdd5fd4SThomas Graf errout:
577dfdd5fd4SThomas Graf 	return err;
5781da177e4SLinus Torvalds }
5791da177e4SLinus Torvalds 
5804b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
5811da177e4SLinus Torvalds {
5825c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5835c753978SThomas Graf 	struct in_ifaddr *ifa;
5845c753978SThomas Graf 	struct ifaddrmsg *ifm;
5851da177e4SLinus Torvalds 	struct net_device *dev;
5861da177e4SLinus Torvalds 	struct in_device *in_dev;
5877b218574SDenis V. Lunev 	int err;
5881da177e4SLinus Torvalds 
5895c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
5905c753978SThomas Graf 	if (err < 0)
5915c753978SThomas Graf 		goto errout;
5921da177e4SLinus Torvalds 
5935c753978SThomas Graf 	ifm = nlmsg_data(nlh);
594c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
5957b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
5965c753978SThomas Graf 		goto errout;
5971da177e4SLinus Torvalds 
5984b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
5995c753978SThomas Graf 	err = -ENODEV;
6007b218574SDenis V. Lunev 	if (dev == NULL)
6015c753978SThomas Graf 		goto errout;
6021da177e4SLinus Torvalds 
6035c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
6045c753978SThomas Graf 	err = -ENOBUFS;
6057b218574SDenis V. Lunev 	if (in_dev == NULL)
6065c753978SThomas Graf 		goto errout;
60771e27da9SHerbert Xu 
6085c753978SThomas Graf 	ifa = inet_alloc_ifa();
6097b218574SDenis V. Lunev 	if (ifa == NULL)
6105c753978SThomas Graf 		/*
6115c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
6125c753978SThomas Graf 		 * assigned to its device and is destroy with it.
6135c753978SThomas Graf 		 */
6145c753978SThomas Graf 		goto errout;
6155c753978SThomas Graf 
616a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
6175c753978SThomas Graf 	in_dev_hold(in_dev);
6185c753978SThomas Graf 
6195c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
6205c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
6215c753978SThomas Graf 
622fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
6231da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
6241da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
6251da177e4SLinus Torvalds 	ifa->ifa_flags = ifm->ifa_flags;
6261da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
6271da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
6285c753978SThomas Graf 
629a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
630a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
6315c753978SThomas Graf 
6325c753978SThomas Graf 	if (tb[IFA_BROADCAST])
633a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
6345c753978SThomas Graf 
6355c753978SThomas Graf 	if (tb[IFA_LABEL])
6365c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
6371da177e4SLinus Torvalds 	else
6381da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
6391da177e4SLinus Torvalds 
6405c753978SThomas Graf 	return ifa;
6415c753978SThomas Graf 
6425c753978SThomas Graf errout:
6435c753978SThomas Graf 	return ERR_PTR(err);
6445c753978SThomas Graf }
6455c753978SThomas Graf 
6465c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
6475c753978SThomas Graf {
6483b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6495c753978SThomas Graf 	struct in_ifaddr *ifa;
6505c753978SThomas Graf 
6515c753978SThomas Graf 	ASSERT_RTNL();
6525c753978SThomas Graf 
6534b8aa9abSDenis V. Lunev 	ifa = rtm_to_ifaddr(net, nlh);
6545c753978SThomas Graf 	if (IS_ERR(ifa))
6555c753978SThomas Graf 		return PTR_ERR(ifa);
6565c753978SThomas Graf 
657d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
6581da177e4SLinus Torvalds }
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds /*
6611da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
6621da177e4SLinus Torvalds  */
6631da177e4SLinus Torvalds 
6649f9354b9SEric Dumazet static inline int inet_abc_len(__be32 addr)
6651da177e4SLinus Torvalds {
6661da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
6671da177e4SLinus Torvalds 
668f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
6691da177e4SLinus Torvalds 		rc = 0;
6701da177e4SLinus Torvalds 	else {
671714e85beSAl Viro 		__u32 haddr = ntohl(addr);
6721da177e4SLinus Torvalds 
673714e85beSAl Viro 		if (IN_CLASSA(haddr))
6741da177e4SLinus Torvalds 			rc = 8;
675714e85beSAl Viro 		else if (IN_CLASSB(haddr))
6761da177e4SLinus Torvalds 			rc = 16;
677714e85beSAl Viro 		else if (IN_CLASSC(haddr))
6781da177e4SLinus Torvalds 			rc = 24;
6791da177e4SLinus Torvalds 	}
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	return rc;
6821da177e4SLinus Torvalds }
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 
685e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
6861da177e4SLinus Torvalds {
6871da177e4SLinus Torvalds 	struct ifreq ifr;
6881da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
6891da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
6901da177e4SLinus Torvalds 	struct in_device *in_dev;
6911da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
6921da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
6931da177e4SLinus Torvalds 	struct net_device *dev;
6941da177e4SLinus Torvalds 	char *colon;
6951da177e4SLinus Torvalds 	int ret = -EFAULT;
6961da177e4SLinus Torvalds 	int tryaddrmatch = 0;
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	/*
6991da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
7001da177e4SLinus Torvalds 	 */
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
7031da177e4SLinus Torvalds 		goto out;
7041da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds 	/* save original address for comparison */
7071da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
7101da177e4SLinus Torvalds 	if (colon)
7111da177e4SLinus Torvalds 		*colon = 0;
7121da177e4SLinus Torvalds 
713e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds 	switch (cmd) {
7161da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7171da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7181da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7191da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
7201da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
7211da177e4SLinus Torvalds 		   so that we do not impose a lock.
7221da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
7231da177e4SLinus Torvalds 		 */
7241da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
7251da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
7261da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
7271da177e4SLinus Torvalds 		break;
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
7301da177e4SLinus Torvalds 		ret = -EACCES;
7311da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
7321da177e4SLinus Torvalds 			goto out;
7331da177e4SLinus Torvalds 		break;
7341da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
7351da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
7361da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
7371da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
7381da177e4SLinus Torvalds 		ret = -EACCES;
7391da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
7401da177e4SLinus Torvalds 			goto out;
7411da177e4SLinus Torvalds 		ret = -EINVAL;
7421da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
7431da177e4SLinus Torvalds 			goto out;
7441da177e4SLinus Torvalds 		break;
7451da177e4SLinus Torvalds 	default:
7461da177e4SLinus Torvalds 		ret = -EINVAL;
7471da177e4SLinus Torvalds 		goto out;
7481da177e4SLinus Torvalds 	}
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds 	rtnl_lock();
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	ret = -ENODEV;
7539f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
7549f9354b9SEric Dumazet 	if (!dev)
7551da177e4SLinus Torvalds 		goto done;
7561da177e4SLinus Torvalds 
7571da177e4SLinus Torvalds 	if (colon)
7581da177e4SLinus Torvalds 		*colon = ':';
7591da177e4SLinus Torvalds 
7609f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
7619f9354b9SEric Dumazet 	if (in_dev) {
7621da177e4SLinus Torvalds 		if (tryaddrmatch) {
7631da177e4SLinus Torvalds 			/* Matthias Andree */
7641da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
7651da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
7661da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
7671da177e4SLinus Torvalds 			   This is checked above. */
7681da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7691da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
7701da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
7711da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
7726c91afe1SDavid S. Miller 							ifa->ifa_local) {
7731da177e4SLinus Torvalds 					break; /* found */
7741da177e4SLinus Torvalds 				}
7751da177e4SLinus Torvalds 			}
7761da177e4SLinus Torvalds 		}
7771da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
7781da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
7791da177e4SLinus Torvalds 		   comparing just the label */
7801da177e4SLinus Torvalds 		if (!ifa) {
7811da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7821da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
7831da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
7841da177e4SLinus Torvalds 					break;
7851da177e4SLinus Torvalds 		}
7861da177e4SLinus Torvalds 	}
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
7891da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
7901da177e4SLinus Torvalds 		goto done;
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 	switch (cmd) {
7931da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7941da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
7951da177e4SLinus Torvalds 		goto rarok;
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7981da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
7991da177e4SLinus Torvalds 		goto rarok;
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
8021da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
8031da177e4SLinus Torvalds 		goto rarok;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
8061da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
8071da177e4SLinus Torvalds 		goto rarok;
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
8101da177e4SLinus Torvalds 		if (colon) {
8111da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
8121da177e4SLinus Torvalds 			if (!ifa)
8131da177e4SLinus Torvalds 				break;
8141da177e4SLinus Torvalds 			ret = 0;
8151da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
8161da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
8171da177e4SLinus Torvalds 			break;
8181da177e4SLinus Torvalds 		}
8191da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
8201da177e4SLinus Torvalds 		break;
8211da177e4SLinus Torvalds 
8221da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
8231da177e4SLinus Torvalds 		ret = -EINVAL;
8241da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8251da177e4SLinus Torvalds 			break;
8261da177e4SLinus Torvalds 
8271da177e4SLinus Torvalds 		if (!ifa) {
8281da177e4SLinus Torvalds 			ret = -ENOBUFS;
8299f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
830fd23c3b3SDavid S. Miller 			INIT_HLIST_NODE(&ifa->hash);
8319f9354b9SEric Dumazet 			if (!ifa)
8321da177e4SLinus Torvalds 				break;
8331da177e4SLinus Torvalds 			if (colon)
8341da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
8351da177e4SLinus Torvalds 			else
8361da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8371da177e4SLinus Torvalds 		} else {
8381da177e4SLinus Torvalds 			ret = 0;
8391da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
8401da177e4SLinus Torvalds 				break;
8411da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8421da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
843148f9729SBjorn Mork 			ifa->ifa_scope = 0;
8441da177e4SLinus Torvalds 		}
8451da177e4SLinus Torvalds 
8461da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
8471da177e4SLinus Torvalds 
8481da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
8491da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
8501da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
8511da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
8521da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
8531da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
8541da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
8551da177e4SLinus Torvalds 		} else {
8561da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
8571da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
8581da177e4SLinus Torvalds 		}
8591da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
8601da177e4SLinus Torvalds 		break;
8611da177e4SLinus Torvalds 
8621da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
8631da177e4SLinus Torvalds 		ret = 0;
8641da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
8651da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8661da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
8671da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
8681da177e4SLinus Torvalds 		}
8691da177e4SLinus Torvalds 		break;
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
8721da177e4SLinus Torvalds 		ret = 0;
8731da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
8741da177e4SLinus Torvalds 			break;
8751da177e4SLinus Torvalds 		ret = -EINVAL;
8761da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8771da177e4SLinus Torvalds 			break;
8781da177e4SLinus Torvalds 		ret = 0;
8791da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
8801da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
8811da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
8821da177e4SLinus Torvalds 		break;
8831da177e4SLinus Torvalds 
8841da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds 		/*
8871da177e4SLinus Torvalds 		 *	The mask we set must be legal.
8881da177e4SLinus Torvalds 		 */
8891da177e4SLinus Torvalds 		ret = -EINVAL;
8901da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
8911da177e4SLinus Torvalds 			break;
8921da177e4SLinus Torvalds 		ret = 0;
8931da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
894a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
8951da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8961da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
8971da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 			/* See if current broadcast address matches
9001da177e4SLinus Torvalds 			 * with current netmask, then recalculate
9011da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
9021da177e4SLinus Torvalds 			 * funny address, so don't touch it since
9031da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
9041da177e4SLinus Torvalds 			 */
9051da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
9061da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
9071da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
908dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
9091da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
9101da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
9111da177e4SLinus Torvalds 			}
9121da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
9131da177e4SLinus Torvalds 		}
9141da177e4SLinus Torvalds 		break;
9151da177e4SLinus Torvalds 	}
9161da177e4SLinus Torvalds done:
9171da177e4SLinus Torvalds 	rtnl_unlock();
9181da177e4SLinus Torvalds out:
9191da177e4SLinus Torvalds 	return ret;
9201da177e4SLinus Torvalds rarok:
9211da177e4SLinus Torvalds 	rtnl_unlock();
9221da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
9231da177e4SLinus Torvalds 	goto out;
9241da177e4SLinus Torvalds }
9251da177e4SLinus Torvalds 
9261da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
9271da177e4SLinus Torvalds {
928e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
9291da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
9301da177e4SLinus Torvalds 	struct ifreq ifr;
9311da177e4SLinus Torvalds 	int done = 0;
9321da177e4SLinus Torvalds 
9339f9354b9SEric Dumazet 	if (!in_dev)
9341da177e4SLinus Torvalds 		goto out;
9351da177e4SLinus Torvalds 
9369f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
9371da177e4SLinus Torvalds 		if (!buf) {
9381da177e4SLinus Torvalds 			done += sizeof(ifr);
9391da177e4SLinus Torvalds 			continue;
9401da177e4SLinus Torvalds 		}
9411da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
9421da177e4SLinus Torvalds 			break;
9431da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
9441da177e4SLinus Torvalds 		if (ifa->ifa_label)
9451da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, ifa->ifa_label);
9461da177e4SLinus Torvalds 		else
9471da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, dev->name);
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
9501da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
9511da177e4SLinus Torvalds 								ifa->ifa_local;
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
9541da177e4SLinus Torvalds 			done = -EFAULT;
9551da177e4SLinus Torvalds 			break;
9561da177e4SLinus Torvalds 		}
9571da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
9581da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
9591da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
9601da177e4SLinus Torvalds 	}
9611da177e4SLinus Torvalds out:
9621da177e4SLinus Torvalds 	return done;
9631da177e4SLinus Torvalds }
9641da177e4SLinus Torvalds 
965a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
9661da177e4SLinus Torvalds {
967a61ced5dSAl Viro 	__be32 addr = 0;
9681da177e4SLinus Torvalds 	struct in_device *in_dev;
969c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
9701da177e4SLinus Torvalds 
9711da177e4SLinus Torvalds 	rcu_read_lock();
972e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
9731da177e4SLinus Torvalds 	if (!in_dev)
9741da177e4SLinus Torvalds 		goto no_in_dev;
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
9771da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
9781da177e4SLinus Torvalds 			continue;
9791da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
9801da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9811da177e4SLinus Torvalds 			break;
9821da177e4SLinus Torvalds 		}
9831da177e4SLinus Torvalds 		if (!addr)
9841da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9851da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
9861da177e4SLinus Torvalds 
9871da177e4SLinus Torvalds 	if (addr)
988c6d14c84SEric Dumazet 		goto out_unlock;
9899f9354b9SEric Dumazet no_in_dev:
9901da177e4SLinus Torvalds 
9911da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
9921da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
9931da177e4SLinus Torvalds 	   in dev_base list.
9941da177e4SLinus Torvalds 	 */
995c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
9969f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
9979f9354b9SEric Dumazet 		if (!in_dev)
9981da177e4SLinus Torvalds 			continue;
9991da177e4SLinus Torvalds 
10001da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
10011da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
10021da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
10031da177e4SLinus Torvalds 				addr = ifa->ifa_local;
1004c6d14c84SEric Dumazet 				goto out_unlock;
10051da177e4SLinus Torvalds 			}
10061da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
10071da177e4SLinus Torvalds 	}
1008c6d14c84SEric Dumazet out_unlock:
10091da177e4SLinus Torvalds 	rcu_read_unlock();
10101da177e4SLinus Torvalds 	return addr;
10111da177e4SLinus Torvalds }
10129f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
10131da177e4SLinus Torvalds 
101460cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
101560cad5daSAl Viro 			      __be32 local, int scope)
10161da177e4SLinus Torvalds {
10171da177e4SLinus Torvalds 	int same = 0;
1018a144ea4bSAl Viro 	__be32 addr = 0;
10191da177e4SLinus Torvalds 
10201da177e4SLinus Torvalds 	for_ifa(in_dev) {
10211da177e4SLinus Torvalds 		if (!addr &&
10221da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
10231da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
10241da177e4SLinus Torvalds 			addr = ifa->ifa_local;
10251da177e4SLinus Torvalds 			if (same)
10261da177e4SLinus Torvalds 				break;
10271da177e4SLinus Torvalds 		}
10281da177e4SLinus Torvalds 		if (!same) {
10291da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
10301da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
10311da177e4SLinus Torvalds 			if (same && addr) {
10321da177e4SLinus Torvalds 				if (local || !dst)
10331da177e4SLinus Torvalds 					break;
10341da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
10351da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
10361da177e4SLinus Torvalds 					break;
10371da177e4SLinus Torvalds 				/* No, then can we use new local src? */
10381da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
10391da177e4SLinus Torvalds 					addr = ifa->ifa_local;
10401da177e4SLinus Torvalds 					break;
10411da177e4SLinus Torvalds 				}
10421da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
10431da177e4SLinus Torvalds 				same = 0;
10441da177e4SLinus Torvalds 			}
10451da177e4SLinus Torvalds 		}
10461da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
10471da177e4SLinus Torvalds 
10481da177e4SLinus Torvalds 	return same ? addr : 0;
10491da177e4SLinus Torvalds }
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds /*
10521da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
10539bd85e32SDenis V. Lunev  * - in_dev: only on this interface, 0=any interface
10541da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
10551da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
10561da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
10571da177e4SLinus Torvalds  */
10589bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev,
10599bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
10601da177e4SLinus Torvalds {
106160cad5daSAl Viro 	__be32 addr = 0;
10629bd85e32SDenis V. Lunev 	struct net_device *dev;
106339a6d063SDenis V. Lunev 	struct net *net;
10641da177e4SLinus Torvalds 
106539a6d063SDenis V. Lunev 	if (scope != RT_SCOPE_LINK)
10669bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
10671da177e4SLinus Torvalds 
1068c346dca1SYOSHIFUJI Hideaki 	net = dev_net(in_dev->dev);
10691da177e4SLinus Torvalds 	rcu_read_lock();
1070c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
10719f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
10729f9354b9SEric Dumazet 		if (in_dev) {
10731da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
10741da177e4SLinus Torvalds 			if (addr)
10751da177e4SLinus Torvalds 				break;
10761da177e4SLinus Torvalds 		}
10771da177e4SLinus Torvalds 	}
10781da177e4SLinus Torvalds 	rcu_read_unlock();
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds 	return addr;
10811da177e4SLinus Torvalds }
1082eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds /*
10851da177e4SLinus Torvalds  *	Device notifier
10861da177e4SLinus Torvalds  */
10871da177e4SLinus Torvalds 
10881da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
10891da177e4SLinus Torvalds {
1090e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
10911da177e4SLinus Torvalds }
10929f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
10951da177e4SLinus Torvalds {
1096e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
10971da177e4SLinus Torvalds }
10989f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
10991da177e4SLinus Torvalds 
11009f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
11019f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
11021da177e4SLinus Torvalds */
11031da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
11041da177e4SLinus Torvalds {
11051da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
11061da177e4SLinus Torvalds 	int named = 0;
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
11091da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
11121da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11131da177e4SLinus Torvalds 		if (named++ == 0)
1114573bf470SThomas Graf 			goto skip;
111544344b2aSMark McLoughlin 		dot = strchr(old, ':');
11161da177e4SLinus Torvalds 		if (dot == NULL) {
11171da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
11181da177e4SLinus Torvalds 			dot = old;
11191da177e4SLinus Torvalds 		}
11209f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
11211da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
11229f9354b9SEric Dumazet 		else
11231da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1124573bf470SThomas Graf skip:
1125573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
11261da177e4SLinus Torvalds 	}
11271da177e4SLinus Torvalds }
11281da177e4SLinus Torvalds 
112906770843SBreno Leitao static inline bool inetdev_valid_mtu(unsigned mtu)
113006770843SBreno Leitao {
113106770843SBreno Leitao 	return mtu >= 68;
113206770843SBreno Leitao }
113306770843SBreno Leitao 
1134d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1135d11327adSIan Campbell 					struct in_device *in_dev)
1136d11327adSIan Campbell 
1137d11327adSIan Campbell {
1138b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1139d11327adSIan Campbell 
1140b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1141b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1142d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
11436c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
11446c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1145d11327adSIan Campbell 			 dev->dev_addr, NULL);
1146d11327adSIan Campbell 	}
1147b76d0789SZoltan Kiss }
1148d11327adSIan Campbell 
11491da177e4SLinus Torvalds /* Called only under RTNL semaphore */
11501da177e4SLinus Torvalds 
11511da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
11521da177e4SLinus Torvalds 			 void *ptr)
11531da177e4SLinus Torvalds {
11541da177e4SLinus Torvalds 	struct net_device *dev = ptr;
1155e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11561da177e4SLinus Torvalds 
11571da177e4SLinus Torvalds 	ASSERT_RTNL();
11581da177e4SLinus Torvalds 
11591da177e4SLinus Torvalds 	if (!in_dev) {
11608030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
11611da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
11628d76527eSHerbert Xu 			if (!in_dev)
1163b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
11640cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
116542f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
116642f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
11671da177e4SLinus Torvalds 			}
116806770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
116906770843SBreno Leitao 			/* Re-enabling IP */
117006770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
117106770843SBreno Leitao 				in_dev = inetdev_init(dev);
11728030f544SHerbert Xu 		}
11731da177e4SLinus Torvalds 		goto out;
11741da177e4SLinus Torvalds 	}
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 	switch (event) {
11771da177e4SLinus Torvalds 	case NETDEV_REGISTER:
11781da177e4SLinus Torvalds 		printk(KERN_DEBUG "inetdev_event: bug\n");
1179a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
11801da177e4SLinus Torvalds 		break;
11811da177e4SLinus Torvalds 	case NETDEV_UP:
118206770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
11831da177e4SLinus Torvalds 			break;
11840cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
11859f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
11869f9354b9SEric Dumazet 
11879f9354b9SEric Dumazet 			if (ifa) {
1188fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
11891da177e4SLinus Torvalds 				ifa->ifa_local =
11901da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
11911da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
11921da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
11931da177e4SLinus Torvalds 				in_dev_hold(in_dev);
11941da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
11951da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
11961da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11971da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
11981da177e4SLinus Torvalds 			}
11991da177e4SLinus Torvalds 		}
12001da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1201eefef1cfSStephen Hemminger 		/* fall through */
1202eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1203d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1204d11327adSIan Campbell 			break;
1205d11327adSIan Campbell 		/* fall through */
1206d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1207a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1208d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
12091da177e4SLinus Torvalds 		break;
12101da177e4SLinus Torvalds 	case NETDEV_DOWN:
12111da177e4SLinus Torvalds 		ip_mc_down(in_dev);
12121da177e4SLinus Torvalds 		break;
121393d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
121475c78500SMoni Shoua 		ip_mc_unmap(in_dev);
121575c78500SMoni Shoua 		break;
121693d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
121775c78500SMoni Shoua 		ip_mc_remap(in_dev);
121875c78500SMoni Shoua 		break;
12191da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
122006770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
12211da177e4SLinus Torvalds 			break;
122206770843SBreno Leitao 		/* disable IP when MTU is not enough */
12231da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
12241da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
12251da177e4SLinus Torvalds 		break;
12261da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
12271da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
12281da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
12291da177e4SLinus Torvalds 		 */
12301da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
12311da177e4SLinus Torvalds 
123251602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
123366f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
12341da177e4SLinus Torvalds 		break;
12351da177e4SLinus Torvalds 	}
12361da177e4SLinus Torvalds out:
12371da177e4SLinus Torvalds 	return NOTIFY_DONE;
12381da177e4SLinus Torvalds }
12391da177e4SLinus Torvalds 
12401da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
12411da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
12421da177e4SLinus Torvalds };
12431da177e4SLinus Torvalds 
1244339bf98fSThomas Graf static inline size_t inet_nlmsg_size(void)
1245339bf98fSThomas Graf {
1246339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1247339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1248339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1249339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1250339bf98fSThomas Graf 	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1251339bf98fSThomas Graf }
1252339bf98fSThomas Graf 
12531da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
1254b6544c0bSJamal Hadi Salim 			    u32 pid, u32 seq, int event, unsigned int flags)
12551da177e4SLinus Torvalds {
12561da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
12571da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
12581da177e4SLinus Torvalds 
125947f68512SThomas Graf 	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
126047f68512SThomas Graf 	if (nlh == NULL)
126126932566SPatrick McHardy 		return -EMSGSIZE;
126247f68512SThomas Graf 
126347f68512SThomas Graf 	ifm = nlmsg_data(nlh);
12641da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
12651da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
12661da177e4SLinus Torvalds 	ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
12671da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
12681da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
12691da177e4SLinus Torvalds 
1270f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1271f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1272f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1273f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) ||
1274f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1275f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1276f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
1277f3756b79SDavid S. Miller 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)))
1278f3756b79SDavid S. Miller 		goto nla_put_failure;
127947f68512SThomas Graf 
128047f68512SThomas Graf 	return nlmsg_end(skb, nlh);
128147f68512SThomas Graf 
128247f68512SThomas Graf nla_put_failure:
128326932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
128426932566SPatrick McHardy 	return -EMSGSIZE;
12851da177e4SLinus Torvalds }
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
12881da177e4SLinus Torvalds {
12893b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1290eec4df98SEric Dumazet 	int h, s_h;
1291eec4df98SEric Dumazet 	int idx, s_idx;
1292eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
12931da177e4SLinus Torvalds 	struct net_device *dev;
12941da177e4SLinus Torvalds 	struct in_device *in_dev;
12951da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1296eec4df98SEric Dumazet 	struct hlist_head *head;
1297eec4df98SEric Dumazet 	struct hlist_node *node;
12981da177e4SLinus Torvalds 
1299eec4df98SEric Dumazet 	s_h = cb->args[0];
1300eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1301eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1302eec4df98SEric Dumazet 
1303eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
13047562f876SPavel Emelianov 		idx = 0;
1305eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1306eec4df98SEric Dumazet 		rcu_read_lock();
1307eec4df98SEric Dumazet 		hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
13081da177e4SLinus Torvalds 			if (idx < s_idx)
13097562f876SPavel Emelianov 				goto cont;
13104b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
13111da177e4SLinus Torvalds 				s_ip_idx = 0;
1312eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
13139f9354b9SEric Dumazet 			if (!in_dev)
13147562f876SPavel Emelianov 				goto cont;
13151da177e4SLinus Torvalds 
13161da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
13171da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
13181da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1319596e4150SStephen Hemminger 					continue;
1320eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
1321eec4df98SEric Dumazet 					     NETLINK_CB(cb->skb).pid,
13221da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1323eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1324eec4df98SEric Dumazet 					rcu_read_unlock();
13251da177e4SLinus Torvalds 					goto done;
13261da177e4SLinus Torvalds 				}
1327eec4df98SEric Dumazet 			}
13287562f876SPavel Emelianov cont:
13297562f876SPavel Emelianov 			idx++;
13301da177e4SLinus Torvalds 		}
1331eec4df98SEric Dumazet 		rcu_read_unlock();
1332eec4df98SEric Dumazet 	}
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds done:
1335eec4df98SEric Dumazet 	cb->args[0] = h;
1336eec4df98SEric Dumazet 	cb->args[1] = idx;
1337eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds 	return skb->len;
13401da177e4SLinus Torvalds }
13411da177e4SLinus Torvalds 
1342d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
1343d6062cbbSThomas Graf 		      u32 pid)
13441da177e4SLinus Torvalds {
134547f68512SThomas Graf 	struct sk_buff *skb;
1346d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1347d6062cbbSThomas Graf 	int err = -ENOBUFS;
13484b8aa9abSDenis V. Lunev 	struct net *net;
13491da177e4SLinus Torvalds 
1350c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1351339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
135247f68512SThomas Graf 	if (skb == NULL)
1353d6062cbbSThomas Graf 		goto errout;
1354d6062cbbSThomas Graf 
1355d6062cbbSThomas Graf 	err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
135626932566SPatrick McHardy 	if (err < 0) {
135726932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
135826932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
135926932566SPatrick McHardy 		kfree_skb(skb);
136026932566SPatrick McHardy 		goto errout;
136126932566SPatrick McHardy 	}
13621ce85fe4SPablo Neira Ayuso 	rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
13631ce85fe4SPablo Neira Ayuso 	return;
1364d6062cbbSThomas Graf errout:
1365d6062cbbSThomas Graf 	if (err < 0)
13664b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
13671da177e4SLinus Torvalds }
13681da177e4SLinus Torvalds 
13699f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
13709f0f7272SThomas Graf {
13711fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
13729f0f7272SThomas Graf 
13739f0f7272SThomas Graf 	if (!in_dev)
13749f0f7272SThomas Graf 		return 0;
13759f0f7272SThomas Graf 
13769f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
13779f0f7272SThomas Graf }
13789f0f7272SThomas Graf 
13799f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
13809f0f7272SThomas Graf {
13811fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
13829f0f7272SThomas Graf 	struct nlattr *nla;
13839f0f7272SThomas Graf 	int i;
13849f0f7272SThomas Graf 
13859f0f7272SThomas Graf 	if (!in_dev)
13869f0f7272SThomas Graf 		return -ENODATA;
13879f0f7272SThomas Graf 
13889f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
13899f0f7272SThomas Graf 	if (nla == NULL)
13909f0f7272SThomas Graf 		return -EMSGSIZE;
13919f0f7272SThomas Graf 
13929f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
13939f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
13949f0f7272SThomas Graf 
13959f0f7272SThomas Graf 	return 0;
13969f0f7272SThomas Graf }
13979f0f7272SThomas Graf 
13989f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
13999f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
14009f0f7272SThomas Graf };
14019f0f7272SThomas Graf 
1402cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1403cf7afbfeSThomas Graf 				 const struct nlattr *nla)
14049f0f7272SThomas Graf {
14059f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
14069f0f7272SThomas Graf 	int err, rem;
14079f0f7272SThomas Graf 
1408f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1409cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
14109f0f7272SThomas Graf 
14119f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
14129f0f7272SThomas Graf 	if (err < 0)
14139f0f7272SThomas Graf 		return err;
14149f0f7272SThomas Graf 
14159f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14169f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
14179f0f7272SThomas Graf 			int cfgid = nla_type(a);
14189f0f7272SThomas Graf 
14199f0f7272SThomas Graf 			if (nla_len(a) < 4)
14209f0f7272SThomas Graf 				return -EINVAL;
14219f0f7272SThomas Graf 
14229f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
14239f0f7272SThomas Graf 				return -EINVAL;
14249f0f7272SThomas Graf 		}
14259f0f7272SThomas Graf 	}
14269f0f7272SThomas Graf 
1427cf7afbfeSThomas Graf 	return 0;
1428cf7afbfeSThomas Graf }
1429cf7afbfeSThomas Graf 
1430cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1431cf7afbfeSThomas Graf {
1432f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1433cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1434cf7afbfeSThomas Graf 	int rem;
1435cf7afbfeSThomas Graf 
1436cf7afbfeSThomas Graf 	if (!in_dev)
1437cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1438cf7afbfeSThomas Graf 
1439cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1440cf7afbfeSThomas Graf 		BUG();
1441cf7afbfeSThomas Graf 
14429f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14439f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
14449f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
14459f0f7272SThomas Graf 	}
14469f0f7272SThomas Graf 
14479f0f7272SThomas Graf 	return 0;
14489f0f7272SThomas Graf }
14499f0f7272SThomas Graf 
14501da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
14511da177e4SLinus Torvalds 
1452c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
145331be3085SHerbert Xu {
145431be3085SHerbert Xu 	struct net_device *dev;
145531be3085SHerbert Xu 
145631be3085SHerbert Xu 	rcu_read_lock();
1457c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1458c6d14c84SEric Dumazet 		struct in_device *in_dev;
1459c6d14c84SEric Dumazet 
146031be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
146131be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
14629355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1463c6d14c84SEric Dumazet 	}
146431be3085SHerbert Xu 	rcu_read_unlock();
146531be3085SHerbert Xu }
146631be3085SHerbert Xu 
1467c6d14c84SEric Dumazet /* called with RTNL locked */
1468c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
146968dd299bSPavel Emelyanov {
147068dd299bSPavel Emelyanov 	struct net_device *dev;
1471586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
147268dd299bSPavel Emelyanov 
1473586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
14749355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
147568dd299bSPavel Emelyanov 
1476c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
147768dd299bSPavel Emelyanov 		struct in_device *in_dev;
14780187bdfbSBen Hutchings 		if (on)
14790187bdfbSBen Hutchings 			dev_disable_lro(dev);
148068dd299bSPavel Emelyanov 		rcu_read_lock();
148168dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
148268dd299bSPavel Emelyanov 		if (in_dev)
148368dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
148468dd299bSPavel Emelyanov 		rcu_read_unlock();
148568dd299bSPavel Emelyanov 	}
148668dd299bSPavel Emelyanov }
148768dd299bSPavel Emelyanov 
148831be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write,
14898d65af78SAlexey Dobriyan 			     void __user *buffer,
149031be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
149131be3085SHerbert Xu {
1492d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
14938d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
1494d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
149531be3085SHerbert Xu 
149631be3085SHerbert Xu 	if (write) {
149731be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1498c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
149931be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
150031be3085SHerbert Xu 
150131be3085SHerbert Xu 		set_bit(i, cnf->state);
150231be3085SHerbert Xu 
15039355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1504c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
1505d01ff0a0SPeter Pan(潘卫平) 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1)
1506d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
1507d01ff0a0SPeter Pan(潘卫平) 				rt_cache_flush(net, 0);
150831be3085SHerbert Xu 	}
150931be3085SHerbert Xu 
151031be3085SHerbert Xu 	return ret;
151131be3085SHerbert Xu }
151231be3085SHerbert Xu 
15131da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write,
15148d65af78SAlexey Dobriyan 				  void __user *buffer,
15151da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
15161da177e4SLinus Torvalds {
15171da177e4SLinus Torvalds 	int *valp = ctl->data;
15181da177e4SLinus Torvalds 	int val = *valp;
151988af182eSEric W. Biederman 	loff_t pos = *ppos;
15208d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
15211da177e4SLinus Torvalds 
15221da177e4SLinus Torvalds 	if (write && *valp != val) {
1523c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
1524c0ce9fb3SPavel Emelyanov 
15250187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
152688af182eSEric W. Biederman 			if (!rtnl_trylock()) {
152788af182eSEric W. Biederman 				/* Restore the original values before restarting */
152888af182eSEric W. Biederman 				*valp = val;
152988af182eSEric W. Biederman 				*ppos = pos;
15309b8adb5eSEric W. Biederman 				return restart_syscall();
153188af182eSEric W. Biederman 			}
15320187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1533c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
15340187bdfbSBen Hutchings 			} else if (*valp) {
15350187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
15360187bdfbSBen Hutchings 				struct in_device *idev =
15370187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
15380187bdfbSBen Hutchings 				dev_disable_lro(idev->dev);
15390187bdfbSBen Hutchings 			}
15400187bdfbSBen Hutchings 			rtnl_unlock();
154176e6ebfbSDenis V. Lunev 			rt_cache_flush(net, 0);
15421da177e4SLinus Torvalds 		}
15430187bdfbSBen Hutchings 	}
15441da177e4SLinus Torvalds 
15451da177e4SLinus Torvalds 	return ret;
15461da177e4SLinus Torvalds }
15471da177e4SLinus Torvalds 
1548323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write,
15498d65af78SAlexey Dobriyan 				void __user *buffer,
15501da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
15511da177e4SLinus Torvalds {
15521da177e4SLinus Torvalds 	int *valp = ctl->data;
15531da177e4SLinus Torvalds 	int val = *valp;
15548d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
155576e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
15561da177e4SLinus Torvalds 
15571da177e4SLinus Torvalds 	if (write && *valp != val)
155876e6ebfbSDenis V. Lunev 		rt_cache_flush(net, 0);
15591da177e4SLinus Torvalds 
15601da177e4SLinus Torvalds 	return ret;
15611da177e4SLinus Torvalds }
15621da177e4SLinus Torvalds 
1563f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
156442f811b8SHerbert Xu 	{ \
156542f811b8SHerbert Xu 		.procname	= name, \
156642f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
156702291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
156842f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
156942f811b8SHerbert Xu 		.mode		= mval, \
157042f811b8SHerbert Xu 		.proc_handler	= proc, \
157131be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
157242f811b8SHerbert Xu 	}
157342f811b8SHerbert Xu 
157442f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
1575f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
157642f811b8SHerbert Xu 
157742f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
1578f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
157942f811b8SHerbert Xu 
1580f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1581f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
158242f811b8SHerbert Xu 
158342f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
1584f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
158542f811b8SHerbert Xu 
15861da177e4SLinus Torvalds static struct devinet_sysctl_table {
15871da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
158802291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
1589bfada697SPavel Emelyanov 	char *dev_name;
15901da177e4SLinus Torvalds } devinet_sysctl = {
15911da177e4SLinus Torvalds 	.devinet_vars = {
159242f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
1593f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
159442f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
159542f811b8SHerbert Xu 
159642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
159742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
159842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
159942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
160042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
160142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
160242f811b8SHerbert Xu 					"accept_source_route"),
16038153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
160428f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
160542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
160642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
160742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
160842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
160942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
161042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
161142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
161242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
161342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
1614eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
161565324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
161642f811b8SHerbert Xu 
161742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
161842f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
161942f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
162042f811b8SHerbert Xu 					      "force_igmp_version"),
162142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
162242f811b8SHerbert Xu 					      "promote_secondaries"),
16231da177e4SLinus Torvalds 	},
16241da177e4SLinus Torvalds };
16251da177e4SLinus Torvalds 
1626ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
1627f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
16281da177e4SLinus Torvalds {
16291da177e4SLinus Torvalds 	int i;
16309fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
16311da177e4SLinus Torvalds 
1632bfada697SPavel Emelyanov #define DEVINET_CTL_PATH_DEV	3
1633bfada697SPavel Emelyanov 
1634bfada697SPavel Emelyanov 	struct ctl_path devinet_ctl_path[] = {
1635f8572d8fSEric W. Biederman 		{ .procname = "net",  },
1636f8572d8fSEric W. Biederman 		{ .procname = "ipv4", },
1637f8572d8fSEric W. Biederman 		{ .procname = "conf", },
1638bfada697SPavel Emelyanov 		{ /* to be set */ },
1639bfada697SPavel Emelyanov 		{ },
1640bfada697SPavel Emelyanov 	};
1641bfada697SPavel Emelyanov 
16429fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
16431da177e4SLinus Torvalds 	if (!t)
16449fa89642SPavel Emelyanov 		goto out;
16459fa89642SPavel Emelyanov 
16461da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
16471da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
164831be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
1649c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
16501da177e4SLinus Torvalds 	}
16511da177e4SLinus Torvalds 
16521da177e4SLinus Torvalds 	/*
16531da177e4SLinus Torvalds 	 * Make a copy of dev_name, because '.procname' is regarded as const
16541da177e4SLinus Torvalds 	 * by sysctl and we wouldn't want anyone to change it under our feet
16551da177e4SLinus Torvalds 	 * (see SIOCSIFNAME).
16561da177e4SLinus Torvalds 	 */
1657bfada697SPavel Emelyanov 	t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1658bfada697SPavel Emelyanov 	if (!t->dev_name)
16591da177e4SLinus Torvalds 		goto free;
16601da177e4SLinus Torvalds 
1661bfada697SPavel Emelyanov 	devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
16621da177e4SLinus Torvalds 
1663752d14dcSPavel Emelyanov 	t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
1664bfada697SPavel Emelyanov 			t->devinet_vars);
16651da177e4SLinus Torvalds 	if (!t->sysctl_header)
16661da177e4SLinus Torvalds 		goto free_procname;
16671da177e4SLinus Torvalds 
16681da177e4SLinus Torvalds 	p->sysctl = t;
1669ea40b324SPavel Emelyanov 	return 0;
16701da177e4SLinus Torvalds 
16711da177e4SLinus Torvalds free_procname:
1672bfada697SPavel Emelyanov 	kfree(t->dev_name);
16731da177e4SLinus Torvalds free:
16741da177e4SLinus Torvalds 	kfree(t);
16759fa89642SPavel Emelyanov out:
1676ea40b324SPavel Emelyanov 	return -ENOBUFS;
16771da177e4SLinus Torvalds }
16781da177e4SLinus Torvalds 
167951602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
168066f27a52SPavel Emelyanov {
168151602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
168266f27a52SPavel Emelyanov 
168351602b2aSPavel Emelyanov 	if (t == NULL)
168451602b2aSPavel Emelyanov 		return;
168551602b2aSPavel Emelyanov 
168651602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
1687ff538818SLucian Adrian Grijincu 	unregister_net_sysctl_table(t->sysctl_header);
1688bfada697SPavel Emelyanov 	kfree(t->dev_name);
16891da177e4SLinus Torvalds 	kfree(t);
16901da177e4SLinus Torvalds }
169151602b2aSPavel Emelyanov 
169251602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
169351602b2aSPavel Emelyanov {
169454716e3bSEric W. Biederman 	neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
1695c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
1696f8572d8fSEric W. Biederman 					&idev->cnf);
169751602b2aSPavel Emelyanov }
169851602b2aSPavel Emelyanov 
169951602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
170051602b2aSPavel Emelyanov {
170151602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
170251602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
17031da177e4SLinus Torvalds }
17041da177e4SLinus Torvalds 
170568dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
170668dd299bSPavel Emelyanov 	{
170768dd299bSPavel Emelyanov 		.procname	= "ip_forward",
170868dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
170902291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
171068dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
171168dd299bSPavel Emelyanov 		.mode		= 0644,
171268dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
171368dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
1714c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
171568dd299bSPavel Emelyanov 	},
171668dd299bSPavel Emelyanov 	{ },
171768dd299bSPavel Emelyanov };
171868dd299bSPavel Emelyanov 
1719752d14dcSPavel Emelyanov static __net_initdata struct ctl_path net_ipv4_path[] = {
1720f8572d8fSEric W. Biederman 	{ .procname = "net", },
1721f8572d8fSEric W. Biederman 	{ .procname = "ipv4", },
172268dd299bSPavel Emelyanov 	{ },
172368dd299bSPavel Emelyanov };
17242a75de0cSEric Dumazet #endif
172568dd299bSPavel Emelyanov 
1726752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
1727752d14dcSPavel Emelyanov {
1728752d14dcSPavel Emelyanov 	int err;
1729752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
17302a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
17312a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
1732752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
17332a75de0cSEric Dumazet #endif
1734752d14dcSPavel Emelyanov 
1735752d14dcSPavel Emelyanov 	err = -ENOMEM;
1736752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
1737752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
1738752d14dcSPavel Emelyanov 
173909ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
1740752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1741752d14dcSPavel Emelyanov 		if (all == NULL)
1742752d14dcSPavel Emelyanov 			goto err_alloc_all;
1743752d14dcSPavel Emelyanov 
1744752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1745752d14dcSPavel Emelyanov 		if (dflt == NULL)
1746752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
1747752d14dcSPavel Emelyanov 
17482a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1749752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1750752d14dcSPavel Emelyanov 		if (tbl == NULL)
1751752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
1752752d14dcSPavel Emelyanov 
175302291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
1754752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
1755752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
17562a75de0cSEric Dumazet #endif
1757752d14dcSPavel Emelyanov 	}
1758752d14dcSPavel Emelyanov 
1759752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1760f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
1761752d14dcSPavel Emelyanov 	if (err < 0)
1762752d14dcSPavel Emelyanov 		goto err_reg_all;
1763752d14dcSPavel Emelyanov 
1764f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
1765752d14dcSPavel Emelyanov 	if (err < 0)
1766752d14dcSPavel Emelyanov 		goto err_reg_dflt;
1767752d14dcSPavel Emelyanov 
1768752d14dcSPavel Emelyanov 	err = -ENOMEM;
1769752d14dcSPavel Emelyanov 	forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1770752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
1771752d14dcSPavel Emelyanov 		goto err_reg_ctl;
17722a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
1773752d14dcSPavel Emelyanov #endif
1774752d14dcSPavel Emelyanov 
1775752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
1776752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
1777752d14dcSPavel Emelyanov 	return 0;
1778752d14dcSPavel Emelyanov 
1779752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1780752d14dcSPavel Emelyanov err_reg_ctl:
1781752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
1782752d14dcSPavel Emelyanov err_reg_dflt:
1783752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
1784752d14dcSPavel Emelyanov err_reg_all:
1785752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
1786752d14dcSPavel Emelyanov 		kfree(tbl);
1787752d14dcSPavel Emelyanov err_alloc_ctl:
17882a75de0cSEric Dumazet #endif
1789752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
1790752d14dcSPavel Emelyanov 		kfree(dflt);
1791752d14dcSPavel Emelyanov err_alloc_dflt:
1792752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
1793752d14dcSPavel Emelyanov 		kfree(all);
1794752d14dcSPavel Emelyanov err_alloc_all:
1795752d14dcSPavel Emelyanov 	return err;
1796752d14dcSPavel Emelyanov }
1797752d14dcSPavel Emelyanov 
1798752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
1799752d14dcSPavel Emelyanov {
18002a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1801752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
1802752d14dcSPavel Emelyanov 
1803752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
1804752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
1805752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1806752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
1807752d14dcSPavel Emelyanov 	kfree(tbl);
18082a75de0cSEric Dumazet #endif
1809752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
1810752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
1811752d14dcSPavel Emelyanov }
1812752d14dcSPavel Emelyanov 
1813752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
1814752d14dcSPavel Emelyanov 	.init = devinet_init_net,
1815752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
1816752d14dcSPavel Emelyanov };
1817752d14dcSPavel Emelyanov 
18189f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
18199f0f7272SThomas Graf 	.family		  = AF_INET,
18209f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
18219f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
1822cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
1823cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
18249f0f7272SThomas Graf };
18259f0f7272SThomas Graf 
18261da177e4SLinus Torvalds void __init devinet_init(void)
18271da177e4SLinus Torvalds {
1828fd23c3b3SDavid S. Miller 	int i;
1829fd23c3b3SDavid S. Miller 
1830fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
1831fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
1832fd23c3b3SDavid S. Miller 
1833752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
1834752d14dcSPavel Emelyanov 
18351da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
18361da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
183763f3444fSThomas Graf 
18389f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
18399f0f7272SThomas Graf 
1840c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
1841c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
1842c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
18431da177e4SLinus Torvalds }
18441da177e4SLinus Torvalds 
1845