xref: /linux/net/ipv4/devinet.c (revision 05a324b9c50c3edbe0ce48ee3e37b210859ef1ae)
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>
665c766d64SJiri Pirko #include <net/addrconf.h>
671da177e4SLinus Torvalds 
68406b6f97SDavid S. Miller #include "fib_lookup.h"
69406b6f97SDavid S. Miller 
700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = {
7142f811b8SHerbert Xu 	.data = {
7202291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7302291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7402291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7502291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
7642f811b8SHerbert Xu 	},
771da177e4SLinus Torvalds };
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8042f811b8SHerbert Xu 	.data = {
8102291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8202291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8302291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
8642f811b8SHerbert Xu 	},
871da177e4SLinus Torvalds };
881da177e4SLinus Torvalds 
899355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
909355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9142f811b8SHerbert Xu 
92ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
935c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
945c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
955c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
965176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
975c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
985c753978SThomas Graf };
995c753978SThomas Graf 
10040384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
10140384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
10240384999SEric Dumazet 
103fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
104fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock);
105fd23c3b3SDavid S. Miller 
10640384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr)
107fd23c3b3SDavid S. Miller {
10840384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
109fd23c3b3SDavid S. Miller 
11040384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
111fd23c3b3SDavid S. Miller }
112fd23c3b3SDavid S. Miller 
113fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
114fd23c3b3SDavid S. Miller {
11540384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
116fd23c3b3SDavid S. Miller 
117fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
118fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
119fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
120fd23c3b3SDavid S. Miller }
121fd23c3b3SDavid S. Miller 
122fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
123fd23c3b3SDavid S. Miller {
124fd23c3b3SDavid S. Miller 	spin_lock(&inet_addr_hash_lock);
125fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
126fd23c3b3SDavid S. Miller 	spin_unlock(&inet_addr_hash_lock);
127fd23c3b3SDavid S. Miller }
128fd23c3b3SDavid S. Miller 
1299435eb1cSDavid S. Miller /**
1309435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1319435eb1cSDavid S. Miller  * @net: the net namespace
1329435eb1cSDavid S. Miller  * @addr: the source address
1339435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1349435eb1cSDavid S. Miller  *
1359435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1369435eb1cSDavid S. Miller  */
1379435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1389435eb1cSDavid S. Miller {
13940384999SEric Dumazet 	u32 hash = inet_addr_hash(net, addr);
1409435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1419435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1429435eb1cSDavid S. Miller 
1439435eb1cSDavid S. Miller 	rcu_read_lock();
144b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) {
14540384999SEric Dumazet 		if (ifa->ifa_local == addr) {
1469435eb1cSDavid S. Miller 			struct net_device *dev = ifa->ifa_dev->dev;
1479435eb1cSDavid S. Miller 
1489435eb1cSDavid S. Miller 			if (!net_eq(dev_net(dev), net))
1499435eb1cSDavid S. Miller 				continue;
1509435eb1cSDavid S. Miller 			result = dev;
1519435eb1cSDavid S. Miller 			break;
1529435eb1cSDavid S. Miller 		}
1539435eb1cSDavid S. Miller 	}
154406b6f97SDavid S. Miller 	if (!result) {
155406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
156406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
157406b6f97SDavid S. Miller 		struct fib_table *local;
158406b6f97SDavid S. Miller 
159406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
160406b6f97SDavid S. Miller 		 * over loopback subnets work.
161406b6f97SDavid S. Miller 		 */
162406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
163406b6f97SDavid S. Miller 		if (local &&
164406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
165406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
166406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
167406b6f97SDavid S. Miller 	}
1689435eb1cSDavid S. Miller 	if (result && devref)
1699435eb1cSDavid S. Miller 		dev_hold(result);
1709435eb1cSDavid S. Miller 	rcu_read_unlock();
1719435eb1cSDavid S. Miller 	return result;
1729435eb1cSDavid S. Miller }
1739435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1749435eb1cSDavid S. Miller 
175d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1761da177e4SLinus Torvalds 
177e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1781da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1791da177e4SLinus Torvalds 			 int destroy);
1801da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
18166f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev);
18251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
18351602b2aSPavel Emelyanov #else
18440384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev)
18551602b2aSPavel Emelyanov {
18651602b2aSPavel Emelyanov }
18740384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
18851602b2aSPavel Emelyanov {
18951602b2aSPavel Emelyanov }
1901da177e4SLinus Torvalds #endif
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds /* Locks all the inet devices. */
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1951da177e4SLinus Torvalds {
19693adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2021da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2031da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2041da177e4SLinus Torvalds 	kfree(ifa);
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
20740384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2081da177e4SLinus Torvalds {
2091da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2131da177e4SLinus Torvalds {
2141da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2151da177e4SLinus Torvalds 
216547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
217547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
2181da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
21991df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2201da177e4SLinus Torvalds #endif
2211da177e4SLinus Torvalds 	dev_put(dev);
2221da177e4SLinus Torvalds 	if (!idev->dead)
2239f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2249f9354b9SEric Dumazet 	else
2251da177e4SLinus Torvalds 		kfree(idev);
2261da177e4SLinus Torvalds }
2279f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2281da177e4SLinus Torvalds 
22971e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds 	struct in_device *in_dev;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	ASSERT_RTNL();
2341da177e4SLinus Torvalds 
2350da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2361da177e4SLinus Torvalds 	if (!in_dev)
2371da177e4SLinus Torvalds 		goto out;
238c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2399355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2401da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2411da177e4SLinus Torvalds 	in_dev->dev = dev;
2429f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2439f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2441da177e4SLinus Torvalds 		goto out_kfree;
2450187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2460187bdfbSBen Hutchings 		dev_disable_lro(dev);
2471da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2481da177e4SLinus Torvalds 	dev_hold(dev);
24930c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2501da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2511da177e4SLinus Torvalds 
25266f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2531da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2541da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2551da177e4SLinus Torvalds 		ip_mc_up(in_dev);
256483479ecSJarek Poplawski 
25730c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
258cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
259483479ecSJarek Poplawski out:
2601da177e4SLinus Torvalds 	return in_dev;
2611da177e4SLinus Torvalds out_kfree:
2621da177e4SLinus Torvalds 	kfree(in_dev);
2631da177e4SLinus Torvalds 	in_dev = NULL;
2641da177e4SLinus Torvalds 	goto out;
2651da177e4SLinus Torvalds }
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2681da177e4SLinus Torvalds {
2691da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2701da177e4SLinus Torvalds 	in_dev_put(idev);
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2761da177e4SLinus Torvalds 	struct net_device *dev;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	ASSERT_RTNL();
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	dev = in_dev->dev;
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	in_dev->dead = 1;
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2871da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2881da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2891da177e4SLinus Torvalds 	}
2901da177e4SLinus Torvalds 
291a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
2921da177e4SLinus Torvalds 
29351602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
2941da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
2951da177e4SLinus Torvalds 	arp_ifdown(dev);
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
300ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds 	rcu_read_lock();
3031da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3041da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3051da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3061da177e4SLinus Torvalds 				rcu_read_unlock();
3071da177e4SLinus Torvalds 				return 1;
3081da177e4SLinus Torvalds 			}
3091da177e4SLinus Torvalds 		}
3101da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3111da177e4SLinus Torvalds 	rcu_read_unlock();
3121da177e4SLinus Torvalds 	return 0;
3131da177e4SLinus Torvalds }
3141da177e4SLinus Torvalds 
315d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
31615e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3171da177e4SLinus Torvalds {
3188f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3190ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3200ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3210ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3220ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds 	ASSERT_RTNL();
3251da177e4SLinus Torvalds 
3268f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3278f937c60SHarald Welte 	 * unless alias promotion is set
3288f937c60SHarald Welte 	 **/
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3311da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3340ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3350ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3360ff60a45SJamal Hadi Salim 				last_prim = ifa;
3370ff60a45SJamal Hadi Salim 
3381da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3391da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3401da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3411da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3420ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3431da177e4SLinus Torvalds 				continue;
3441da177e4SLinus Torvalds 			}
3451da177e4SLinus Torvalds 
3460ff60a45SJamal Hadi Salim 			if (!do_promote) {
347fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3481da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3491da177e4SLinus Torvalds 
35015e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
351e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
352e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3531da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3548f937c60SHarald Welte 			} else {
3558f937c60SHarald Welte 				promote = ifa;
3568f937c60SHarald Welte 				break;
3578f937c60SHarald Welte 			}
3581da177e4SLinus Torvalds 		}
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 
3612d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3622d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3632d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3642d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3652d230e2bSJulian Anastasov 	 */
3662d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3672d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3682d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3692d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3702d230e2bSJulian Anastasov 	}
3712d230e2bSJulian Anastasov 
3721da177e4SLinus Torvalds 	/* 2. Unlink it */
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
375fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3801da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3811da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3821da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3831da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3841da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3851da177e4SLinus Torvalds 	   So that, this order is correct.
3861da177e4SLinus Torvalds 	 */
38715e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
388e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3890ff60a45SJamal Hadi Salim 
3900ff60a45SJamal Hadi Salim 	if (promote) {
39104024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
3920ff60a45SJamal Hadi Salim 
3930ff60a45SJamal Hadi Salim 		if (prev_prom) {
3940ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
3950ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
3960ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
3970ff60a45SJamal Hadi Salim 		}
3980ff60a45SJamal Hadi Salim 
3990ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
40015e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
401e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
402e041c683SAlan Stern 				NETDEV_UP, promote);
40304024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4040ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4050ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4060ff60a45SJamal Hadi Salim 					continue;
4070ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4080ff60a45SJamal Hadi Salim 		}
4090ff60a45SJamal Hadi Salim 
4100ff60a45SJamal Hadi Salim 	}
4116363097cSHerbert Xu 	if (destroy)
4121da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
415d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
416d6062cbbSThomas Graf 			 int destroy)
417d6062cbbSThomas Graf {
418d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
419d6062cbbSThomas Graf }
420d6062cbbSThomas Graf 
4215c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4225c766d64SJiri Pirko 
4235c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4245c766d64SJiri Pirko 
425d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
42615e47304SEric W. Biederman 			     u32 portid)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4291da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	ASSERT_RTNL();
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4341da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4351da177e4SLinus Torvalds 		return 0;
4361da177e4SLinus Torvalds 	}
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4391da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4421da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4431da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4441da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4451da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4461da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4471da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4481da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4491da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4501da177e4SLinus Torvalds 				return -EEXIST;
4511da177e4SLinus Torvalds 			}
4521da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4531da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4541da177e4SLinus Torvalds 				return -EINVAL;
4551da177e4SLinus Torvalds 			}
4561da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4571da177e4SLinus Torvalds 		}
4581da177e4SLinus Torvalds 	}
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
4611da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4621da177e4SLinus Torvalds 		ifap = last_primary;
4631da177e4SLinus Torvalds 	}
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4661da177e4SLinus Torvalds 	*ifap = ifa;
4671da177e4SLinus Torvalds 
468fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
469fd23c3b3SDavid S. Miller 
4705c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
4715c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, 0);
4725c766d64SJiri Pirko 
4731da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4741da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4751da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
47615e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
477e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	return 0;
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
482d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
483d6062cbbSThomas Graf {
484d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
485d6062cbbSThomas Graf }
486d6062cbbSThomas Graf 
4871da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4881da177e4SLinus Torvalds {
489e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 	ASSERT_RTNL();
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	if (!in_dev) {
4941da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4951da177e4SLinus Torvalds 		return -ENOBUFS;
4961da177e4SLinus Torvalds 	}
49771e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
4981da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
499547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5001da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5011da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5021da177e4SLinus Torvalds 	}
503f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5041da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5051da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
5088723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5098723e1b4SEric Dumazet  * We dont take a reference on found in_device
5108723e1b4SEric Dumazet  */
5117fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5121da177e4SLinus Torvalds {
5131da177e4SLinus Torvalds 	struct net_device *dev;
5141da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
515c148fc2eSEric Dumazet 
516c148fc2eSEric Dumazet 	rcu_read_lock();
517c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5181da177e4SLinus Torvalds 	if (dev)
5198723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
520c148fc2eSEric Dumazet 	rcu_read_unlock();
5211da177e4SLinus Torvalds 	return in_dev;
5221da177e4SLinus Torvalds }
5239f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5261da177e4SLinus Torvalds 
52760cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
52860cad5daSAl Viro 				    __be32 mask)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	ASSERT_RTNL();
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5331da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5341da177e4SLinus Torvalds 			return ifa;
5351da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5361da177e4SLinus Torvalds 	return NULL;
5371da177e4SLinus Torvalds }
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
5401da177e4SLinus Torvalds {
5413b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
542dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5431da177e4SLinus Torvalds 	struct in_device *in_dev;
544dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5451da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
546dfdd5fd4SThomas Graf 	int err = -EINVAL;
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	ASSERT_RTNL();
5491da177e4SLinus Torvalds 
550dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
551dfdd5fd4SThomas Graf 	if (err < 0)
552dfdd5fd4SThomas Graf 		goto errout;
553dfdd5fd4SThomas Graf 
554dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5557fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
556dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
557dfdd5fd4SThomas Graf 		err = -ENODEV;
558dfdd5fd4SThomas Graf 		goto errout;
559dfdd5fd4SThomas Graf 	}
560dfdd5fd4SThomas Graf 
5611da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5621da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
563dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
564a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5651da177e4SLinus Torvalds 			continue;
566dfdd5fd4SThomas Graf 
567dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
568dfdd5fd4SThomas Graf 			continue;
569dfdd5fd4SThomas Graf 
570dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
571dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
572a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
573dfdd5fd4SThomas Graf 			continue;
574dfdd5fd4SThomas Graf 
57515e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
5761da177e4SLinus Torvalds 		return 0;
5771da177e4SLinus Torvalds 	}
578dfdd5fd4SThomas Graf 
579dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
580dfdd5fd4SThomas Graf errout:
581dfdd5fd4SThomas Graf 	return err;
5821da177e4SLinus Torvalds }
5831da177e4SLinus Torvalds 
5845c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
5855c766d64SJiri Pirko 
5865c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
5875c766d64SJiri Pirko {
5885c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
5895c766d64SJiri Pirko 	struct in_ifaddr *ifa;
5905c766d64SJiri Pirko 	int i;
5915c766d64SJiri Pirko 
5925c766d64SJiri Pirko 	now = jiffies;
5935c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
5945c766d64SJiri Pirko 
5955c766d64SJiri Pirko 	rcu_read_lock();
5965c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
597b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
5985c766d64SJiri Pirko 			unsigned long age;
5995c766d64SJiri Pirko 
6005c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6015c766d64SJiri Pirko 				continue;
6025c766d64SJiri Pirko 
6035c766d64SJiri Pirko 			/* We try to batch several events at once. */
6045c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6055c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6065c766d64SJiri Pirko 
6075c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6085c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
6095c766d64SJiri Pirko 				struct in_ifaddr **ifap ;
6105c766d64SJiri Pirko 
6115c766d64SJiri Pirko 				rtnl_lock();
6125c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
6135c766d64SJiri Pirko 				     *ifap != NULL; ifap = &ifa->ifa_next) {
6145c766d64SJiri Pirko 					if (*ifap == ifa)
6155c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
6165c766d64SJiri Pirko 							     ifap, 1);
6175c766d64SJiri Pirko 				}
6185c766d64SJiri Pirko 				rtnl_unlock();
6195c766d64SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
6205c766d64SJiri Pirko 				   INFINITY_LIFE_TIME) {
6215c766d64SJiri Pirko 				continue;
6225c766d64SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
6235c766d64SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
6245c766d64SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
6255c766d64SJiri Pirko 					next = ifa->ifa_tstamp +
6265c766d64SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
6275c766d64SJiri Pirko 
6285c766d64SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) {
6295c766d64SJiri Pirko 					ifa->ifa_flags |= IFA_F_DEPRECATED;
6305c766d64SJiri Pirko 					rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
6315c766d64SJiri Pirko 				}
6325c766d64SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
6335c766d64SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
6345c766d64SJiri Pirko 					       next)) {
6355c766d64SJiri Pirko 				next = ifa->ifa_tstamp +
6365c766d64SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
6375c766d64SJiri Pirko 			}
6385c766d64SJiri Pirko 		}
6395c766d64SJiri Pirko 	}
6405c766d64SJiri Pirko 	rcu_read_unlock();
6415c766d64SJiri Pirko 
6425c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
6435c766d64SJiri Pirko 	next_sched = next;
6445c766d64SJiri Pirko 
6455c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
6465c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
6475c766d64SJiri Pirko 		next_sched = next_sec;
6485c766d64SJiri Pirko 
6495c766d64SJiri Pirko 	now = jiffies;
6505c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
6515c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
6525c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
6535c766d64SJiri Pirko 
6545c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, next_sched - now);
6555c766d64SJiri Pirko }
6565c766d64SJiri Pirko 
6575c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
6585c766d64SJiri Pirko 			     __u32 prefered_lft)
6595c766d64SJiri Pirko {
6605c766d64SJiri Pirko 	unsigned long timeout;
6615c766d64SJiri Pirko 
6625c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
6635c766d64SJiri Pirko 
6645c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
6655c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
6665c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
6675c766d64SJiri Pirko 	else
6685c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
6695c766d64SJiri Pirko 
6705c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
6715c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
6725c766d64SJiri Pirko 		if (timeout == 0)
6735c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
6745c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
6755c766d64SJiri Pirko 	}
6765c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
6775c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
6785c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
6795c766d64SJiri Pirko }
6805c766d64SJiri Pirko 
6815c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
6825c766d64SJiri Pirko 				       __u32 *pvalid_lft, __u32 *pprefered_lft)
6831da177e4SLinus Torvalds {
6845c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
6855c753978SThomas Graf 	struct in_ifaddr *ifa;
6865c753978SThomas Graf 	struct ifaddrmsg *ifm;
6871da177e4SLinus Torvalds 	struct net_device *dev;
6881da177e4SLinus Torvalds 	struct in_device *in_dev;
6897b218574SDenis V. Lunev 	int err;
6901da177e4SLinus Torvalds 
6915c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
6925c753978SThomas Graf 	if (err < 0)
6935c753978SThomas Graf 		goto errout;
6941da177e4SLinus Torvalds 
6955c753978SThomas Graf 	ifm = nlmsg_data(nlh);
696c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
6977b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
6985c753978SThomas Graf 		goto errout;
6991da177e4SLinus Torvalds 
7004b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
7015c753978SThomas Graf 	err = -ENODEV;
7027b218574SDenis V. Lunev 	if (dev == NULL)
7035c753978SThomas Graf 		goto errout;
7041da177e4SLinus Torvalds 
7055c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
7065c753978SThomas Graf 	err = -ENOBUFS;
7077b218574SDenis V. Lunev 	if (in_dev == NULL)
7085c753978SThomas Graf 		goto errout;
70971e27da9SHerbert Xu 
7105c753978SThomas Graf 	ifa = inet_alloc_ifa();
7117b218574SDenis V. Lunev 	if (ifa == NULL)
7125c753978SThomas Graf 		/*
7135c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
7145c753978SThomas Graf 		 * assigned to its device and is destroy with it.
7155c753978SThomas Graf 		 */
7165c753978SThomas Graf 		goto errout;
7175c753978SThomas Graf 
718a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
7195c753978SThomas Graf 	in_dev_hold(in_dev);
7205c753978SThomas Graf 
7215c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
7225c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
7235c753978SThomas Graf 
724fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
7251da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
7261da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
7271da177e4SLinus Torvalds 	ifa->ifa_flags = ifm->ifa_flags;
7281da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
7291da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
7305c753978SThomas Graf 
731a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
732a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
7335c753978SThomas Graf 
7345c753978SThomas Graf 	if (tb[IFA_BROADCAST])
735a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
7365c753978SThomas Graf 
7375c753978SThomas Graf 	if (tb[IFA_LABEL])
7385c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
7391da177e4SLinus Torvalds 	else
7401da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
7411da177e4SLinus Torvalds 
7425c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
7435c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
7445c766d64SJiri Pirko 
7455c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
7465c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
7475c766d64SJiri Pirko 			err = -EINVAL;
7485c766d64SJiri Pirko 			goto errout;
7495c766d64SJiri Pirko 		}
7505c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
7515c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
7525c766d64SJiri Pirko 	}
7535c766d64SJiri Pirko 
7545c753978SThomas Graf 	return ifa;
7555c753978SThomas Graf 
7565c753978SThomas Graf errout:
7575c753978SThomas Graf 	return ERR_PTR(err);
7585c753978SThomas Graf }
7595c753978SThomas Graf 
7605c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
7615c766d64SJiri Pirko {
7625c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
7635c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
7645c766d64SJiri Pirko 
7655c766d64SJiri Pirko 	if (!ifa->ifa_local)
7665c766d64SJiri Pirko 		return NULL;
7675c766d64SJiri Pirko 
7685c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
7695c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
7705c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
7715c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
7725c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
7735c766d64SJiri Pirko 			return ifa1;
7745c766d64SJiri Pirko 	}
7755c766d64SJiri Pirko 	return NULL;
7765c766d64SJiri Pirko }
7775c766d64SJiri Pirko 
7785c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
7795c753978SThomas Graf {
7803b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
7815c753978SThomas Graf 	struct in_ifaddr *ifa;
7825c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
7835c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
7845c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
7855c753978SThomas Graf 
7865c753978SThomas Graf 	ASSERT_RTNL();
7875c753978SThomas Graf 
7885c766d64SJiri Pirko 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
7895c753978SThomas Graf 	if (IS_ERR(ifa))
7905c753978SThomas Graf 		return PTR_ERR(ifa);
7915c753978SThomas Graf 
7925c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
7935c766d64SJiri Pirko 	if (!ifa_existing) {
7945c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
7955c766d64SJiri Pirko 		 * userspace alreay relies on not having to provide this.
7965c766d64SJiri Pirko 		 */
7975c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
79815e47304SEric W. Biederman 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
7995c766d64SJiri Pirko 	} else {
8005c766d64SJiri Pirko 		inet_free_ifa(ifa);
8015c766d64SJiri Pirko 
8025c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
8035c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
8045c766d64SJiri Pirko 			return -EEXIST;
80534e2ed34SJiri Pirko 		ifa = ifa_existing;
80634e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
807*05a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
808*05a324b9SJiri Pirko 		schedule_delayed_work(&check_lifetime_work, 0);
80934e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
81034e2ed34SJiri Pirko 		blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
8115c766d64SJiri Pirko 	}
8125c766d64SJiri Pirko 	return 0;
8131da177e4SLinus Torvalds }
8141da177e4SLinus Torvalds 
8151da177e4SLinus Torvalds /*
8161da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
8171da177e4SLinus Torvalds  */
8181da177e4SLinus Torvalds 
81940384999SEric Dumazet static int inet_abc_len(__be32 addr)
8201da177e4SLinus Torvalds {
8211da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
8221da177e4SLinus Torvalds 
823f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
8241da177e4SLinus Torvalds 		rc = 0;
8251da177e4SLinus Torvalds 	else {
826714e85beSAl Viro 		__u32 haddr = ntohl(addr);
8271da177e4SLinus Torvalds 
828714e85beSAl Viro 		if (IN_CLASSA(haddr))
8291da177e4SLinus Torvalds 			rc = 8;
830714e85beSAl Viro 		else if (IN_CLASSB(haddr))
8311da177e4SLinus Torvalds 			rc = 16;
832714e85beSAl Viro 		else if (IN_CLASSC(haddr))
8331da177e4SLinus Torvalds 			rc = 24;
8341da177e4SLinus Torvalds 	}
8351da177e4SLinus Torvalds 
8361da177e4SLinus Torvalds 	return rc;
8371da177e4SLinus Torvalds }
8381da177e4SLinus Torvalds 
8391da177e4SLinus Torvalds 
840e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
8411da177e4SLinus Torvalds {
8421da177e4SLinus Torvalds 	struct ifreq ifr;
8431da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
8441da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
8451da177e4SLinus Torvalds 	struct in_device *in_dev;
8461da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
8471da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
8481da177e4SLinus Torvalds 	struct net_device *dev;
8491da177e4SLinus Torvalds 	char *colon;
8501da177e4SLinus Torvalds 	int ret = -EFAULT;
8511da177e4SLinus Torvalds 	int tryaddrmatch = 0;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds 	/*
8541da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
8551da177e4SLinus Torvalds 	 */
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
8581da177e4SLinus Torvalds 		goto out;
8591da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
8601da177e4SLinus Torvalds 
8611da177e4SLinus Torvalds 	/* save original address for comparison */
8621da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
8651da177e4SLinus Torvalds 	if (colon)
8661da177e4SLinus Torvalds 		*colon = 0;
8671da177e4SLinus Torvalds 
868e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds 	switch (cmd) {
8711da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
8721da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
8731da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
8741da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
8751da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
8761da177e4SLinus Torvalds 		   so that we do not impose a lock.
8771da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
8781da177e4SLinus Torvalds 		 */
8791da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
8801da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
8811da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
8821da177e4SLinus Torvalds 		break;
8831da177e4SLinus Torvalds 
8841da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
885bf5b30b8SZhao Hongjiang 		ret = -EPERM;
88652e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
8871da177e4SLinus Torvalds 			goto out;
8881da177e4SLinus Torvalds 		break;
8891da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
8901da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
8911da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
8921da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
893bf5b30b8SZhao Hongjiang 		ret = -EPERM;
89452e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
8951da177e4SLinus Torvalds 			goto out;
8961da177e4SLinus Torvalds 		ret = -EINVAL;
8971da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
8981da177e4SLinus Torvalds 			goto out;
8991da177e4SLinus Torvalds 		break;
9001da177e4SLinus Torvalds 	default:
9011da177e4SLinus Torvalds 		ret = -EINVAL;
9021da177e4SLinus Torvalds 		goto out;
9031da177e4SLinus Torvalds 	}
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	rtnl_lock();
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds 	ret = -ENODEV;
9089f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
9099f9354b9SEric Dumazet 	if (!dev)
9101da177e4SLinus Torvalds 		goto done;
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds 	if (colon)
9131da177e4SLinus Torvalds 		*colon = ':';
9141da177e4SLinus Torvalds 
9159f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
9169f9354b9SEric Dumazet 	if (in_dev) {
9171da177e4SLinus Torvalds 		if (tryaddrmatch) {
9181da177e4SLinus Torvalds 			/* Matthias Andree */
9191da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
9201da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
9211da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
9221da177e4SLinus Torvalds 			   This is checked above. */
9231da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
9241da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
9251da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
9261da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
9276c91afe1SDavid S. Miller 							ifa->ifa_local) {
9281da177e4SLinus Torvalds 					break; /* found */
9291da177e4SLinus Torvalds 				}
9301da177e4SLinus Torvalds 			}
9311da177e4SLinus Torvalds 		}
9321da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
9331da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
9341da177e4SLinus Torvalds 		   comparing just the label */
9351da177e4SLinus Torvalds 		if (!ifa) {
9361da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
9371da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
9381da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
9391da177e4SLinus Torvalds 					break;
9401da177e4SLinus Torvalds 		}
9411da177e4SLinus Torvalds 	}
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
9441da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
9451da177e4SLinus Torvalds 		goto done;
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds 	switch (cmd) {
9481da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9491da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
9501da177e4SLinus Torvalds 		goto rarok;
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9531da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
9541da177e4SLinus Torvalds 		goto rarok;
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9571da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
9581da177e4SLinus Torvalds 		goto rarok;
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9611da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
9621da177e4SLinus Torvalds 		goto rarok;
9631da177e4SLinus Torvalds 
9641da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
9651da177e4SLinus Torvalds 		if (colon) {
9661da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
9671da177e4SLinus Torvalds 			if (!ifa)
9681da177e4SLinus Torvalds 				break;
9691da177e4SLinus Torvalds 			ret = 0;
9701da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
9711da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
9721da177e4SLinus Torvalds 			break;
9731da177e4SLinus Torvalds 		}
9741da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
9751da177e4SLinus Torvalds 		break;
9761da177e4SLinus Torvalds 
9771da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
9781da177e4SLinus Torvalds 		ret = -EINVAL;
9791da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
9801da177e4SLinus Torvalds 			break;
9811da177e4SLinus Torvalds 
9821da177e4SLinus Torvalds 		if (!ifa) {
9831da177e4SLinus Torvalds 			ret = -ENOBUFS;
9849f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
9859f9354b9SEric Dumazet 			if (!ifa)
9861da177e4SLinus Torvalds 				break;
987c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
9881da177e4SLinus Torvalds 			if (colon)
9891da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
9901da177e4SLinus Torvalds 			else
9911da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
9921da177e4SLinus Torvalds 		} else {
9931da177e4SLinus Torvalds 			ret = 0;
9941da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
9951da177e4SLinus Torvalds 				break;
9961da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
9971da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
998148f9729SBjorn Mork 			ifa->ifa_scope = 0;
9991da177e4SLinus Torvalds 		}
10001da177e4SLinus Torvalds 
10011da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
10041da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
10051da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
10061da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10071da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
10081da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
10091da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
10101da177e4SLinus Torvalds 		} else {
10111da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
10121da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
10131da177e4SLinus Torvalds 		}
10145c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
10151da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
10161da177e4SLinus Torvalds 		break;
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10191da177e4SLinus Torvalds 		ret = 0;
10201da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
10211da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10221da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
10231da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
10241da177e4SLinus Torvalds 		}
10251da177e4SLinus Torvalds 		break;
10261da177e4SLinus Torvalds 
10271da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10281da177e4SLinus Torvalds 		ret = 0;
10291da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
10301da177e4SLinus Torvalds 			break;
10311da177e4SLinus Torvalds 		ret = -EINVAL;
10321da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10331da177e4SLinus Torvalds 			break;
10341da177e4SLinus Torvalds 		ret = 0;
10351da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
10361da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
10371da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
10381da177e4SLinus Torvalds 		break;
10391da177e4SLinus Torvalds 
10401da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds 		/*
10431da177e4SLinus Torvalds 		 *	The mask we set must be legal.
10441da177e4SLinus Torvalds 		 */
10451da177e4SLinus Torvalds 		ret = -EINVAL;
10461da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
10471da177e4SLinus Torvalds 			break;
10481da177e4SLinus Torvalds 		ret = 0;
10491da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1050a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
10511da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10521da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
10531da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
10541da177e4SLinus Torvalds 
10551da177e4SLinus Torvalds 			/* See if current broadcast address matches
10561da177e4SLinus Torvalds 			 * with current netmask, then recalculate
10571da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
10581da177e4SLinus Torvalds 			 * funny address, so don't touch it since
10591da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
10601da177e4SLinus Torvalds 			 */
10611da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10621da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
10631da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1064dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
10651da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
10661da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
10671da177e4SLinus Torvalds 			}
10681da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
10691da177e4SLinus Torvalds 		}
10701da177e4SLinus Torvalds 		break;
10711da177e4SLinus Torvalds 	}
10721da177e4SLinus Torvalds done:
10731da177e4SLinus Torvalds 	rtnl_unlock();
10741da177e4SLinus Torvalds out:
10751da177e4SLinus Torvalds 	return ret;
10761da177e4SLinus Torvalds rarok:
10771da177e4SLinus Torvalds 	rtnl_unlock();
10781da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
10791da177e4SLinus Torvalds 	goto out;
10801da177e4SLinus Torvalds }
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
10831da177e4SLinus Torvalds {
1084e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
10851da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
10861da177e4SLinus Torvalds 	struct ifreq ifr;
10871da177e4SLinus Torvalds 	int done = 0;
10881da177e4SLinus Torvalds 
10899f9354b9SEric Dumazet 	if (!in_dev)
10901da177e4SLinus Torvalds 		goto out;
10911da177e4SLinus Torvalds 
10929f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
10931da177e4SLinus Torvalds 		if (!buf) {
10941da177e4SLinus Torvalds 			done += sizeof(ifr);
10951da177e4SLinus Torvalds 			continue;
10961da177e4SLinus Torvalds 		}
10971da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
10981da177e4SLinus Torvalds 			break;
10991da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
11001da177e4SLinus Torvalds 		if (ifa->ifa_label)
11011da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, ifa->ifa_label);
11021da177e4SLinus Torvalds 		else
11031da177e4SLinus Torvalds 			strcpy(ifr.ifr_name, dev->name);
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
11061da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
11071da177e4SLinus Torvalds 								ifa->ifa_local;
11081da177e4SLinus Torvalds 
11091da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
11101da177e4SLinus Torvalds 			done = -EFAULT;
11111da177e4SLinus Torvalds 			break;
11121da177e4SLinus Torvalds 		}
11131da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
11141da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
11151da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
11161da177e4SLinus Torvalds 	}
11171da177e4SLinus Torvalds out:
11181da177e4SLinus Torvalds 	return done;
11191da177e4SLinus Torvalds }
11201da177e4SLinus Torvalds 
1121a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
11221da177e4SLinus Torvalds {
1123a61ced5dSAl Viro 	__be32 addr = 0;
11241da177e4SLinus Torvalds 	struct in_device *in_dev;
1125c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds 	rcu_read_lock();
1128e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
11291da177e4SLinus Torvalds 	if (!in_dev)
11301da177e4SLinus Torvalds 		goto no_in_dev;
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
11331da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
11341da177e4SLinus Torvalds 			continue;
11351da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
11361da177e4SLinus Torvalds 			addr = ifa->ifa_local;
11371da177e4SLinus Torvalds 			break;
11381da177e4SLinus Torvalds 		}
11391da177e4SLinus Torvalds 		if (!addr)
11401da177e4SLinus Torvalds 			addr = ifa->ifa_local;
11411da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
11421da177e4SLinus Torvalds 
11431da177e4SLinus Torvalds 	if (addr)
1144c6d14c84SEric Dumazet 		goto out_unlock;
11459f9354b9SEric Dumazet no_in_dev:
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
11481da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
11491da177e4SLinus Torvalds 	   in dev_base list.
11501da177e4SLinus Torvalds 	 */
1151c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
11529f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
11539f9354b9SEric Dumazet 		if (!in_dev)
11541da177e4SLinus Torvalds 			continue;
11551da177e4SLinus Torvalds 
11561da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
11571da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
11581da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
11591da177e4SLinus Torvalds 				addr = ifa->ifa_local;
1160c6d14c84SEric Dumazet 				goto out_unlock;
11611da177e4SLinus Torvalds 			}
11621da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
11631da177e4SLinus Torvalds 	}
1164c6d14c84SEric Dumazet out_unlock:
11651da177e4SLinus Torvalds 	rcu_read_unlock();
11661da177e4SLinus Torvalds 	return addr;
11671da177e4SLinus Torvalds }
11689f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
11691da177e4SLinus Torvalds 
117060cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
117160cad5daSAl Viro 			      __be32 local, int scope)
11721da177e4SLinus Torvalds {
11731da177e4SLinus Torvalds 	int same = 0;
1174a144ea4bSAl Viro 	__be32 addr = 0;
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 	for_ifa(in_dev) {
11771da177e4SLinus Torvalds 		if (!addr &&
11781da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
11791da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
11801da177e4SLinus Torvalds 			addr = ifa->ifa_local;
11811da177e4SLinus Torvalds 			if (same)
11821da177e4SLinus Torvalds 				break;
11831da177e4SLinus Torvalds 		}
11841da177e4SLinus Torvalds 		if (!same) {
11851da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
11861da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
11871da177e4SLinus Torvalds 			if (same && addr) {
11881da177e4SLinus Torvalds 				if (local || !dst)
11891da177e4SLinus Torvalds 					break;
11901da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
11911da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
11921da177e4SLinus Torvalds 					break;
11931da177e4SLinus Torvalds 				/* No, then can we use new local src? */
11941da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
11951da177e4SLinus Torvalds 					addr = ifa->ifa_local;
11961da177e4SLinus Torvalds 					break;
11971da177e4SLinus Torvalds 				}
11981da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
11991da177e4SLinus Torvalds 				same = 0;
12001da177e4SLinus Torvalds 			}
12011da177e4SLinus Torvalds 		}
12021da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds 	return same ? addr : 0;
12051da177e4SLinus Torvalds }
12061da177e4SLinus Torvalds 
12071da177e4SLinus Torvalds /*
12081da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
12099bd85e32SDenis V. Lunev  * - in_dev: only on this interface, 0=any interface
12101da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
12111da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
12121da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
12131da177e4SLinus Torvalds  */
12149bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev,
12159bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
12161da177e4SLinus Torvalds {
121760cad5daSAl Viro 	__be32 addr = 0;
12189bd85e32SDenis V. Lunev 	struct net_device *dev;
121939a6d063SDenis V. Lunev 	struct net *net;
12201da177e4SLinus Torvalds 
122139a6d063SDenis V. Lunev 	if (scope != RT_SCOPE_LINK)
12229bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
12231da177e4SLinus Torvalds 
1224c346dca1SYOSHIFUJI Hideaki 	net = dev_net(in_dev->dev);
12251da177e4SLinus Torvalds 	rcu_read_lock();
1226c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
12279f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
12289f9354b9SEric Dumazet 		if (in_dev) {
12291da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
12301da177e4SLinus Torvalds 			if (addr)
12311da177e4SLinus Torvalds 				break;
12321da177e4SLinus Torvalds 		}
12331da177e4SLinus Torvalds 	}
12341da177e4SLinus Torvalds 	rcu_read_unlock();
12351da177e4SLinus Torvalds 
12361da177e4SLinus Torvalds 	return addr;
12371da177e4SLinus Torvalds }
1238eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
12391da177e4SLinus Torvalds 
12401da177e4SLinus Torvalds /*
12411da177e4SLinus Torvalds  *	Device notifier
12421da177e4SLinus Torvalds  */
12431da177e4SLinus Torvalds 
12441da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
12451da177e4SLinus Torvalds {
1246e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
12471da177e4SLinus Torvalds }
12489f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
12491da177e4SLinus Torvalds 
12501da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
12511da177e4SLinus Torvalds {
1252e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
12531da177e4SLinus Torvalds }
12549f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
12551da177e4SLinus Torvalds 
12569f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
12579f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
12581da177e4SLinus Torvalds */
12591da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
12601da177e4SLinus Torvalds {
12611da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12621da177e4SLinus Torvalds 	int named = 0;
12631da177e4SLinus Torvalds 
12641da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12651da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
12661da177e4SLinus Torvalds 
12671da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
12681da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
12691da177e4SLinus Torvalds 		if (named++ == 0)
1270573bf470SThomas Graf 			goto skip;
127144344b2aSMark McLoughlin 		dot = strchr(old, ':');
12721da177e4SLinus Torvalds 		if (dot == NULL) {
12731da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
12741da177e4SLinus Torvalds 			dot = old;
12751da177e4SLinus Torvalds 		}
12769f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
12771da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
12789f9354b9SEric Dumazet 		else
12791da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1280573bf470SThomas Graf skip:
1281573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
12821da177e4SLinus Torvalds 	}
12831da177e4SLinus Torvalds }
12841da177e4SLinus Torvalds 
128540384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
128606770843SBreno Leitao {
128706770843SBreno Leitao 	return mtu >= 68;
128806770843SBreno Leitao }
128906770843SBreno Leitao 
1290d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1291d11327adSIan Campbell 					struct in_device *in_dev)
1292d11327adSIan Campbell 
1293d11327adSIan Campbell {
1294b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1295d11327adSIan Campbell 
1296b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1297b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1298d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
12996c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
13006c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1301d11327adSIan Campbell 			 dev->dev_addr, NULL);
1302d11327adSIan Campbell 	}
1303b76d0789SZoltan Kiss }
1304d11327adSIan Campbell 
13051da177e4SLinus Torvalds /* Called only under RTNL semaphore */
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
13081da177e4SLinus Torvalds 			 void *ptr)
13091da177e4SLinus Torvalds {
13101da177e4SLinus Torvalds 	struct net_device *dev = ptr;
1311748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13121da177e4SLinus Torvalds 
13131da177e4SLinus Torvalds 	ASSERT_RTNL();
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds 	if (!in_dev) {
13168030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
13171da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
13188d76527eSHerbert Xu 			if (!in_dev)
1319b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
13200cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
132142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
132242f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
13231da177e4SLinus Torvalds 			}
132406770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
132506770843SBreno Leitao 			/* Re-enabling IP */
132606770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
132706770843SBreno Leitao 				in_dev = inetdev_init(dev);
13288030f544SHerbert Xu 		}
13291da177e4SLinus Torvalds 		goto out;
13301da177e4SLinus Torvalds 	}
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 	switch (event) {
13331da177e4SLinus Torvalds 	case NETDEV_REGISTER:
133491df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1335a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
13361da177e4SLinus Torvalds 		break;
13371da177e4SLinus Torvalds 	case NETDEV_UP:
133806770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
13391da177e4SLinus Torvalds 			break;
13400cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
13419f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
13429f9354b9SEric Dumazet 
13439f9354b9SEric Dumazet 			if (ifa) {
1344fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
13451da177e4SLinus Torvalds 				ifa->ifa_local =
13461da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
13471da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
13481da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
13491da177e4SLinus Torvalds 				in_dev_hold(in_dev);
13501da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
13511da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
13521da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
13535c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
13545c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
13551da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
13561da177e4SLinus Torvalds 			}
13571da177e4SLinus Torvalds 		}
13581da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1359eefef1cfSStephen Hemminger 		/* fall through */
1360eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1361d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1362d11327adSIan Campbell 			break;
1363d11327adSIan Campbell 		/* fall through */
1364d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1365a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1366d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
13671da177e4SLinus Torvalds 		break;
13681da177e4SLinus Torvalds 	case NETDEV_DOWN:
13691da177e4SLinus Torvalds 		ip_mc_down(in_dev);
13701da177e4SLinus Torvalds 		break;
137193d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
137275c78500SMoni Shoua 		ip_mc_unmap(in_dev);
137375c78500SMoni Shoua 		break;
137493d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
137575c78500SMoni Shoua 		ip_mc_remap(in_dev);
137675c78500SMoni Shoua 		break;
13771da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
137806770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
13791da177e4SLinus Torvalds 			break;
138006770843SBreno Leitao 		/* disable IP when MTU is not enough */
13811da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
13821da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
13831da177e4SLinus Torvalds 		break;
13841da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
13851da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
13861da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
13871da177e4SLinus Torvalds 		 */
13881da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
13891da177e4SLinus Torvalds 
139051602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
139166f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
13921da177e4SLinus Torvalds 		break;
13931da177e4SLinus Torvalds 	}
13941da177e4SLinus Torvalds out:
13951da177e4SLinus Torvalds 	return NOTIFY_DONE;
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
13991da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
14001da177e4SLinus Torvalds };
14011da177e4SLinus Torvalds 
140240384999SEric Dumazet static size_t inet_nlmsg_size(void)
1403339bf98fSThomas Graf {
1404339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1405339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1406339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1407339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1408339bf98fSThomas Graf 	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1409339bf98fSThomas Graf }
1410339bf98fSThomas Graf 
14115c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
14125c766d64SJiri Pirko {
14135c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
14145c766d64SJiri Pirko }
14155c766d64SJiri Pirko 
14165c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
14175c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
14185c766d64SJiri Pirko {
14195c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
14205c766d64SJiri Pirko 
14215c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
14225c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
14235c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
14245c766d64SJiri Pirko 	ci.ifa_valid = valid;
14255c766d64SJiri Pirko 
14265c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
14275c766d64SJiri Pirko }
14285c766d64SJiri Pirko 
14291da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
143015e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
14311da177e4SLinus Torvalds {
14321da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
14331da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
14345c766d64SJiri Pirko 	u32 preferred, valid;
14351da177e4SLinus Torvalds 
143615e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
143747f68512SThomas Graf 	if (nlh == NULL)
143826932566SPatrick McHardy 		return -EMSGSIZE;
143947f68512SThomas Graf 
144047f68512SThomas Graf 	ifm = nlmsg_data(nlh);
14411da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
14421da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
14435c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
14441da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
14451da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
14461da177e4SLinus Torvalds 
14475c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
14485c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
14495c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
14505c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
14515c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
14525c766d64SJiri Pirko 
14535c766d64SJiri Pirko 			if (preferred > tval)
14545c766d64SJiri Pirko 				preferred -= tval;
14555c766d64SJiri Pirko 			else
14565c766d64SJiri Pirko 				preferred = 0;
14575c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
14585c766d64SJiri Pirko 				if (valid > tval)
14595c766d64SJiri Pirko 					valid -= tval;
14605c766d64SJiri Pirko 				else
14615c766d64SJiri Pirko 					valid = 0;
14625c766d64SJiri Pirko 			}
14635c766d64SJiri Pirko 		}
14645c766d64SJiri Pirko 	} else {
14655c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
14665c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
14675c766d64SJiri Pirko 	}
1468f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1469f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1470f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1471f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) ||
1472f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1473f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1474f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
14755c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
14765c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
14775c766d64SJiri Pirko 			  preferred, valid))
1478f3756b79SDavid S. Miller 		goto nla_put_failure;
147947f68512SThomas Graf 
148047f68512SThomas Graf 	return nlmsg_end(skb, nlh);
148147f68512SThomas Graf 
148247f68512SThomas Graf nla_put_failure:
148326932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
148426932566SPatrick McHardy 	return -EMSGSIZE;
14851da177e4SLinus Torvalds }
14861da177e4SLinus Torvalds 
14871da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
14881da177e4SLinus Torvalds {
14893b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1490eec4df98SEric Dumazet 	int h, s_h;
1491eec4df98SEric Dumazet 	int idx, s_idx;
1492eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
14931da177e4SLinus Torvalds 	struct net_device *dev;
14941da177e4SLinus Torvalds 	struct in_device *in_dev;
14951da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1496eec4df98SEric Dumazet 	struct hlist_head *head;
14971da177e4SLinus Torvalds 
1498eec4df98SEric Dumazet 	s_h = cb->args[0];
1499eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1500eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1501eec4df98SEric Dumazet 
1502eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
15037562f876SPavel Emelianov 		idx = 0;
1504eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1505eec4df98SEric Dumazet 		rcu_read_lock();
1506b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
15071da177e4SLinus Torvalds 			if (idx < s_idx)
15087562f876SPavel Emelianov 				goto cont;
15094b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
15101da177e4SLinus Torvalds 				s_ip_idx = 0;
1511eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
15129f9354b9SEric Dumazet 			if (!in_dev)
15137562f876SPavel Emelianov 				goto cont;
15141da177e4SLinus Torvalds 
15151da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
15161da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
15171da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1518596e4150SStephen Hemminger 					continue;
1519eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
152015e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
15211da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1522eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1523eec4df98SEric Dumazet 					rcu_read_unlock();
15241da177e4SLinus Torvalds 					goto done;
15251da177e4SLinus Torvalds 				}
1526eec4df98SEric Dumazet 			}
15277562f876SPavel Emelianov cont:
15287562f876SPavel Emelianov 			idx++;
15291da177e4SLinus Torvalds 		}
1530eec4df98SEric Dumazet 		rcu_read_unlock();
1531eec4df98SEric Dumazet 	}
15321da177e4SLinus Torvalds 
15331da177e4SLinus Torvalds done:
1534eec4df98SEric Dumazet 	cb->args[0] = h;
1535eec4df98SEric Dumazet 	cb->args[1] = idx;
1536eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
15371da177e4SLinus Torvalds 
15381da177e4SLinus Torvalds 	return skb->len;
15391da177e4SLinus Torvalds }
15401da177e4SLinus Torvalds 
1541d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
154215e47304SEric W. Biederman 		      u32 portid)
15431da177e4SLinus Torvalds {
154447f68512SThomas Graf 	struct sk_buff *skb;
1545d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1546d6062cbbSThomas Graf 	int err = -ENOBUFS;
15474b8aa9abSDenis V. Lunev 	struct net *net;
15481da177e4SLinus Torvalds 
1549c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1550339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
155147f68512SThomas Graf 	if (skb == NULL)
1552d6062cbbSThomas Graf 		goto errout;
1553d6062cbbSThomas Graf 
155415e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
155526932566SPatrick McHardy 	if (err < 0) {
155626932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
155726932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
155826932566SPatrick McHardy 		kfree_skb(skb);
155926932566SPatrick McHardy 		goto errout;
156026932566SPatrick McHardy 	}
156115e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
15621ce85fe4SPablo Neira Ayuso 	return;
1563d6062cbbSThomas Graf errout:
1564d6062cbbSThomas Graf 	if (err < 0)
15654b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
15661da177e4SLinus Torvalds }
15671da177e4SLinus Torvalds 
15689f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
15699f0f7272SThomas Graf {
15701fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
15719f0f7272SThomas Graf 
15729f0f7272SThomas Graf 	if (!in_dev)
15739f0f7272SThomas Graf 		return 0;
15749f0f7272SThomas Graf 
15759f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
15769f0f7272SThomas Graf }
15779f0f7272SThomas Graf 
15789f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
15799f0f7272SThomas Graf {
15801fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
15819f0f7272SThomas Graf 	struct nlattr *nla;
15829f0f7272SThomas Graf 	int i;
15839f0f7272SThomas Graf 
15849f0f7272SThomas Graf 	if (!in_dev)
15859f0f7272SThomas Graf 		return -ENODATA;
15869f0f7272SThomas Graf 
15879f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
15889f0f7272SThomas Graf 	if (nla == NULL)
15899f0f7272SThomas Graf 		return -EMSGSIZE;
15909f0f7272SThomas Graf 
15919f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
15929f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
15939f0f7272SThomas Graf 
15949f0f7272SThomas Graf 	return 0;
15959f0f7272SThomas Graf }
15969f0f7272SThomas Graf 
15979f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
15989f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
15999f0f7272SThomas Graf };
16009f0f7272SThomas Graf 
1601cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1602cf7afbfeSThomas Graf 				 const struct nlattr *nla)
16039f0f7272SThomas Graf {
16049f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
16059f0f7272SThomas Graf 	int err, rem;
16069f0f7272SThomas Graf 
1607f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1608cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
16099f0f7272SThomas Graf 
16109f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
16119f0f7272SThomas Graf 	if (err < 0)
16129f0f7272SThomas Graf 		return err;
16139f0f7272SThomas Graf 
16149f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
16159f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
16169f0f7272SThomas Graf 			int cfgid = nla_type(a);
16179f0f7272SThomas Graf 
16189f0f7272SThomas Graf 			if (nla_len(a) < 4)
16199f0f7272SThomas Graf 				return -EINVAL;
16209f0f7272SThomas Graf 
16219f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
16229f0f7272SThomas Graf 				return -EINVAL;
16239f0f7272SThomas Graf 		}
16249f0f7272SThomas Graf 	}
16259f0f7272SThomas Graf 
1626cf7afbfeSThomas Graf 	return 0;
1627cf7afbfeSThomas Graf }
1628cf7afbfeSThomas Graf 
1629cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1630cf7afbfeSThomas Graf {
1631f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1632cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1633cf7afbfeSThomas Graf 	int rem;
1634cf7afbfeSThomas Graf 
1635cf7afbfeSThomas Graf 	if (!in_dev)
1636cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1637cf7afbfeSThomas Graf 
1638cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1639cf7afbfeSThomas Graf 		BUG();
1640cf7afbfeSThomas Graf 
16419f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
16429f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
16439f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
16449f0f7272SThomas Graf 	}
16459f0f7272SThomas Graf 
16469f0f7272SThomas Graf 	return 0;
16479f0f7272SThomas Graf }
16489f0f7272SThomas Graf 
1649edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1650edc9e748SNicolas Dichtel {
1651edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1652edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1653edc9e748SNicolas Dichtel 
16549e551110SNicolas Dichtel 	/* type -1 is used for ALL */
16559e551110SNicolas Dichtel 	if (type == -1 || type == NETCONFA_FORWARDING)
1656edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1657cc535dfbSNicolas Dichtel 	if (type == -1 || type == NETCONFA_RP_FILTER)
1658cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1659d67b8c61SNicolas Dichtel 	if (type == -1 || type == NETCONFA_MC_FORWARDING)
1660d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
1661edc9e748SNicolas Dichtel 
1662edc9e748SNicolas Dichtel 	return size;
1663edc9e748SNicolas Dichtel }
1664edc9e748SNicolas Dichtel 
1665edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1666edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1667edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1668edc9e748SNicolas Dichtel 				     int type)
1669edc9e748SNicolas Dichtel {
1670edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1671edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1672edc9e748SNicolas Dichtel 
1673edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1674edc9e748SNicolas Dichtel 			flags);
1675edc9e748SNicolas Dichtel 	if (nlh == NULL)
1676edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1677edc9e748SNicolas Dichtel 
1678edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1679edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1680edc9e748SNicolas Dichtel 
1681edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1682edc9e748SNicolas Dichtel 		goto nla_put_failure;
1683edc9e748SNicolas Dichtel 
16849e551110SNicolas Dichtel 	/* type -1 is used for ALL */
16859e551110SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_FORWARDING) &&
1686edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1687edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1688edc9e748SNicolas Dichtel 		goto nla_put_failure;
1689cc535dfbSNicolas Dichtel 	if ((type == -1 || type == NETCONFA_RP_FILTER) &&
1690cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1691cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1692cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1693d67b8c61SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
1694d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1695d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1696d67b8c61SNicolas Dichtel 		goto nla_put_failure;
1697edc9e748SNicolas Dichtel 
1698edc9e748SNicolas Dichtel 	return nlmsg_end(skb, nlh);
1699edc9e748SNicolas Dichtel 
1700edc9e748SNicolas Dichtel nla_put_failure:
1701edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1702edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1703edc9e748SNicolas Dichtel }
1704edc9e748SNicolas Dichtel 
1705d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
1706edc9e748SNicolas Dichtel 				 struct ipv4_devconf *devconf)
1707edc9e748SNicolas Dichtel {
1708edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1709edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1710edc9e748SNicolas Dichtel 
1711edc9e748SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
1712edc9e748SNicolas Dichtel 	if (skb == NULL)
1713edc9e748SNicolas Dichtel 		goto errout;
1714edc9e748SNicolas Dichtel 
1715edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
1716edc9e748SNicolas Dichtel 					RTM_NEWNETCONF, 0, type);
1717edc9e748SNicolas Dichtel 	if (err < 0) {
1718edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1719edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1720edc9e748SNicolas Dichtel 		kfree_skb(skb);
1721edc9e748SNicolas Dichtel 		goto errout;
1722edc9e748SNicolas Dichtel 	}
1723edc9e748SNicolas Dichtel 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC);
1724edc9e748SNicolas Dichtel 	return;
1725edc9e748SNicolas Dichtel errout:
1726edc9e748SNicolas Dichtel 	if (err < 0)
1727edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1728edc9e748SNicolas Dichtel }
1729edc9e748SNicolas Dichtel 
17309e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
17319e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
17329e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1733cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
17349e551110SNicolas Dichtel };
17359e551110SNicolas Dichtel 
17369e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
17379e551110SNicolas Dichtel 				    struct nlmsghdr *nlh,
17389e551110SNicolas Dichtel 				    void *arg)
17399e551110SNicolas Dichtel {
17409e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
17419e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
17429e551110SNicolas Dichtel 	struct netconfmsg *ncm;
17439e551110SNicolas Dichtel 	struct sk_buff *skb;
17449e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
17459e551110SNicolas Dichtel 	struct in_device *in_dev;
17469e551110SNicolas Dichtel 	struct net_device *dev;
17479e551110SNicolas Dichtel 	int ifindex;
17489e551110SNicolas Dichtel 	int err;
17499e551110SNicolas Dichtel 
17509e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
17519e551110SNicolas Dichtel 			  devconf_ipv4_policy);
17529e551110SNicolas Dichtel 	if (err < 0)
17539e551110SNicolas Dichtel 		goto errout;
17549e551110SNicolas Dichtel 
17559e551110SNicolas Dichtel 	err = EINVAL;
17569e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
17579e551110SNicolas Dichtel 		goto errout;
17589e551110SNicolas Dichtel 
17599e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
17609e551110SNicolas Dichtel 	switch (ifindex) {
17619e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
17629e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
17639e551110SNicolas Dichtel 		break;
17649e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
17659e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
17669e551110SNicolas Dichtel 		break;
17679e551110SNicolas Dichtel 	default:
17689e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
17699e551110SNicolas Dichtel 		if (dev == NULL)
17709e551110SNicolas Dichtel 			goto errout;
17719e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
17729e551110SNicolas Dichtel 		if (in_dev == NULL)
17739e551110SNicolas Dichtel 			goto errout;
17749e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
17759e551110SNicolas Dichtel 		break;
17769e551110SNicolas Dichtel 	}
17779e551110SNicolas Dichtel 
17789e551110SNicolas Dichtel 	err = -ENOBUFS;
17799e551110SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
17809e551110SNicolas Dichtel 	if (skb == NULL)
17819e551110SNicolas Dichtel 		goto errout;
17829e551110SNicolas Dichtel 
17839e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
17849e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
17859e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
17869e551110SNicolas Dichtel 					-1);
17879e551110SNicolas Dichtel 	if (err < 0) {
17889e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
17899e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
17909e551110SNicolas Dichtel 		kfree_skb(skb);
17919e551110SNicolas Dichtel 		goto errout;
17929e551110SNicolas Dichtel 	}
17939e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
17949e551110SNicolas Dichtel errout:
17959e551110SNicolas Dichtel 	return err;
17969e551110SNicolas Dichtel }
17979e551110SNicolas Dichtel 
17981da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
17991da177e4SLinus Torvalds 
1800c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
180131be3085SHerbert Xu {
180231be3085SHerbert Xu 	struct net_device *dev;
180331be3085SHerbert Xu 
180431be3085SHerbert Xu 	rcu_read_lock();
1805c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1806c6d14c84SEric Dumazet 		struct in_device *in_dev;
1807c6d14c84SEric Dumazet 
180831be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
180931be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
18109355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1811c6d14c84SEric Dumazet 	}
181231be3085SHerbert Xu 	rcu_read_unlock();
181331be3085SHerbert Xu }
181431be3085SHerbert Xu 
1815c6d14c84SEric Dumazet /* called with RTNL locked */
1816c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
181768dd299bSPavel Emelyanov {
181868dd299bSPavel Emelyanov 	struct net_device *dev;
1819586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
182068dd299bSPavel Emelyanov 
1821586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
18229355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
1823edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1824edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
1825edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
1826edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1827edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
1828edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
182968dd299bSPavel Emelyanov 
1830c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
183168dd299bSPavel Emelyanov 		struct in_device *in_dev;
18320187bdfbSBen Hutchings 		if (on)
18330187bdfbSBen Hutchings 			dev_disable_lro(dev);
183468dd299bSPavel Emelyanov 		rcu_read_lock();
183568dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
1836edc9e748SNicolas Dichtel 		if (in_dev) {
183768dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1838edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1839edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
1840edc9e748SNicolas Dichtel 		}
184168dd299bSPavel Emelyanov 		rcu_read_unlock();
184268dd299bSPavel Emelyanov 	}
184368dd299bSPavel Emelyanov }
184468dd299bSPavel Emelyanov 
184531be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write,
18468d65af78SAlexey Dobriyan 			     void __user *buffer,
184731be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
184831be3085SHerbert Xu {
1849d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
18508d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
1851d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
185231be3085SHerbert Xu 
185331be3085SHerbert Xu 	if (write) {
185431be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1855c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
185631be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
185731be3085SHerbert Xu 
185831be3085SHerbert Xu 		set_bit(i, cnf->state);
185931be3085SHerbert Xu 
18609355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1861c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
1862d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
1863d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
1864d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
18654ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
1866cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
1867cc535dfbSNicolas Dichtel 		    new_value != old_value) {
1868cc535dfbSNicolas Dichtel 			int ifindex;
1869cc535dfbSNicolas Dichtel 
1870cc535dfbSNicolas Dichtel 			if (cnf == net->ipv4.devconf_dflt)
1871cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_DEFAULT;
1872cc535dfbSNicolas Dichtel 			else if (cnf == net->ipv4.devconf_all)
1873cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_ALL;
1874cc535dfbSNicolas Dichtel 			else {
1875cc535dfbSNicolas Dichtel 				struct in_device *idev =
1876cc535dfbSNicolas Dichtel 					container_of(cnf, struct in_device,
1877cc535dfbSNicolas Dichtel 						     cnf);
1878cc535dfbSNicolas Dichtel 				ifindex = idev->dev->ifindex;
1879cc535dfbSNicolas Dichtel 			}
1880cc535dfbSNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER,
1881cc535dfbSNicolas Dichtel 						    ifindex, cnf);
1882cc535dfbSNicolas Dichtel 		}
188331be3085SHerbert Xu 	}
188431be3085SHerbert Xu 
188531be3085SHerbert Xu 	return ret;
188631be3085SHerbert Xu }
188731be3085SHerbert Xu 
18881da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write,
18898d65af78SAlexey Dobriyan 				  void __user *buffer,
18901da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
18911da177e4SLinus Torvalds {
18921da177e4SLinus Torvalds 	int *valp = ctl->data;
18931da177e4SLinus Torvalds 	int val = *valp;
189488af182eSEric W. Biederman 	loff_t pos = *ppos;
18958d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
18961da177e4SLinus Torvalds 
18971da177e4SLinus Torvalds 	if (write && *valp != val) {
1898c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
1899c0ce9fb3SPavel Emelyanov 
19000187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
190188af182eSEric W. Biederman 			if (!rtnl_trylock()) {
190288af182eSEric W. Biederman 				/* Restore the original values before restarting */
190388af182eSEric W. Biederman 				*valp = val;
190488af182eSEric W. Biederman 				*ppos = pos;
19059b8adb5eSEric W. Biederman 				return restart_syscall();
190688af182eSEric W. Biederman 			}
19070187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1908c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
1909edc9e748SNicolas Dichtel 			} else {
19100187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
19110187bdfbSBen Hutchings 				struct in_device *idev =
19120187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
1913edc9e748SNicolas Dichtel 				if (*valp)
19140187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
1915edc9e748SNicolas Dichtel 				inet_netconf_notify_devconf(net,
1916edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
1917edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
1918edc9e748SNicolas Dichtel 							    cnf);
19190187bdfbSBen Hutchings 			}
19200187bdfbSBen Hutchings 			rtnl_unlock();
19214ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
1922edc9e748SNicolas Dichtel 		} else
1923edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1924edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
1925edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
19260187bdfbSBen Hutchings 	}
19271da177e4SLinus Torvalds 
19281da177e4SLinus Torvalds 	return ret;
19291da177e4SLinus Torvalds }
19301da177e4SLinus Torvalds 
1931323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write,
19328d65af78SAlexey Dobriyan 				void __user *buffer,
19331da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
19341da177e4SLinus Torvalds {
19351da177e4SLinus Torvalds 	int *valp = ctl->data;
19361da177e4SLinus Torvalds 	int val = *valp;
19378d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
193876e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
19391da177e4SLinus Torvalds 
19401da177e4SLinus Torvalds 	if (write && *valp != val)
19414ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
19421da177e4SLinus Torvalds 
19431da177e4SLinus Torvalds 	return ret;
19441da177e4SLinus Torvalds }
19451da177e4SLinus Torvalds 
1946f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
194742f811b8SHerbert Xu 	{ \
194842f811b8SHerbert Xu 		.procname	= name, \
194942f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
195002291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
195142f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
195242f811b8SHerbert Xu 		.mode		= mval, \
195342f811b8SHerbert Xu 		.proc_handler	= proc, \
195431be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
195542f811b8SHerbert Xu 	}
195642f811b8SHerbert Xu 
195742f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
1958f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
195942f811b8SHerbert Xu 
196042f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
1961f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
196242f811b8SHerbert Xu 
1963f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1964f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
196542f811b8SHerbert Xu 
196642f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
1967f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
196842f811b8SHerbert Xu 
19691da177e4SLinus Torvalds static struct devinet_sysctl_table {
19701da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
197102291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
19721da177e4SLinus Torvalds } devinet_sysctl = {
19731da177e4SLinus Torvalds 	.devinet_vars = {
197442f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
1975f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
197642f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
197742f811b8SHerbert Xu 
197842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
197942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
198042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
198142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
198242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
198342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
198442f811b8SHerbert Xu 					"accept_source_route"),
19858153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
198628f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
198742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
198842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
198942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
199042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
199142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
199242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
199342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
199442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
199542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
1996eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
199765324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
199842f811b8SHerbert Xu 
199942f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
200042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
200142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
200242f811b8SHerbert Xu 					      "force_igmp_version"),
200342f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
200442f811b8SHerbert Xu 					      "promote_secondaries"),
2005d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2006d0daebc3SThomas Graf 					      "route_localnet"),
20071da177e4SLinus Torvalds 	},
20081da177e4SLinus Torvalds };
20091da177e4SLinus Torvalds 
2010ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
2011f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
20121da177e4SLinus Torvalds {
20131da177e4SLinus Torvalds 	int i;
20149fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
20158607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2016bfada697SPavel Emelyanov 
20179fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
20181da177e4SLinus Torvalds 	if (!t)
20199fa89642SPavel Emelyanov 		goto out;
20209fa89642SPavel Emelyanov 
20211da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
20221da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
202331be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2024c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
20251da177e4SLinus Torvalds 	}
20261da177e4SLinus Torvalds 
20278607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
20281da177e4SLinus Torvalds 
20298607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
20301da177e4SLinus Torvalds 	if (!t->sysctl_header)
20318607ddb8SEric W. Biederman 		goto free;
20321da177e4SLinus Torvalds 
20331da177e4SLinus Torvalds 	p->sysctl = t;
2034ea40b324SPavel Emelyanov 	return 0;
20351da177e4SLinus Torvalds 
20361da177e4SLinus Torvalds free:
20371da177e4SLinus Torvalds 	kfree(t);
20389fa89642SPavel Emelyanov out:
2039ea40b324SPavel Emelyanov 	return -ENOBUFS;
20401da177e4SLinus Torvalds }
20411da177e4SLinus Torvalds 
204251602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
204366f27a52SPavel Emelyanov {
204451602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
204566f27a52SPavel Emelyanov 
204651602b2aSPavel Emelyanov 	if (t == NULL)
204751602b2aSPavel Emelyanov 		return;
204851602b2aSPavel Emelyanov 
204951602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
2050ff538818SLucian Adrian Grijincu 	unregister_net_sysctl_table(t->sysctl_header);
20511da177e4SLinus Torvalds 	kfree(t);
20521da177e4SLinus Torvalds }
205351602b2aSPavel Emelyanov 
205451602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
205551602b2aSPavel Emelyanov {
205654716e3bSEric W. Biederman 	neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
2057c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
2058f8572d8fSEric W. Biederman 					&idev->cnf);
205951602b2aSPavel Emelyanov }
206051602b2aSPavel Emelyanov 
206151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
206251602b2aSPavel Emelyanov {
206351602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
206451602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
20651da177e4SLinus Torvalds }
20661da177e4SLinus Torvalds 
206768dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
206868dd299bSPavel Emelyanov 	{
206968dd299bSPavel Emelyanov 		.procname	= "ip_forward",
207068dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
207102291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
207268dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
207368dd299bSPavel Emelyanov 		.mode		= 0644,
207468dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
207568dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2076c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
207768dd299bSPavel Emelyanov 	},
207868dd299bSPavel Emelyanov 	{ },
207968dd299bSPavel Emelyanov };
20802a75de0cSEric Dumazet #endif
208168dd299bSPavel Emelyanov 
2082752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2083752d14dcSPavel Emelyanov {
2084752d14dcSPavel Emelyanov 	int err;
2085752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
20862a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
20872a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2088752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
20892a75de0cSEric Dumazet #endif
2090752d14dcSPavel Emelyanov 
2091752d14dcSPavel Emelyanov 	err = -ENOMEM;
2092752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2093752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2094752d14dcSPavel Emelyanov 
209509ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2096752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
2097752d14dcSPavel Emelyanov 		if (all == NULL)
2098752d14dcSPavel Emelyanov 			goto err_alloc_all;
2099752d14dcSPavel Emelyanov 
2100752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
2101752d14dcSPavel Emelyanov 		if (dflt == NULL)
2102752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2103752d14dcSPavel Emelyanov 
21042a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2105752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
2106752d14dcSPavel Emelyanov 		if (tbl == NULL)
2107752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2108752d14dcSPavel Emelyanov 
210902291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2110752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2111752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
21122a75de0cSEric Dumazet #endif
2113752d14dcSPavel Emelyanov 	}
2114752d14dcSPavel Emelyanov 
2115752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2116f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
2117752d14dcSPavel Emelyanov 	if (err < 0)
2118752d14dcSPavel Emelyanov 		goto err_reg_all;
2119752d14dcSPavel Emelyanov 
2120f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
2121752d14dcSPavel Emelyanov 	if (err < 0)
2122752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2123752d14dcSPavel Emelyanov 
2124752d14dcSPavel Emelyanov 	err = -ENOMEM;
21258607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
2126752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
2127752d14dcSPavel Emelyanov 		goto err_reg_ctl;
21282a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2129752d14dcSPavel Emelyanov #endif
2130752d14dcSPavel Emelyanov 
2131752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2132752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2133752d14dcSPavel Emelyanov 	return 0;
2134752d14dcSPavel Emelyanov 
2135752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2136752d14dcSPavel Emelyanov err_reg_ctl:
2137752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
2138752d14dcSPavel Emelyanov err_reg_dflt:
2139752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
2140752d14dcSPavel Emelyanov err_reg_all:
2141752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2142752d14dcSPavel Emelyanov 		kfree(tbl);
2143752d14dcSPavel Emelyanov err_alloc_ctl:
21442a75de0cSEric Dumazet #endif
2145752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2146752d14dcSPavel Emelyanov 		kfree(dflt);
2147752d14dcSPavel Emelyanov err_alloc_dflt:
2148752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2149752d14dcSPavel Emelyanov 		kfree(all);
2150752d14dcSPavel Emelyanov err_alloc_all:
2151752d14dcSPavel Emelyanov 	return err;
2152752d14dcSPavel Emelyanov }
2153752d14dcSPavel Emelyanov 
2154752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2155752d14dcSPavel Emelyanov {
21562a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2157752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2158752d14dcSPavel Emelyanov 
2159752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2160752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2161752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
2162752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
2163752d14dcSPavel Emelyanov 	kfree(tbl);
21642a75de0cSEric Dumazet #endif
2165752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2166752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2167752d14dcSPavel Emelyanov }
2168752d14dcSPavel Emelyanov 
2169752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2170752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2171752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2172752d14dcSPavel Emelyanov };
2173752d14dcSPavel Emelyanov 
21749f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
21759f0f7272SThomas Graf 	.family		  = AF_INET,
21769f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
21779f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2178cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2179cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
21809f0f7272SThomas Graf };
21819f0f7272SThomas Graf 
21821da177e4SLinus Torvalds void __init devinet_init(void)
21831da177e4SLinus Torvalds {
2184fd23c3b3SDavid S. Miller 	int i;
2185fd23c3b3SDavid S. Miller 
2186fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2187fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2188fd23c3b3SDavid S. Miller 
2189752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2190752d14dcSPavel Emelyanov 
21911da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
21921da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
219363f3444fSThomas Graf 
21945c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, 0);
21955c766d64SJiri Pirko 
21969f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
21979f0f7272SThomas Graf 
2198c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
2199c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
2200c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
22019e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
22029e551110SNicolas Dichtel 		      NULL, NULL);
22031da177e4SLinus Torvalds }
22041da177e4SLinus Torvalds 
2205