xref: /linux/net/ipv4/devinet.c (revision d67b8c616b48df30e2836d797795f2420d109bc9)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	NET3	IP device support routines.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
51da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
61da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
71da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *	Derived from the IP parts of dev.c 1.0.19
1002c30a84SJesper Juhl  * 		Authors:	Ross Biro
111da177e4SLinus Torvalds  *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
121da177e4SLinus Torvalds  *				Mark Evans, <evansmp@uhura.aston.ac.uk>
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *	Additional Authors:
151da177e4SLinus Torvalds  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
161da177e4SLinus Torvalds  *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *	Changes:
191da177e4SLinus Torvalds  *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
201da177e4SLinus Torvalds  *					lists.
211da177e4SLinus Torvalds  *		Cyrus Durgin:		updated for kmod
221da177e4SLinus Torvalds  *		Matthias Andree:	in devinet_ioctl, compare label and
231da177e4SLinus Torvalds  *					address (4.4BSD alias style support),
241da177e4SLinus Torvalds  *					fall back to comparing just the label
251da177e4SLinus Torvalds  *					if no match found.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <asm/uaccess.h>
301da177e4SLinus Torvalds #include <linux/bitops.h>
314fc268d2SRandy Dunlap #include <linux/capability.h>
321da177e4SLinus Torvalds #include <linux/module.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/kernel.h>
351da177e4SLinus Torvalds #include <linux/string.h>
361da177e4SLinus Torvalds #include <linux/mm.h>
371da177e4SLinus Torvalds #include <linux/socket.h>
381da177e4SLinus Torvalds #include <linux/sockios.h>
391da177e4SLinus Torvalds #include <linux/in.h>
401da177e4SLinus Torvalds #include <linux/errno.h>
411da177e4SLinus Torvalds #include <linux/interrupt.h>
421823730fSThomas Graf #include <linux/if_addr.h>
431da177e4SLinus Torvalds #include <linux/if_ether.h>
441da177e4SLinus Torvalds #include <linux/inet.h>
451da177e4SLinus Torvalds #include <linux/netdevice.h>
461da177e4SLinus Torvalds #include <linux/etherdevice.h>
471da177e4SLinus Torvalds #include <linux/skbuff.h>
481da177e4SLinus Torvalds #include <linux/init.h>
491da177e4SLinus Torvalds #include <linux/notifier.h>
501da177e4SLinus Torvalds #include <linux/inetdevice.h>
511da177e4SLinus Torvalds #include <linux/igmp.h>
525a0e3ad6STejun Heo #include <linux/slab.h>
53fd23c3b3SDavid S. Miller #include <linux/hash.h>
541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
551da177e4SLinus Torvalds #include <linux/sysctl.h>
561da177e4SLinus Torvalds #endif
571da177e4SLinus Torvalds #include <linux/kmod.h>
58edc9e748SNicolas Dichtel #include <linux/netconf.h>
591da177e4SLinus Torvalds 
6014c85021SArnaldo Carvalho de Melo #include <net/arp.h>
611da177e4SLinus Torvalds #include <net/ip.h>
621da177e4SLinus Torvalds #include <net/route.h>
631da177e4SLinus Torvalds #include <net/ip_fib.h>
6463f3444fSThomas Graf #include <net/rtnetlink.h>
65752d14dcSPavel Emelyanov #include <net/net_namespace.h>
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 
9840384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
9940384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
10040384999SEric Dumazet 
101fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
102fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock);
103fd23c3b3SDavid S. Miller 
10440384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr)
105fd23c3b3SDavid S. Miller {
10640384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
107fd23c3b3SDavid S. Miller 
10840384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
109fd23c3b3SDavid S. Miller }
110fd23c3b3SDavid S. Miller 
111fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
112fd23c3b3SDavid S. Miller {
11340384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
114fd23c3b3SDavid S. Miller 
115fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
116fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
117fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
118fd23c3b3SDavid S. Miller }
119fd23c3b3SDavid S. Miller 
120fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
121fd23c3b3SDavid S. Miller {
122fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
123fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
124fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
125fd23c3b3SDavid S. Miller }
126fd23c3b3SDavid S. Miller 
1279435eb1cSDavid S. Miller /**
1289435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1299435eb1cSDavid S. Miller  * @net: the net namespace
1309435eb1cSDavid S. Miller  * @addr: the source address
1319435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1329435eb1cSDavid S. Miller  *
1339435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1349435eb1cSDavid S. Miller  */
1359435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1369435eb1cSDavid S. Miller {
13740384999SEric Dumazet 	u32 hash = inet_addr_hash(net, addr);
1389435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1399435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1409435eb1cSDavid S. Miller 	struct hlist_node *node;
1419435eb1cSDavid S. Miller 
1429435eb1cSDavid S. Miller 	rcu_read_lock();
1439435eb1cSDavid S. Miller 	hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
14440384999SEric Dumazet 		if (ifa->ifa_local == addr) {
1459435eb1cSDavid S. Miller 			struct net_device *dev = ifa->ifa_dev->dev;
1469435eb1cSDavid S. Miller 
1479435eb1cSDavid S. Miller 			if (!net_eq(dev_net(dev), net))
1489435eb1cSDavid S. Miller 				continue;
1499435eb1cSDavid S. Miller 			result = dev;
1509435eb1cSDavid S. Miller 			break;
1519435eb1cSDavid S. Miller 		}
1529435eb1cSDavid S. Miller 	}
153406b6f97SDavid S. Miller 	if (!result) {
154406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
155406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
156406b6f97SDavid S. Miller 		struct fib_table *local;
157406b6f97SDavid S. Miller 
158406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
159406b6f97SDavid S. Miller 		 * over loopback subnets work.
160406b6f97SDavid S. Miller 		 */
161406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
162406b6f97SDavid S. Miller 		if (local &&
163406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
164406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
165406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
166406b6f97SDavid S. Miller 	}
1679435eb1cSDavid S. Miller 	if (result && devref)
1689435eb1cSDavid S. Miller 		dev_hold(result);
1699435eb1cSDavid S. Miller 	rcu_read_unlock();
1709435eb1cSDavid S. Miller 	return result;
1719435eb1cSDavid S. Miller }
1729435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1739435eb1cSDavid S. Miller 
174d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1751da177e4SLinus Torvalds 
176e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1771da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1781da177e4SLinus Torvalds 			 int destroy);
1791da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
18066f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev);
18151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
18251602b2aSPavel Emelyanov #else
18340384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev)
18451602b2aSPavel Emelyanov {
18551602b2aSPavel Emelyanov }
18640384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
18751602b2aSPavel Emelyanov {
18851602b2aSPavel Emelyanov }
1891da177e4SLinus Torvalds #endif
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds /* Locks all the inet devices. */
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1941da177e4SLinus Torvalds {
19593adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
1991da177e4SLinus Torvalds {
2001da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2011da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2021da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2031da177e4SLinus Torvalds 	kfree(ifa);
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds 
20640384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2071da177e4SLinus Torvalds {
2081da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2121da177e4SLinus Torvalds {
2131da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2141da177e4SLinus Torvalds 
215547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
216547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2171da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
21891df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2191da177e4SLinus Torvalds #endif
2201da177e4SLinus Torvalds 	dev_put(dev);
2211da177e4SLinus Torvalds 	if (!idev->dead)
2229f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2239f9354b9SEric Dumazet 	else
2241da177e4SLinus Torvalds 		kfree(idev);
2251da177e4SLinus Torvalds }
2269f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2271da177e4SLinus Torvalds 
22871e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	struct in_device *in_dev;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 	ASSERT_RTNL();
2331da177e4SLinus Torvalds 
2340da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2351da177e4SLinus Torvalds 	if (!in_dev)
2361da177e4SLinus Torvalds 		goto out;
237c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2389355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2391da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2401da177e4SLinus Torvalds 	in_dev->dev = dev;
2419f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2429f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2431da177e4SLinus Torvalds 		goto out_kfree;
2440187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2450187bdfbSBen Hutchings 		dev_disable_lro(dev);
2461da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2471da177e4SLinus Torvalds 	dev_hold(dev);
24830c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2491da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2501da177e4SLinus Torvalds 
25166f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2521da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2531da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2541da177e4SLinus Torvalds 		ip_mc_up(in_dev);
255483479ecSJarek Poplawski 
25630c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
257cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
258483479ecSJarek Poplawski out:
2591da177e4SLinus Torvalds 	return in_dev;
2601da177e4SLinus Torvalds out_kfree:
2611da177e4SLinus Torvalds 	kfree(in_dev);
2621da177e4SLinus Torvalds 	in_dev = NULL;
2631da177e4SLinus Torvalds 	goto out;
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2671da177e4SLinus Torvalds {
2681da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2691da177e4SLinus Torvalds 	in_dev_put(idev);
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2731da177e4SLinus Torvalds {
2741da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2751da177e4SLinus Torvalds 	struct net_device *dev;
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	ASSERT_RTNL();
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 	dev = in_dev->dev;
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	in_dev->dead = 1;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2861da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2871da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2881da177e4SLinus Torvalds 	}
2891da177e4SLinus Torvalds 
290a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
2911da177e4SLinus Torvalds 
29251602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
2931da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
2941da177e4SLinus Torvalds 	arp_ifdown(dev);
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds 
299ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3001da177e4SLinus Torvalds {
3011da177e4SLinus Torvalds 	rcu_read_lock();
3021da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3031da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3041da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3051da177e4SLinus Torvalds 				rcu_read_unlock();
3061da177e4SLinus Torvalds 				return 1;
3071da177e4SLinus Torvalds 			}
3081da177e4SLinus Torvalds 		}
3091da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3101da177e4SLinus Torvalds 	rcu_read_unlock();
3111da177e4SLinus Torvalds 	return 0;
3121da177e4SLinus Torvalds }
3131da177e4SLinus Torvalds 
314d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
31515e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3161da177e4SLinus Torvalds {
3178f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3180ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3190ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3200ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3210ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	ASSERT_RTNL();
3241da177e4SLinus Torvalds 
3258f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3268f937c60SHarald Welte 	 * unless alias promotion is set
3278f937c60SHarald Welte 	 **/
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3301da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3330ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3340ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3350ff60a45SJamal Hadi Salim 				last_prim = ifa;
3360ff60a45SJamal Hadi Salim 
3371da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3381da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3391da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3401da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3410ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3421da177e4SLinus Torvalds 				continue;
3431da177e4SLinus Torvalds 			}
3441da177e4SLinus Torvalds 
3450ff60a45SJamal Hadi Salim 			if (!do_promote) {
346fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3471da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3481da177e4SLinus Torvalds 
34915e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
350e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
351e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3521da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3538f937c60SHarald Welte 			} else {
3548f937c60SHarald Welte 				promote = ifa;
3558f937c60SHarald Welte 				break;
3568f937c60SHarald Welte 			}
3571da177e4SLinus Torvalds 		}
3581da177e4SLinus Torvalds 	}
3591da177e4SLinus Torvalds 
3602d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3612d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3622d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3632d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3642d230e2bSJulian Anastasov 	 */
3652d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3662d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3672d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3682d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3692d230e2bSJulian Anastasov 	}
3702d230e2bSJulian Anastasov 
3711da177e4SLinus Torvalds 	/* 2. Unlink it */
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
374fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3791da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3801da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3811da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3821da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3831da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3841da177e4SLinus Torvalds 	   So that, this order is correct.
3851da177e4SLinus Torvalds 	 */
38615e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
387e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3880ff60a45SJamal Hadi Salim 
3890ff60a45SJamal Hadi Salim 	if (promote) {
39004024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
3910ff60a45SJamal Hadi Salim 
3920ff60a45SJamal Hadi Salim 		if (prev_prom) {
3930ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
3940ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
3950ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
3960ff60a45SJamal Hadi Salim 		}
3970ff60a45SJamal Hadi Salim 
3980ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
39915e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
400e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
401e041c683SAlan Stern 				NETDEV_UP, promote);
40204024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4030ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4040ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4050ff60a45SJamal Hadi Salim 					continue;
4060ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4070ff60a45SJamal Hadi Salim 		}
4080ff60a45SJamal Hadi Salim 
4090ff60a45SJamal Hadi Salim 	}
4106363097cSHerbert Xu 	if (destroy)
4111da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds 
414d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
415d6062cbbSThomas Graf 			 int destroy)
416d6062cbbSThomas Graf {
417d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
418d6062cbbSThomas Graf }
419d6062cbbSThomas Graf 
420d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
42115e47304SEric W. Biederman 			     u32 portid)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4241da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	ASSERT_RTNL();
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4291da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4301da177e4SLinus Torvalds 		return 0;
4311da177e4SLinus Torvalds 	}
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4341da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4371da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4381da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4391da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4401da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4411da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4421da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4431da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4441da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4451da177e4SLinus Torvalds 				return -EEXIST;
4461da177e4SLinus Torvalds 			}
4471da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4481da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4491da177e4SLinus Torvalds 				return -EINVAL;
4501da177e4SLinus Torvalds 			}
4511da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4521da177e4SLinus Torvalds 		}
4531da177e4SLinus Torvalds 	}
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
4561da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4571da177e4SLinus Torvalds 		ifap = last_primary;
4581da177e4SLinus Torvalds 	}
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4611da177e4SLinus Torvalds 	*ifap = ifa;
4621da177e4SLinus Torvalds 
463fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
464fd23c3b3SDavid S. Miller 
4651da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4661da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4671da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
46815e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
469e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 	return 0;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
474d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
475d6062cbbSThomas Graf {
476d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
477d6062cbbSThomas Graf }
478d6062cbbSThomas Graf 
4791da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4801da177e4SLinus Torvalds {
481e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	ASSERT_RTNL();
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	if (!in_dev) {
4861da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4871da177e4SLinus Torvalds 		return -ENOBUFS;
4881da177e4SLinus Torvalds 	}
48971e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
4901da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
491547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
4921da177e4SLinus Torvalds 		in_dev_hold(in_dev);
4931da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
4941da177e4SLinus Torvalds 	}
495f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
4961da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
4971da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
4981da177e4SLinus Torvalds }
4991da177e4SLinus Torvalds 
5008723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5018723e1b4SEric Dumazet  * We dont take a reference on found in_device
5028723e1b4SEric Dumazet  */
5037fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5041da177e4SLinus Torvalds {
5051da177e4SLinus Torvalds 	struct net_device *dev;
5061da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
507c148fc2eSEric Dumazet 
508c148fc2eSEric Dumazet 	rcu_read_lock();
509c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5101da177e4SLinus Torvalds 	if (dev)
5118723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
512c148fc2eSEric Dumazet 	rcu_read_unlock();
5131da177e4SLinus Torvalds 	return in_dev;
5141da177e4SLinus Torvalds }
5159f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5181da177e4SLinus Torvalds 
51960cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
52060cad5daSAl Viro 				    __be32 mask)
5211da177e4SLinus Torvalds {
5221da177e4SLinus Torvalds 	ASSERT_RTNL();
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5251da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5261da177e4SLinus Torvalds 			return ifa;
5271da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5281da177e4SLinus Torvalds 	return NULL;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
5321da177e4SLinus Torvalds {
5333b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
534dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5351da177e4SLinus Torvalds 	struct in_device *in_dev;
536dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5371da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
538dfdd5fd4SThomas Graf 	int err = -EINVAL;
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 	ASSERT_RTNL();
5411da177e4SLinus Torvalds 
542dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
543dfdd5fd4SThomas Graf 	if (err < 0)
544dfdd5fd4SThomas Graf 		goto errout;
545dfdd5fd4SThomas Graf 
546dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5477fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
548dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
549dfdd5fd4SThomas Graf 		err = -ENODEV;
550dfdd5fd4SThomas Graf 		goto errout;
551dfdd5fd4SThomas Graf 	}
552dfdd5fd4SThomas Graf 
5531da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5541da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
555dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
556a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5571da177e4SLinus Torvalds 			continue;
558dfdd5fd4SThomas Graf 
559dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
560dfdd5fd4SThomas Graf 			continue;
561dfdd5fd4SThomas Graf 
562dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
563dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
564a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
565dfdd5fd4SThomas Graf 			continue;
566dfdd5fd4SThomas Graf 
56715e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
5681da177e4SLinus Torvalds 		return 0;
5691da177e4SLinus Torvalds 	}
570dfdd5fd4SThomas Graf 
571dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
572dfdd5fd4SThomas Graf errout:
573dfdd5fd4SThomas Graf 	return err;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
5764b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
5771da177e4SLinus Torvalds {
5785c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5795c753978SThomas Graf 	struct in_ifaddr *ifa;
5805c753978SThomas Graf 	struct ifaddrmsg *ifm;
5811da177e4SLinus Torvalds 	struct net_device *dev;
5821da177e4SLinus Torvalds 	struct in_device *in_dev;
5837b218574SDenis V. Lunev 	int err;
5841da177e4SLinus Torvalds 
5855c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
5865c753978SThomas Graf 	if (err < 0)
5875c753978SThomas Graf 		goto errout;
5881da177e4SLinus Torvalds 
5895c753978SThomas Graf 	ifm = nlmsg_data(nlh);
590c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
5917b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
5925c753978SThomas Graf 		goto errout;
5931da177e4SLinus Torvalds 
5944b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
5955c753978SThomas Graf 	err = -ENODEV;
5967b218574SDenis V. Lunev 	if (dev == NULL)
5975c753978SThomas Graf 		goto errout;
5981da177e4SLinus Torvalds 
5995c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
6005c753978SThomas Graf 	err = -ENOBUFS;
6017b218574SDenis V. Lunev 	if (in_dev == NULL)
6025c753978SThomas Graf 		goto errout;
60371e27da9SHerbert Xu 
6045c753978SThomas Graf 	ifa = inet_alloc_ifa();
6057b218574SDenis V. Lunev 	if (ifa == NULL)
6065c753978SThomas Graf 		/*
6075c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
6085c753978SThomas Graf 		 * assigned to its device and is destroy with it.
6095c753978SThomas Graf 		 */
6105c753978SThomas Graf 		goto errout;
6115c753978SThomas Graf 
612a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
6135c753978SThomas Graf 	in_dev_hold(in_dev);
6145c753978SThomas Graf 
6155c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
6165c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
6175c753978SThomas Graf 
618fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
6191da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
6201da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
6211da177e4SLinus Torvalds 	ifa->ifa_flags = ifm->ifa_flags;
6221da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
6231da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
6245c753978SThomas Graf 
625a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
626a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
6275c753978SThomas Graf 
6285c753978SThomas Graf 	if (tb[IFA_BROADCAST])
629a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
6305c753978SThomas Graf 
6315c753978SThomas Graf 	if (tb[IFA_LABEL])
6325c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
6331da177e4SLinus Torvalds 	else
6341da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
6351da177e4SLinus Torvalds 
6365c753978SThomas Graf 	return ifa;
6375c753978SThomas Graf 
6385c753978SThomas Graf errout:
6395c753978SThomas Graf 	return ERR_PTR(err);
6405c753978SThomas Graf }
6415c753978SThomas Graf 
6425c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
6435c753978SThomas Graf {
6443b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
6455c753978SThomas Graf 	struct in_ifaddr *ifa;
6465c753978SThomas Graf 
6475c753978SThomas Graf 	ASSERT_RTNL();
6485c753978SThomas Graf 
6494b8aa9abSDenis V. Lunev 	ifa = rtm_to_ifaddr(net, nlh);
6505c753978SThomas Graf 	if (IS_ERR(ifa))
6515c753978SThomas Graf 		return PTR_ERR(ifa);
6525c753978SThomas Graf 
65315e47304SEric W. Biederman 	return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds /*
6571da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
6581da177e4SLinus Torvalds  */
6591da177e4SLinus Torvalds 
66040384999SEric Dumazet static int inet_abc_len(__be32 addr)
6611da177e4SLinus Torvalds {
6621da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
6631da177e4SLinus Torvalds 
664f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
6651da177e4SLinus Torvalds 		rc = 0;
6661da177e4SLinus Torvalds 	else {
667714e85beSAl Viro 		__u32 haddr = ntohl(addr);
6681da177e4SLinus Torvalds 
669714e85beSAl Viro 		if (IN_CLASSA(haddr))
6701da177e4SLinus Torvalds 			rc = 8;
671714e85beSAl Viro 		else if (IN_CLASSB(haddr))
6721da177e4SLinus Torvalds 			rc = 16;
673714e85beSAl Viro 		else if (IN_CLASSC(haddr))
6741da177e4SLinus Torvalds 			rc = 24;
6751da177e4SLinus Torvalds 	}
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 	return rc;
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 
681e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
6821da177e4SLinus Torvalds {
6831da177e4SLinus Torvalds 	struct ifreq ifr;
6841da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
6851da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
6861da177e4SLinus Torvalds 	struct in_device *in_dev;
6871da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
6881da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
6891da177e4SLinus Torvalds 	struct net_device *dev;
6901da177e4SLinus Torvalds 	char *colon;
6911da177e4SLinus Torvalds 	int ret = -EFAULT;
6921da177e4SLinus Torvalds 	int tryaddrmatch = 0;
6931da177e4SLinus Torvalds 
6941da177e4SLinus Torvalds 	/*
6951da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
6961da177e4SLinus Torvalds 	 */
6971da177e4SLinus Torvalds 
6981da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
6991da177e4SLinus Torvalds 		goto out;
7001da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	/* save original address for comparison */
7031da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
7061da177e4SLinus Torvalds 	if (colon)
7071da177e4SLinus Torvalds 		*colon = 0;
7081da177e4SLinus Torvalds 
709e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 	switch (cmd) {
7121da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7131da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7141da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7151da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
7161da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
7171da177e4SLinus Torvalds 		   so that we do not impose a lock.
7181da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
7191da177e4SLinus Torvalds 		 */
7201da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
7211da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
7221da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
7231da177e4SLinus Torvalds 		break;
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
726bf5b30b8SZhao Hongjiang 		ret = -EPERM;
72752e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
7281da177e4SLinus Torvalds 			goto out;
7291da177e4SLinus Torvalds 		break;
7301da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
7311da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
7321da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
7331da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
734bf5b30b8SZhao Hongjiang 		ret = -EPERM;
73552e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
7361da177e4SLinus Torvalds 			goto out;
7371da177e4SLinus Torvalds 		ret = -EINVAL;
7381da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
7391da177e4SLinus Torvalds 			goto out;
7401da177e4SLinus Torvalds 		break;
7411da177e4SLinus Torvalds 	default:
7421da177e4SLinus Torvalds 		ret = -EINVAL;
7431da177e4SLinus Torvalds 		goto out;
7441da177e4SLinus Torvalds 	}
7451da177e4SLinus Torvalds 
7461da177e4SLinus Torvalds 	rtnl_lock();
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	ret = -ENODEV;
7499f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
7509f9354b9SEric Dumazet 	if (!dev)
7511da177e4SLinus Torvalds 		goto done;
7521da177e4SLinus Torvalds 
7531da177e4SLinus Torvalds 	if (colon)
7541da177e4SLinus Torvalds 		*colon = ':';
7551da177e4SLinus Torvalds 
7569f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
7579f9354b9SEric Dumazet 	if (in_dev) {
7581da177e4SLinus Torvalds 		if (tryaddrmatch) {
7591da177e4SLinus Torvalds 			/* Matthias Andree */
7601da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
7611da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
7621da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
7631da177e4SLinus Torvalds 			   This is checked above. */
7641da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7651da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
7661da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
7671da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
7686c91afe1SDavid S. Miller 							ifa->ifa_local) {
7691da177e4SLinus Torvalds 					break; /* found */
7701da177e4SLinus Torvalds 				}
7711da177e4SLinus Torvalds 			}
7721da177e4SLinus Torvalds 		}
7731da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
7741da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
7751da177e4SLinus Torvalds 		   comparing just the label */
7761da177e4SLinus Torvalds 		if (!ifa) {
7771da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
7781da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
7791da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
7801da177e4SLinus Torvalds 					break;
7811da177e4SLinus Torvalds 		}
7821da177e4SLinus Torvalds 	}
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
7851da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
7861da177e4SLinus Torvalds 		goto done;
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	switch (cmd) {
7891da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
7901da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
7911da177e4SLinus Torvalds 		goto rarok;
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
7941da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
7951da177e4SLinus Torvalds 		goto rarok;
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
7981da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
7991da177e4SLinus Torvalds 		goto rarok;
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
8021da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
8031da177e4SLinus Torvalds 		goto rarok;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
8061da177e4SLinus Torvalds 		if (colon) {
8071da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
8081da177e4SLinus Torvalds 			if (!ifa)
8091da177e4SLinus Torvalds 				break;
8101da177e4SLinus Torvalds 			ret = 0;
8111da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
8121da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
8131da177e4SLinus Torvalds 			break;
8141da177e4SLinus Torvalds 		}
8151da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
8161da177e4SLinus Torvalds 		break;
8171da177e4SLinus Torvalds 
8181da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
8191da177e4SLinus Torvalds 		ret = -EINVAL;
8201da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8211da177e4SLinus Torvalds 			break;
8221da177e4SLinus Torvalds 
8231da177e4SLinus Torvalds 		if (!ifa) {
8241da177e4SLinus Torvalds 			ret = -ENOBUFS;
8259f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
826fd23c3b3SDavid S. Miller 			INIT_HLIST_NODE(&ifa->hash);
8279f9354b9SEric Dumazet 			if (!ifa)
8281da177e4SLinus Torvalds 				break;
8291da177e4SLinus Torvalds 			if (colon)
8301da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
8311da177e4SLinus Torvalds 			else
8321da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8331da177e4SLinus Torvalds 		} else {
8341da177e4SLinus Torvalds 			ret = 0;
8351da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
8361da177e4SLinus Torvalds 				break;
8371da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8381da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
839148f9729SBjorn Mork 			ifa->ifa_scope = 0;
8401da177e4SLinus Torvalds 		}
8411da177e4SLinus Torvalds 
8421da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
8451da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
8461da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
8471da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
8481da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
8491da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
8501da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
8511da177e4SLinus Torvalds 		} else {
8521da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
8531da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
8541da177e4SLinus Torvalds 		}
8551da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
8561da177e4SLinus Torvalds 		break;
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
8591da177e4SLinus Torvalds 		ret = 0;
8601da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
8611da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8621da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
8631da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
8641da177e4SLinus Torvalds 		}
8651da177e4SLinus Torvalds 		break;
8661da177e4SLinus Torvalds 
8671da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
8681da177e4SLinus Torvalds 		ret = 0;
8691da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
8701da177e4SLinus Torvalds 			break;
8711da177e4SLinus Torvalds 		ret = -EINVAL;
8721da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
8731da177e4SLinus Torvalds 			break;
8741da177e4SLinus Torvalds 		ret = 0;
8751da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
8761da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
8771da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
8781da177e4SLinus Torvalds 		break;
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 		/*
8831da177e4SLinus Torvalds 		 *	The mask we set must be legal.
8841da177e4SLinus Torvalds 		 */
8851da177e4SLinus Torvalds 		ret = -EINVAL;
8861da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
8871da177e4SLinus Torvalds 			break;
8881da177e4SLinus Torvalds 		ret = 0;
8891da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
890a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
8911da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
8921da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
8931da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 			/* See if current broadcast address matches
8961da177e4SLinus Torvalds 			 * with current netmask, then recalculate
8971da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
8981da177e4SLinus Torvalds 			 * funny address, so don't touch it since
8991da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
9001da177e4SLinus Torvalds 			 */
9011da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
9021da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
9031da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
904dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
9051da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
9061da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
9071da177e4SLinus Torvalds 			}
9081da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
9091da177e4SLinus Torvalds 		}
9101da177e4SLinus Torvalds 		break;
9111da177e4SLinus Torvalds 	}
9121da177e4SLinus Torvalds done:
9131da177e4SLinus Torvalds 	rtnl_unlock();
9141da177e4SLinus Torvalds out:
9151da177e4SLinus Torvalds 	return ret;
9161da177e4SLinus Torvalds rarok:
9171da177e4SLinus Torvalds 	rtnl_unlock();
9181da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
9191da177e4SLinus Torvalds 	goto out;
9201da177e4SLinus Torvalds }
9211da177e4SLinus Torvalds 
9221da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
9231da177e4SLinus Torvalds {
924e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
9251da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
9261da177e4SLinus Torvalds 	struct ifreq ifr;
9271da177e4SLinus Torvalds 	int done = 0;
9281da177e4SLinus Torvalds 
9299f9354b9SEric Dumazet 	if (!in_dev)
9301da177e4SLinus Torvalds 		goto out;
9311da177e4SLinus Torvalds 
9329f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
9331da177e4SLinus Torvalds 		if (!buf) {
9341da177e4SLinus Torvalds 			done += sizeof(ifr);
9351da177e4SLinus Torvalds 			continue;
9361da177e4SLinus Torvalds 		}
9371da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
9381da177e4SLinus Torvalds 			break;
9391da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
9401da177e4SLinus Torvalds 		if (ifa->ifa_label)
9411da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, ifa->ifa_label);
9421da177e4SLinus Torvalds 		else
9431da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, dev->name);
9441da177e4SLinus Torvalds 
9451da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
9461da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
9471da177e4SLinus Torvalds 								ifa->ifa_local;
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
9501da177e4SLinus Torvalds 			done = -EFAULT;
9511da177e4SLinus Torvalds 			break;
9521da177e4SLinus Torvalds 		}
9531da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
9541da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
9551da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
9561da177e4SLinus Torvalds 	}
9571da177e4SLinus Torvalds out:
9581da177e4SLinus Torvalds 	return done;
9591da177e4SLinus Torvalds }
9601da177e4SLinus Torvalds 
961a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
9621da177e4SLinus Torvalds {
963a61ced5dSAl Viro 	__be32 addr = 0;
9641da177e4SLinus Torvalds 	struct in_device *in_dev;
965c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
9661da177e4SLinus Torvalds 
9671da177e4SLinus Torvalds 	rcu_read_lock();
968e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
9691da177e4SLinus Torvalds 	if (!in_dev)
9701da177e4SLinus Torvalds 		goto no_in_dev;
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
9731da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
9741da177e4SLinus Torvalds 			continue;
9751da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
9761da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9771da177e4SLinus Torvalds 			break;
9781da177e4SLinus Torvalds 		}
9791da177e4SLinus Torvalds 		if (!addr)
9801da177e4SLinus Torvalds 			addr = ifa->ifa_local;
9811da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 	if (addr)
984c6d14c84SEric Dumazet 		goto out_unlock;
9859f9354b9SEric Dumazet no_in_dev:
9861da177e4SLinus Torvalds 
9871da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
9881da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
9891da177e4SLinus Torvalds 	   in dev_base list.
9901da177e4SLinus Torvalds 	 */
991c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
9929f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
9939f9354b9SEric Dumazet 		if (!in_dev)
9941da177e4SLinus Torvalds 			continue;
9951da177e4SLinus Torvalds 
9961da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
9971da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
9981da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
9991da177e4SLinus Torvalds 				addr = ifa->ifa_local;
1000c6d14c84SEric Dumazet 				goto out_unlock;
10011da177e4SLinus Torvalds 			}
10021da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
10031da177e4SLinus Torvalds 	}
1004c6d14c84SEric Dumazet out_unlock:
10051da177e4SLinus Torvalds 	rcu_read_unlock();
10061da177e4SLinus Torvalds 	return addr;
10071da177e4SLinus Torvalds }
10089f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
10091da177e4SLinus Torvalds 
101060cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
101160cad5daSAl Viro 			      __be32 local, int scope)
10121da177e4SLinus Torvalds {
10131da177e4SLinus Torvalds 	int same = 0;
1014a144ea4bSAl Viro 	__be32 addr = 0;
10151da177e4SLinus Torvalds 
10161da177e4SLinus Torvalds 	for_ifa(in_dev) {
10171da177e4SLinus Torvalds 		if (!addr &&
10181da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
10191da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
10201da177e4SLinus Torvalds 			addr = ifa->ifa_local;
10211da177e4SLinus Torvalds 			if (same)
10221da177e4SLinus Torvalds 				break;
10231da177e4SLinus Torvalds 		}
10241da177e4SLinus Torvalds 		if (!same) {
10251da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
10261da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
10271da177e4SLinus Torvalds 			if (same && addr) {
10281da177e4SLinus Torvalds 				if (local || !dst)
10291da177e4SLinus Torvalds 					break;
10301da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
10311da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
10321da177e4SLinus Torvalds 					break;
10331da177e4SLinus Torvalds 				/* No, then can we use new local src? */
10341da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
10351da177e4SLinus Torvalds 					addr = ifa->ifa_local;
10361da177e4SLinus Torvalds 					break;
10371da177e4SLinus Torvalds 				}
10381da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
10391da177e4SLinus Torvalds 				same = 0;
10401da177e4SLinus Torvalds 			}
10411da177e4SLinus Torvalds 		}
10421da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
10431da177e4SLinus Torvalds 
10441da177e4SLinus Torvalds 	return same ? addr : 0;
10451da177e4SLinus Torvalds }
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds /*
10481da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
10499bd85e32SDenis V. Lunev  * - in_dev: only on this interface, 0=any interface
10501da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
10511da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
10521da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
10531da177e4SLinus Torvalds  */
10549bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev,
10559bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
10561da177e4SLinus Torvalds {
105760cad5daSAl Viro 	__be32 addr = 0;
10589bd85e32SDenis V. Lunev 	struct net_device *dev;
105939a6d063SDenis V. Lunev 	struct net *net;
10601da177e4SLinus Torvalds 
106139a6d063SDenis V. Lunev 	if (scope != RT_SCOPE_LINK)
10629bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
10631da177e4SLinus Torvalds 
1064c346dca1SYOSHIFUJI Hideaki 	net = dev_net(in_dev->dev);
10651da177e4SLinus Torvalds 	rcu_read_lock();
1066c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
10679f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
10689f9354b9SEric Dumazet 		if (in_dev) {
10691da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
10701da177e4SLinus Torvalds 			if (addr)
10711da177e4SLinus Torvalds 				break;
10721da177e4SLinus Torvalds 		}
10731da177e4SLinus Torvalds 	}
10741da177e4SLinus Torvalds 	rcu_read_unlock();
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 	return addr;
10771da177e4SLinus Torvalds }
1078eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds /*
10811da177e4SLinus Torvalds  *	Device notifier
10821da177e4SLinus Torvalds  */
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
10851da177e4SLinus Torvalds {
1086e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
10871da177e4SLinus Torvalds }
10889f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
10911da177e4SLinus Torvalds {
1092e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
10931da177e4SLinus Torvalds }
10949f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
10951da177e4SLinus Torvalds 
10969f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
10979f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
10981da177e4SLinus Torvalds */
10991da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
11001da177e4SLinus Torvalds {
11011da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
11021da177e4SLinus Torvalds 	int named = 0;
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
11051da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
11081da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11091da177e4SLinus Torvalds 		if (named++ == 0)
1110573bf470SThomas Graf 			goto skip;
111144344b2aSMark McLoughlin 		dot = strchr(old, ':');
11121da177e4SLinus Torvalds 		if (dot == NULL) {
11131da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
11141da177e4SLinus Torvalds 			dot = old;
11151da177e4SLinus Torvalds 		}
11169f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
11171da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
11189f9354b9SEric Dumazet 		else
11191da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1120573bf470SThomas Graf skip:
1121573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
11221da177e4SLinus Torvalds 	}
11231da177e4SLinus Torvalds }
11241da177e4SLinus Torvalds 
112540384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
112606770843SBreno Leitao {
112706770843SBreno Leitao 	return mtu >= 68;
112806770843SBreno Leitao }
112906770843SBreno Leitao 
1130d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1131d11327adSIan Campbell 					struct in_device *in_dev)
1132d11327adSIan Campbell 
1133d11327adSIan Campbell {
1134b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1135d11327adSIan Campbell 
1136b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1137b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1138d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
11396c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
11406c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1141d11327adSIan Campbell 			 dev->dev_addr, NULL);
1142d11327adSIan Campbell 	}
1143b76d0789SZoltan Kiss }
1144d11327adSIan Campbell 
11451da177e4SLinus Torvalds /* Called only under RTNL semaphore */
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
11481da177e4SLinus Torvalds 			 void *ptr)
11491da177e4SLinus Torvalds {
11501da177e4SLinus Torvalds 	struct net_device *dev = ptr;
1151748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	ASSERT_RTNL();
11541da177e4SLinus Torvalds 
11551da177e4SLinus Torvalds 	if (!in_dev) {
11568030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
11571da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
11588d76527eSHerbert Xu 			if (!in_dev)
1159b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
11600cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
116142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
116242f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
11631da177e4SLinus Torvalds 			}
116406770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
116506770843SBreno Leitao 			/* Re-enabling IP */
116606770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
116706770843SBreno Leitao 				in_dev = inetdev_init(dev);
11688030f544SHerbert Xu 		}
11691da177e4SLinus Torvalds 		goto out;
11701da177e4SLinus Torvalds 	}
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds 	switch (event) {
11731da177e4SLinus Torvalds 	case NETDEV_REGISTER:
117491df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1175a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
11761da177e4SLinus Torvalds 		break;
11771da177e4SLinus Torvalds 	case NETDEV_UP:
117806770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
11791da177e4SLinus Torvalds 			break;
11800cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
11819f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
11829f9354b9SEric Dumazet 
11839f9354b9SEric Dumazet 			if (ifa) {
1184fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
11851da177e4SLinus Torvalds 				ifa->ifa_local =
11861da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
11871da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
11881da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
11891da177e4SLinus Torvalds 				in_dev_hold(in_dev);
11901da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
11911da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
11921da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
11931da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
11941da177e4SLinus Torvalds 			}
11951da177e4SLinus Torvalds 		}
11961da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1197eefef1cfSStephen Hemminger 		/* fall through */
1198eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1199d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1200d11327adSIan Campbell 			break;
1201d11327adSIan Campbell 		/* fall through */
1202d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1203a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1204d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
12051da177e4SLinus Torvalds 		break;
12061da177e4SLinus Torvalds 	case NETDEV_DOWN:
12071da177e4SLinus Torvalds 		ip_mc_down(in_dev);
12081da177e4SLinus Torvalds 		break;
120993d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
121075c78500SMoni Shoua 		ip_mc_unmap(in_dev);
121175c78500SMoni Shoua 		break;
121293d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
121375c78500SMoni Shoua 		ip_mc_remap(in_dev);
121475c78500SMoni Shoua 		break;
12151da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
121606770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
12171da177e4SLinus Torvalds 			break;
121806770843SBreno Leitao 		/* disable IP when MTU is not enough */
12191da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
12201da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
12211da177e4SLinus Torvalds 		break;
12221da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
12231da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
12241da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
12251da177e4SLinus Torvalds 		 */
12261da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
12271da177e4SLinus Torvalds 
122851602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
122966f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
12301da177e4SLinus Torvalds 		break;
12311da177e4SLinus Torvalds 	}
12321da177e4SLinus Torvalds out:
12331da177e4SLinus Torvalds 	return NOTIFY_DONE;
12341da177e4SLinus Torvalds }
12351da177e4SLinus Torvalds 
12361da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
12371da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
12381da177e4SLinus Torvalds };
12391da177e4SLinus Torvalds 
124040384999SEric Dumazet static size_t inet_nlmsg_size(void)
1241339bf98fSThomas Graf {
1242339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1243339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1244339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1245339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1246339bf98fSThomas Graf 	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1247339bf98fSThomas Graf }
1248339bf98fSThomas Graf 
12491da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
125015e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
12511da177e4SLinus Torvalds {
12521da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
12531da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
12541da177e4SLinus Torvalds 
125515e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
125647f68512SThomas Graf 	if (nlh == NULL)
125726932566SPatrick McHardy 		return -EMSGSIZE;
125847f68512SThomas Graf 
125947f68512SThomas Graf 	ifm = nlmsg_data(nlh);
12601da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
12611da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
12621da177e4SLinus Torvalds 	ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
12631da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
12641da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
12651da177e4SLinus Torvalds 
1266f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1267f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1268f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1269f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) ||
1270f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1271f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1272f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
1273f3756b79SDavid S. Miller 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)))
1274f3756b79SDavid S. Miller 		goto nla_put_failure;
127547f68512SThomas Graf 
127647f68512SThomas Graf 	return nlmsg_end(skb, nlh);
127747f68512SThomas Graf 
127847f68512SThomas Graf nla_put_failure:
127926932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
128026932566SPatrick McHardy 	return -EMSGSIZE;
12811da177e4SLinus Torvalds }
12821da177e4SLinus Torvalds 
12831da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
12841da177e4SLinus Torvalds {
12853b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1286eec4df98SEric Dumazet 	int h, s_h;
1287eec4df98SEric Dumazet 	int idx, s_idx;
1288eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
12891da177e4SLinus Torvalds 	struct net_device *dev;
12901da177e4SLinus Torvalds 	struct in_device *in_dev;
12911da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1292eec4df98SEric Dumazet 	struct hlist_head *head;
1293eec4df98SEric Dumazet 	struct hlist_node *node;
12941da177e4SLinus Torvalds 
1295eec4df98SEric Dumazet 	s_h = cb->args[0];
1296eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1297eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1298eec4df98SEric Dumazet 
1299eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
13007562f876SPavel Emelianov 		idx = 0;
1301eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1302eec4df98SEric Dumazet 		rcu_read_lock();
1303eec4df98SEric Dumazet 		hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
13041da177e4SLinus Torvalds 			if (idx < s_idx)
13057562f876SPavel Emelianov 				goto cont;
13064b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
13071da177e4SLinus Torvalds 				s_ip_idx = 0;
1308eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
13099f9354b9SEric Dumazet 			if (!in_dev)
13107562f876SPavel Emelianov 				goto cont;
13111da177e4SLinus Torvalds 
13121da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
13131da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
13141da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1315596e4150SStephen Hemminger 					continue;
1316eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
131715e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
13181da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1319eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1320eec4df98SEric Dumazet 					rcu_read_unlock();
13211da177e4SLinus Torvalds 					goto done;
13221da177e4SLinus Torvalds 				}
1323eec4df98SEric Dumazet 			}
13247562f876SPavel Emelianov cont:
13257562f876SPavel Emelianov 			idx++;
13261da177e4SLinus Torvalds 		}
1327eec4df98SEric Dumazet 		rcu_read_unlock();
1328eec4df98SEric Dumazet 	}
13291da177e4SLinus Torvalds 
13301da177e4SLinus Torvalds done:
1331eec4df98SEric Dumazet 	cb->args[0] = h;
1332eec4df98SEric Dumazet 	cb->args[1] = idx;
1333eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds 	return skb->len;
13361da177e4SLinus Torvalds }
13371da177e4SLinus Torvalds 
1338d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
133915e47304SEric W. Biederman 		      u32 portid)
13401da177e4SLinus Torvalds {
134147f68512SThomas Graf 	struct sk_buff *skb;
1342d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1343d6062cbbSThomas Graf 	int err = -ENOBUFS;
13444b8aa9abSDenis V. Lunev 	struct net *net;
13451da177e4SLinus Torvalds 
1346c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1347339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
134847f68512SThomas Graf 	if (skb == NULL)
1349d6062cbbSThomas Graf 		goto errout;
1350d6062cbbSThomas Graf 
135115e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
135226932566SPatrick McHardy 	if (err < 0) {
135326932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
135426932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
135526932566SPatrick McHardy 		kfree_skb(skb);
135626932566SPatrick McHardy 		goto errout;
135726932566SPatrick McHardy 	}
135815e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
13591ce85fe4SPablo Neira Ayuso 	return;
1360d6062cbbSThomas Graf errout:
1361d6062cbbSThomas Graf 	if (err < 0)
13624b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
13631da177e4SLinus Torvalds }
13641da177e4SLinus Torvalds 
13659f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
13669f0f7272SThomas Graf {
13671fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
13689f0f7272SThomas Graf 
13699f0f7272SThomas Graf 	if (!in_dev)
13709f0f7272SThomas Graf 		return 0;
13719f0f7272SThomas Graf 
13729f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
13739f0f7272SThomas Graf }
13749f0f7272SThomas Graf 
13759f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
13769f0f7272SThomas Graf {
13771fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
13789f0f7272SThomas Graf 	struct nlattr *nla;
13799f0f7272SThomas Graf 	int i;
13809f0f7272SThomas Graf 
13819f0f7272SThomas Graf 	if (!in_dev)
13829f0f7272SThomas Graf 		return -ENODATA;
13839f0f7272SThomas Graf 
13849f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
13859f0f7272SThomas Graf 	if (nla == NULL)
13869f0f7272SThomas Graf 		return -EMSGSIZE;
13879f0f7272SThomas Graf 
13889f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
13899f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
13909f0f7272SThomas Graf 
13919f0f7272SThomas Graf 	return 0;
13929f0f7272SThomas Graf }
13939f0f7272SThomas Graf 
13949f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
13959f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
13969f0f7272SThomas Graf };
13979f0f7272SThomas Graf 
1398cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1399cf7afbfeSThomas Graf 				 const struct nlattr *nla)
14009f0f7272SThomas Graf {
14019f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
14029f0f7272SThomas Graf 	int err, rem;
14039f0f7272SThomas Graf 
1404f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1405cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
14069f0f7272SThomas Graf 
14079f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
14089f0f7272SThomas Graf 	if (err < 0)
14099f0f7272SThomas Graf 		return err;
14109f0f7272SThomas Graf 
14119f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14129f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
14139f0f7272SThomas Graf 			int cfgid = nla_type(a);
14149f0f7272SThomas Graf 
14159f0f7272SThomas Graf 			if (nla_len(a) < 4)
14169f0f7272SThomas Graf 				return -EINVAL;
14179f0f7272SThomas Graf 
14189f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
14199f0f7272SThomas Graf 				return -EINVAL;
14209f0f7272SThomas Graf 		}
14219f0f7272SThomas Graf 	}
14229f0f7272SThomas Graf 
1423cf7afbfeSThomas Graf 	return 0;
1424cf7afbfeSThomas Graf }
1425cf7afbfeSThomas Graf 
1426cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1427cf7afbfeSThomas Graf {
1428f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1429cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1430cf7afbfeSThomas Graf 	int rem;
1431cf7afbfeSThomas Graf 
1432cf7afbfeSThomas Graf 	if (!in_dev)
1433cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1434cf7afbfeSThomas Graf 
1435cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1436cf7afbfeSThomas Graf 		BUG();
1437cf7afbfeSThomas Graf 
14389f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
14399f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
14409f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
14419f0f7272SThomas Graf 	}
14429f0f7272SThomas Graf 
14439f0f7272SThomas Graf 	return 0;
14449f0f7272SThomas Graf }
14459f0f7272SThomas Graf 
1446edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1447edc9e748SNicolas Dichtel {
1448edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1449edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1450edc9e748SNicolas Dichtel 
14519e551110SNicolas Dichtel 	/* type -1 is used for ALL */
14529e551110SNicolas Dichtel 	if (type == -1 || type == NETCONFA_FORWARDING)
1453edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1454cc535dfbSNicolas Dichtel 	if (type == -1 || type == NETCONFA_RP_FILTER)
1455cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1456*d67b8c61SNicolas Dichtel 	if (type == -1 || type == NETCONFA_MC_FORWARDING)
1457*d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
1458edc9e748SNicolas Dichtel 
1459edc9e748SNicolas Dichtel 	return size;
1460edc9e748SNicolas Dichtel }
1461edc9e748SNicolas Dichtel 
1462edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1463edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1464edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1465edc9e748SNicolas Dichtel 				     int type)
1466edc9e748SNicolas Dichtel {
1467edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1468edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1469edc9e748SNicolas Dichtel 
1470edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1471edc9e748SNicolas Dichtel 			flags);
1472edc9e748SNicolas Dichtel 	if (nlh == NULL)
1473edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1474edc9e748SNicolas Dichtel 
1475edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1476edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1477edc9e748SNicolas Dichtel 
1478edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1479edc9e748SNicolas Dichtel 		goto nla_put_failure;
1480edc9e748SNicolas Dichtel 
14819e551110SNicolas Dichtel 	/* type -1 is used for ALL */
14829e551110SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_FORWARDING) &&
1483edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1484edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1485edc9e748SNicolas Dichtel 		goto nla_put_failure;
1486cc535dfbSNicolas Dichtel 	if ((type == -1 || type == NETCONFA_RP_FILTER) &&
1487cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1488cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1489cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1490*d67b8c61SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
1491*d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1492*d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1493*d67b8c61SNicolas Dichtel 		goto nla_put_failure;
1494edc9e748SNicolas Dichtel 
1495edc9e748SNicolas Dichtel 	return nlmsg_end(skb, nlh);
1496edc9e748SNicolas Dichtel 
1497edc9e748SNicolas Dichtel nla_put_failure:
1498edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1499edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1500edc9e748SNicolas Dichtel }
1501edc9e748SNicolas Dichtel 
1502*d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
1503edc9e748SNicolas Dichtel 				 struct ipv4_devconf *devconf)
1504edc9e748SNicolas Dichtel {
1505edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1506edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1507edc9e748SNicolas Dichtel 
1508edc9e748SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
1509edc9e748SNicolas Dichtel 	if (skb == NULL)
1510edc9e748SNicolas Dichtel 		goto errout;
1511edc9e748SNicolas Dichtel 
1512edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
1513edc9e748SNicolas Dichtel 					RTM_NEWNETCONF, 0, type);
1514edc9e748SNicolas Dichtel 	if (err < 0) {
1515edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1516edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1517edc9e748SNicolas Dichtel 		kfree_skb(skb);
1518edc9e748SNicolas Dichtel 		goto errout;
1519edc9e748SNicolas Dichtel 	}
1520edc9e748SNicolas Dichtel 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC);
1521edc9e748SNicolas Dichtel 	return;
1522edc9e748SNicolas Dichtel errout:
1523edc9e748SNicolas Dichtel 	if (err < 0)
1524edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1525edc9e748SNicolas Dichtel }
1526edc9e748SNicolas Dichtel 
15279e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
15289e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
15299e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1530cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
15319e551110SNicolas Dichtel };
15329e551110SNicolas Dichtel 
15339e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
15349e551110SNicolas Dichtel 				    struct nlmsghdr *nlh,
15359e551110SNicolas Dichtel 				    void *arg)
15369e551110SNicolas Dichtel {
15379e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
15389e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
15399e551110SNicolas Dichtel 	struct netconfmsg *ncm;
15409e551110SNicolas Dichtel 	struct sk_buff *skb;
15419e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
15429e551110SNicolas Dichtel 	struct in_device *in_dev;
15439e551110SNicolas Dichtel 	struct net_device *dev;
15449e551110SNicolas Dichtel 	int ifindex;
15459e551110SNicolas Dichtel 	int err;
15469e551110SNicolas Dichtel 
15479e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
15489e551110SNicolas Dichtel 			  devconf_ipv4_policy);
15499e551110SNicolas Dichtel 	if (err < 0)
15509e551110SNicolas Dichtel 		goto errout;
15519e551110SNicolas Dichtel 
15529e551110SNicolas Dichtel 	err = EINVAL;
15539e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
15549e551110SNicolas Dichtel 		goto errout;
15559e551110SNicolas Dichtel 
15569e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
15579e551110SNicolas Dichtel 	switch (ifindex) {
15589e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
15599e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
15609e551110SNicolas Dichtel 		break;
15619e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
15629e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
15639e551110SNicolas Dichtel 		break;
15649e551110SNicolas Dichtel 	default:
15659e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
15669e551110SNicolas Dichtel 		if (dev == NULL)
15679e551110SNicolas Dichtel 			goto errout;
15689e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
15699e551110SNicolas Dichtel 		if (in_dev == NULL)
15709e551110SNicolas Dichtel 			goto errout;
15719e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
15729e551110SNicolas Dichtel 		break;
15739e551110SNicolas Dichtel 	}
15749e551110SNicolas Dichtel 
15759e551110SNicolas Dichtel 	err = -ENOBUFS;
15769e551110SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
15779e551110SNicolas Dichtel 	if (skb == NULL)
15789e551110SNicolas Dichtel 		goto errout;
15799e551110SNicolas Dichtel 
15809e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
15819e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
15829e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
15839e551110SNicolas Dichtel 					-1);
15849e551110SNicolas Dichtel 	if (err < 0) {
15859e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
15869e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
15879e551110SNicolas Dichtel 		kfree_skb(skb);
15889e551110SNicolas Dichtel 		goto errout;
15899e551110SNicolas Dichtel 	}
15909e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
15919e551110SNicolas Dichtel errout:
15929e551110SNicolas Dichtel 	return err;
15939e551110SNicolas Dichtel }
15949e551110SNicolas Dichtel 
15951da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
15961da177e4SLinus Torvalds 
1597c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
159831be3085SHerbert Xu {
159931be3085SHerbert Xu 	struct net_device *dev;
160031be3085SHerbert Xu 
160131be3085SHerbert Xu 	rcu_read_lock();
1602c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1603c6d14c84SEric Dumazet 		struct in_device *in_dev;
1604c6d14c84SEric Dumazet 
160531be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
160631be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
16079355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1608c6d14c84SEric Dumazet 	}
160931be3085SHerbert Xu 	rcu_read_unlock();
161031be3085SHerbert Xu }
161131be3085SHerbert Xu 
1612c6d14c84SEric Dumazet /* called with RTNL locked */
1613c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
161468dd299bSPavel Emelyanov {
161568dd299bSPavel Emelyanov 	struct net_device *dev;
1616586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
161768dd299bSPavel Emelyanov 
1618586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
16199355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
1620edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1621edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
1622edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
1623edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1624edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
1625edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
162668dd299bSPavel Emelyanov 
1627c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
162868dd299bSPavel Emelyanov 		struct in_device *in_dev;
16290187bdfbSBen Hutchings 		if (on)
16300187bdfbSBen Hutchings 			dev_disable_lro(dev);
163168dd299bSPavel Emelyanov 		rcu_read_lock();
163268dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
1633edc9e748SNicolas Dichtel 		if (in_dev) {
163468dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1635edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1636edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
1637edc9e748SNicolas Dichtel 		}
163868dd299bSPavel Emelyanov 		rcu_read_unlock();
163968dd299bSPavel Emelyanov 	}
164068dd299bSPavel Emelyanov }
164168dd299bSPavel Emelyanov 
164231be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write,
16438d65af78SAlexey Dobriyan 			     void __user *buffer,
164431be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
164531be3085SHerbert Xu {
1646d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
16478d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
1648d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
164931be3085SHerbert Xu 
165031be3085SHerbert Xu 	if (write) {
165131be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1652c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
165331be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
165431be3085SHerbert Xu 
165531be3085SHerbert Xu 		set_bit(i, cnf->state);
165631be3085SHerbert Xu 
16579355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1658c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
1659d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
1660d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
1661d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
16624ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
1663cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
1664cc535dfbSNicolas Dichtel 		    new_value != old_value) {
1665cc535dfbSNicolas Dichtel 			int ifindex;
1666cc535dfbSNicolas Dichtel 
1667cc535dfbSNicolas Dichtel 			if (cnf == net->ipv4.devconf_dflt)
1668cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_DEFAULT;
1669cc535dfbSNicolas Dichtel 			else if (cnf == net->ipv4.devconf_all)
1670cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_ALL;
1671cc535dfbSNicolas Dichtel 			else {
1672cc535dfbSNicolas Dichtel 				struct in_device *idev =
1673cc535dfbSNicolas Dichtel 					container_of(cnf, struct in_device,
1674cc535dfbSNicolas Dichtel 						     cnf);
1675cc535dfbSNicolas Dichtel 				ifindex = idev->dev->ifindex;
1676cc535dfbSNicolas Dichtel 			}
1677cc535dfbSNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER,
1678cc535dfbSNicolas Dichtel 						    ifindex, cnf);
1679cc535dfbSNicolas Dichtel 		}
168031be3085SHerbert Xu 	}
168131be3085SHerbert Xu 
168231be3085SHerbert Xu 	return ret;
168331be3085SHerbert Xu }
168431be3085SHerbert Xu 
16851da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write,
16868d65af78SAlexey Dobriyan 				  void __user *buffer,
16871da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
16881da177e4SLinus Torvalds {
16891da177e4SLinus Torvalds 	int *valp = ctl->data;
16901da177e4SLinus Torvalds 	int val = *valp;
169188af182eSEric W. Biederman 	loff_t pos = *ppos;
16928d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
16931da177e4SLinus Torvalds 
16941da177e4SLinus Torvalds 	if (write && *valp != val) {
1695c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
1696c0ce9fb3SPavel Emelyanov 
16970187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
169888af182eSEric W. Biederman 			if (!rtnl_trylock()) {
169988af182eSEric W. Biederman 				/* Restore the original values before restarting */
170088af182eSEric W. Biederman 				*valp = val;
170188af182eSEric W. Biederman 				*ppos = pos;
17029b8adb5eSEric W. Biederman 				return restart_syscall();
170388af182eSEric W. Biederman 			}
17040187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1705c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
1706edc9e748SNicolas Dichtel 			} else {
17070187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
17080187bdfbSBen Hutchings 				struct in_device *idev =
17090187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
1710edc9e748SNicolas Dichtel 				if (*valp)
17110187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
1712edc9e748SNicolas Dichtel 				inet_netconf_notify_devconf(net,
1713edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
1714edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
1715edc9e748SNicolas Dichtel 							    cnf);
17160187bdfbSBen Hutchings 			}
17170187bdfbSBen Hutchings 			rtnl_unlock();
17184ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
1719edc9e748SNicolas Dichtel 		} else
1720edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1721edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
1722edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
17230187bdfbSBen Hutchings 	}
17241da177e4SLinus Torvalds 
17251da177e4SLinus Torvalds 	return ret;
17261da177e4SLinus Torvalds }
17271da177e4SLinus Torvalds 
1728323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write,
17298d65af78SAlexey Dobriyan 				void __user *buffer,
17301da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
17311da177e4SLinus Torvalds {
17321da177e4SLinus Torvalds 	int *valp = ctl->data;
17331da177e4SLinus Torvalds 	int val = *valp;
17348d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
173576e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
17361da177e4SLinus Torvalds 
17371da177e4SLinus Torvalds 	if (write && *valp != val)
17384ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
17391da177e4SLinus Torvalds 
17401da177e4SLinus Torvalds 	return ret;
17411da177e4SLinus Torvalds }
17421da177e4SLinus Torvalds 
1743f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
174442f811b8SHerbert Xu 	{ \
174542f811b8SHerbert Xu 		.procname	= name, \
174642f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
174702291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
174842f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
174942f811b8SHerbert Xu 		.mode		= mval, \
175042f811b8SHerbert Xu 		.proc_handler	= proc, \
175131be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
175242f811b8SHerbert Xu 	}
175342f811b8SHerbert Xu 
175442f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
1755f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
175642f811b8SHerbert Xu 
175742f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
1758f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
175942f811b8SHerbert Xu 
1760f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1761f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
176242f811b8SHerbert Xu 
176342f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
1764f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
176542f811b8SHerbert Xu 
17661da177e4SLinus Torvalds static struct devinet_sysctl_table {
17671da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
176802291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
17691da177e4SLinus Torvalds } devinet_sysctl = {
17701da177e4SLinus Torvalds 	.devinet_vars = {
177142f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
1772f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
177342f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
177442f811b8SHerbert Xu 
177542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
177642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
177742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
177842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
177942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
178042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
178142f811b8SHerbert Xu 					"accept_source_route"),
17828153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
178328f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
178442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
178542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
178642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
178742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
178842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
178942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
179042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
179142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
179242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
1793eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
179465324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
179542f811b8SHerbert Xu 
179642f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
179742f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
179842f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
179942f811b8SHerbert Xu 					      "force_igmp_version"),
180042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
180142f811b8SHerbert Xu 					      "promote_secondaries"),
1802d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
1803d0daebc3SThomas Graf 					      "route_localnet"),
18041da177e4SLinus Torvalds 	},
18051da177e4SLinus Torvalds };
18061da177e4SLinus Torvalds 
1807ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
1808f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
18091da177e4SLinus Torvalds {
18101da177e4SLinus Torvalds 	int i;
18119fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
18128607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
1813bfada697SPavel Emelyanov 
18149fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
18151da177e4SLinus Torvalds 	if (!t)
18169fa89642SPavel Emelyanov 		goto out;
18179fa89642SPavel Emelyanov 
18181da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
18191da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
182031be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
1821c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
18221da177e4SLinus Torvalds 	}
18231da177e4SLinus Torvalds 
18248607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
18251da177e4SLinus Torvalds 
18268607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
18271da177e4SLinus Torvalds 	if (!t->sysctl_header)
18288607ddb8SEric W. Biederman 		goto free;
18291da177e4SLinus Torvalds 
18301da177e4SLinus Torvalds 	p->sysctl = t;
1831ea40b324SPavel Emelyanov 	return 0;
18321da177e4SLinus Torvalds 
18331da177e4SLinus Torvalds free:
18341da177e4SLinus Torvalds 	kfree(t);
18359fa89642SPavel Emelyanov out:
1836ea40b324SPavel Emelyanov 	return -ENOBUFS;
18371da177e4SLinus Torvalds }
18381da177e4SLinus Torvalds 
183951602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
184066f27a52SPavel Emelyanov {
184151602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
184266f27a52SPavel Emelyanov 
184351602b2aSPavel Emelyanov 	if (t == NULL)
184451602b2aSPavel Emelyanov 		return;
184551602b2aSPavel Emelyanov 
184651602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
1847ff538818SLucian Adrian Grijincu 	unregister_net_sysctl_table(t->sysctl_header);
18481da177e4SLinus Torvalds 	kfree(t);
18491da177e4SLinus Torvalds }
185051602b2aSPavel Emelyanov 
185151602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
185251602b2aSPavel Emelyanov {
185354716e3bSEric W. Biederman 	neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
1854c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
1855f8572d8fSEric W. Biederman 					&idev->cnf);
185651602b2aSPavel Emelyanov }
185751602b2aSPavel Emelyanov 
185851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
185951602b2aSPavel Emelyanov {
186051602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
186151602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
18621da177e4SLinus Torvalds }
18631da177e4SLinus Torvalds 
186468dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
186568dd299bSPavel Emelyanov 	{
186668dd299bSPavel Emelyanov 		.procname	= "ip_forward",
186768dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
186802291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
186968dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
187068dd299bSPavel Emelyanov 		.mode		= 0644,
187168dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
187268dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
1873c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
187468dd299bSPavel Emelyanov 	},
187568dd299bSPavel Emelyanov 	{ },
187668dd299bSPavel Emelyanov };
18772a75de0cSEric Dumazet #endif
187868dd299bSPavel Emelyanov 
1879752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
1880752d14dcSPavel Emelyanov {
1881752d14dcSPavel Emelyanov 	int err;
1882752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
18832a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
18842a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
1885752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
18862a75de0cSEric Dumazet #endif
1887752d14dcSPavel Emelyanov 
1888752d14dcSPavel Emelyanov 	err = -ENOMEM;
1889752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
1890752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
1891752d14dcSPavel Emelyanov 
189209ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
1893752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1894752d14dcSPavel Emelyanov 		if (all == NULL)
1895752d14dcSPavel Emelyanov 			goto err_alloc_all;
1896752d14dcSPavel Emelyanov 
1897752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1898752d14dcSPavel Emelyanov 		if (dflt == NULL)
1899752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
1900752d14dcSPavel Emelyanov 
19012a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1902752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1903752d14dcSPavel Emelyanov 		if (tbl == NULL)
1904752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
1905752d14dcSPavel Emelyanov 
190602291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
1907752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
1908752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
19092a75de0cSEric Dumazet #endif
1910752d14dcSPavel Emelyanov 	}
1911752d14dcSPavel Emelyanov 
1912752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1913f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
1914752d14dcSPavel Emelyanov 	if (err < 0)
1915752d14dcSPavel Emelyanov 		goto err_reg_all;
1916752d14dcSPavel Emelyanov 
1917f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
1918752d14dcSPavel Emelyanov 	if (err < 0)
1919752d14dcSPavel Emelyanov 		goto err_reg_dflt;
1920752d14dcSPavel Emelyanov 
1921752d14dcSPavel Emelyanov 	err = -ENOMEM;
19228607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
1923752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
1924752d14dcSPavel Emelyanov 		goto err_reg_ctl;
19252a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
1926752d14dcSPavel Emelyanov #endif
1927752d14dcSPavel Emelyanov 
1928752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
1929752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
1930752d14dcSPavel Emelyanov 	return 0;
1931752d14dcSPavel Emelyanov 
1932752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
1933752d14dcSPavel Emelyanov err_reg_ctl:
1934752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
1935752d14dcSPavel Emelyanov err_reg_dflt:
1936752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
1937752d14dcSPavel Emelyanov err_reg_all:
1938752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
1939752d14dcSPavel Emelyanov 		kfree(tbl);
1940752d14dcSPavel Emelyanov err_alloc_ctl:
19412a75de0cSEric Dumazet #endif
1942752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
1943752d14dcSPavel Emelyanov 		kfree(dflt);
1944752d14dcSPavel Emelyanov err_alloc_dflt:
1945752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
1946752d14dcSPavel Emelyanov 		kfree(all);
1947752d14dcSPavel Emelyanov err_alloc_all:
1948752d14dcSPavel Emelyanov 	return err;
1949752d14dcSPavel Emelyanov }
1950752d14dcSPavel Emelyanov 
1951752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
1952752d14dcSPavel Emelyanov {
19532a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
1954752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
1955752d14dcSPavel Emelyanov 
1956752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
1957752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
1958752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1959752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
1960752d14dcSPavel Emelyanov 	kfree(tbl);
19612a75de0cSEric Dumazet #endif
1962752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
1963752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
1964752d14dcSPavel Emelyanov }
1965752d14dcSPavel Emelyanov 
1966752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
1967752d14dcSPavel Emelyanov 	.init = devinet_init_net,
1968752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
1969752d14dcSPavel Emelyanov };
1970752d14dcSPavel Emelyanov 
19719f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
19729f0f7272SThomas Graf 	.family		  = AF_INET,
19739f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
19749f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
1975cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
1976cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
19779f0f7272SThomas Graf };
19789f0f7272SThomas Graf 
19791da177e4SLinus Torvalds void __init devinet_init(void)
19801da177e4SLinus Torvalds {
1981fd23c3b3SDavid S. Miller 	int i;
1982fd23c3b3SDavid S. Miller 
1983fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
1984fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
1985fd23c3b3SDavid S. Miller 
1986752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
1987752d14dcSPavel Emelyanov 
19881da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
19891da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
199063f3444fSThomas Graf 
19919f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
19929f0f7272SThomas Graf 
1993c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
1994c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
1995c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
19969e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
19979e551110SNicolas Dichtel 		      NULL, NULL);
19981da177e4SLinus Torvalds }
19991da177e4SLinus Torvalds 
2000