xref: /openbmc/linux/net/ipv4/devinet.c (revision 5c6fe01c)
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);
218e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2191da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
22091df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2211da177e4SLinus Torvalds #endif
2221da177e4SLinus Torvalds 	dev_put(dev);
2231da177e4SLinus Torvalds 	if (!idev->dead)
2249f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2259f9354b9SEric Dumazet 	else
2261da177e4SLinus Torvalds 		kfree(idev);
2271da177e4SLinus Torvalds }
2289f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2291da177e4SLinus Torvalds 
23071e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds 	struct in_device *in_dev;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	ASSERT_RTNL();
2351da177e4SLinus Torvalds 
2360da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2371da177e4SLinus Torvalds 	if (!in_dev)
2381da177e4SLinus Torvalds 		goto out;
239c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2409355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2411da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2421da177e4SLinus Torvalds 	in_dev->dev = dev;
2439f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2449f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2451da177e4SLinus Torvalds 		goto out_kfree;
2460187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2470187bdfbSBen Hutchings 		dev_disable_lro(dev);
2481da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2491da177e4SLinus Torvalds 	dev_hold(dev);
25030c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2511da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2521da177e4SLinus Torvalds 
25366f27a52SPavel Emelyanov 	devinet_sysctl_register(in_dev);
2541da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2551da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2561da177e4SLinus Torvalds 		ip_mc_up(in_dev);
257483479ecSJarek Poplawski 
25830c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
259cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
260483479ecSJarek Poplawski out:
2611da177e4SLinus Torvalds 	return in_dev;
2621da177e4SLinus Torvalds out_kfree:
2631da177e4SLinus Torvalds 	kfree(in_dev);
2641da177e4SLinus Torvalds 	in_dev = NULL;
2651da177e4SLinus Torvalds 	goto out;
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2691da177e4SLinus Torvalds {
2701da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2711da177e4SLinus Torvalds 	in_dev_put(idev);
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2751da177e4SLinus Torvalds {
2761da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2771da177e4SLinus Torvalds 	struct net_device *dev;
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 	ASSERT_RTNL();
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	dev = in_dev->dev;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	in_dev->dead = 1;
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2881da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2891da177e4SLinus Torvalds 		inet_free_ifa(ifa);
2901da177e4SLinus Torvalds 	}
2911da177e4SLinus Torvalds 
292a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
2931da177e4SLinus Torvalds 
29451602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
2951da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
2961da177e4SLinus Torvalds 	arp_ifdown(dev);
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
2991da177e4SLinus Torvalds }
3001da177e4SLinus Torvalds 
301ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	rcu_read_lock();
3041da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3051da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3061da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3071da177e4SLinus Torvalds 				rcu_read_unlock();
3081da177e4SLinus Torvalds 				return 1;
3091da177e4SLinus Torvalds 			}
3101da177e4SLinus Torvalds 		}
3111da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3121da177e4SLinus Torvalds 	rcu_read_unlock();
3131da177e4SLinus Torvalds 	return 0;
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
316d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
31715e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3181da177e4SLinus Torvalds {
3198f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3200ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3210ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3220ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3230ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	ASSERT_RTNL();
3261da177e4SLinus Torvalds 
3278f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3288f937c60SHarald Welte 	 * unless alias promotion is set
3298f937c60SHarald Welte 	 **/
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3321da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3350ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3360ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3370ff60a45SJamal Hadi Salim 				last_prim = ifa;
3380ff60a45SJamal Hadi Salim 
3391da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3401da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3411da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3421da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3430ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3441da177e4SLinus Torvalds 				continue;
3451da177e4SLinus Torvalds 			}
3461da177e4SLinus Torvalds 
3470ff60a45SJamal Hadi Salim 			if (!do_promote) {
348fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3491da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3501da177e4SLinus Torvalds 
35115e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
352e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
353e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3541da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3558f937c60SHarald Welte 			} else {
3568f937c60SHarald Welte 				promote = ifa;
3578f937c60SHarald Welte 				break;
3588f937c60SHarald Welte 			}
3591da177e4SLinus Torvalds 		}
3601da177e4SLinus Torvalds 	}
3611da177e4SLinus Torvalds 
3622d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3632d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3642d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3652d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3662d230e2bSJulian Anastasov 	 */
3672d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3682d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3692d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3702d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3712d230e2bSJulian Anastasov 	}
3722d230e2bSJulian Anastasov 
3731da177e4SLinus Torvalds 	/* 2. Unlink it */
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
376fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3811da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3821da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3831da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3841da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3851da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3861da177e4SLinus Torvalds 	   So that, this order is correct.
3871da177e4SLinus Torvalds 	 */
38815e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
389e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
3900ff60a45SJamal Hadi Salim 
3910ff60a45SJamal Hadi Salim 	if (promote) {
39204024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
3930ff60a45SJamal Hadi Salim 
3940ff60a45SJamal Hadi Salim 		if (prev_prom) {
3950ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
3960ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
3970ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
3980ff60a45SJamal Hadi Salim 		}
3990ff60a45SJamal Hadi Salim 
4000ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
40115e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
402e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
403e041c683SAlan Stern 				NETDEV_UP, promote);
40404024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4050ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4060ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4070ff60a45SJamal Hadi Salim 					continue;
4080ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4090ff60a45SJamal Hadi Salim 		}
4100ff60a45SJamal Hadi Salim 
4110ff60a45SJamal Hadi Salim 	}
4126363097cSHerbert Xu 	if (destroy)
4131da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
416d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
417d6062cbbSThomas Graf 			 int destroy)
418d6062cbbSThomas Graf {
419d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
420d6062cbbSThomas Graf }
421d6062cbbSThomas Graf 
4225c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4235c766d64SJiri Pirko 
4245c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4255c766d64SJiri Pirko 
426d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
42715e47304SEric W. Biederman 			     u32 portid)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4301da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	ASSERT_RTNL();
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4351da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4361da177e4SLinus Torvalds 		return 0;
4371da177e4SLinus Torvalds 	}
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4401da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4431da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4441da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4451da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4461da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4471da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4481da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4491da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4501da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4511da177e4SLinus Torvalds 				return -EEXIST;
4521da177e4SLinus Torvalds 			}
4531da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4541da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4551da177e4SLinus Torvalds 				return -EINVAL;
4561da177e4SLinus Torvalds 			}
4571da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4581da177e4SLinus Torvalds 		}
4591da177e4SLinus Torvalds 	}
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
4621da177e4SLinus Torvalds 		net_srandom(ifa->ifa_local);
4631da177e4SLinus Torvalds 		ifap = last_primary;
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4671da177e4SLinus Torvalds 	*ifap = ifa;
4681da177e4SLinus Torvalds 
469fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
470fd23c3b3SDavid S. Miller 
4715c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
4725c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, 0);
4735c766d64SJiri Pirko 
4741da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4751da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4761da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
47715e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
478e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds 	return 0;
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds 
483d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
484d6062cbbSThomas Graf {
485d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
486d6062cbbSThomas Graf }
487d6062cbbSThomas Graf 
4881da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4891da177e4SLinus Torvalds {
490e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	ASSERT_RTNL();
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds 	if (!in_dev) {
4951da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4961da177e4SLinus Torvalds 		return -ENOBUFS;
4971da177e4SLinus Torvalds 	}
49871e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
4991da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
500547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5011da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5021da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5031da177e4SLinus Torvalds 	}
504f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5051da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5061da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5071da177e4SLinus Torvalds }
5081da177e4SLinus Torvalds 
5098723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5108723e1b4SEric Dumazet  * We dont take a reference on found in_device
5118723e1b4SEric Dumazet  */
5127fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5131da177e4SLinus Torvalds {
5141da177e4SLinus Torvalds 	struct net_device *dev;
5151da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
516c148fc2eSEric Dumazet 
517c148fc2eSEric Dumazet 	rcu_read_lock();
518c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5191da177e4SLinus Torvalds 	if (dev)
5208723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
521c148fc2eSEric Dumazet 	rcu_read_unlock();
5221da177e4SLinus Torvalds 	return in_dev;
5231da177e4SLinus Torvalds }
5249f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5271da177e4SLinus Torvalds 
52860cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
52960cad5daSAl Viro 				    __be32 mask)
5301da177e4SLinus Torvalds {
5311da177e4SLinus Torvalds 	ASSERT_RTNL();
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5341da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5351da177e4SLinus Torvalds 			return ifa;
5361da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5371da177e4SLinus Torvalds 	return NULL;
5381da177e4SLinus Torvalds }
5391da177e4SLinus Torvalds 
540661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
5411da177e4SLinus Torvalds {
5423b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
543dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5441da177e4SLinus Torvalds 	struct in_device *in_dev;
545dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5461da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
547dfdd5fd4SThomas Graf 	int err = -EINVAL;
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds 	ASSERT_RTNL();
5501da177e4SLinus Torvalds 
551dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
552dfdd5fd4SThomas Graf 	if (err < 0)
553dfdd5fd4SThomas Graf 		goto errout;
554dfdd5fd4SThomas Graf 
555dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5567fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
557dfdd5fd4SThomas Graf 	if (in_dev == NULL) {
558dfdd5fd4SThomas Graf 		err = -ENODEV;
559dfdd5fd4SThomas Graf 		goto errout;
560dfdd5fd4SThomas Graf 	}
561dfdd5fd4SThomas Graf 
5621da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5631da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
564dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
565a7a628c4SAl Viro 		    ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
5661da177e4SLinus Torvalds 			continue;
567dfdd5fd4SThomas Graf 
568dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
569dfdd5fd4SThomas Graf 			continue;
570dfdd5fd4SThomas Graf 
571dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
572dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
573a7a628c4SAl Viro 		    !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
574dfdd5fd4SThomas Graf 			continue;
575dfdd5fd4SThomas Graf 
57615e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
5771da177e4SLinus Torvalds 		return 0;
5781da177e4SLinus Torvalds 	}
579dfdd5fd4SThomas Graf 
580dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
581dfdd5fd4SThomas Graf errout:
582dfdd5fd4SThomas Graf 	return err;
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds 
5855c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
5865c766d64SJiri Pirko 
5875c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
5885c766d64SJiri Pirko {
5895c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
5905c766d64SJiri Pirko 	struct in_ifaddr *ifa;
591c988d1e8SJiri Pirko 	struct hlist_node *n;
5925c766d64SJiri Pirko 	int i;
5935c766d64SJiri Pirko 
5945c766d64SJiri Pirko 	now = jiffies;
5955c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
5965c766d64SJiri Pirko 
5975c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
598c988d1e8SJiri Pirko 		bool change_needed = false;
599c988d1e8SJiri Pirko 
600c988d1e8SJiri Pirko 		rcu_read_lock();
601b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6025c766d64SJiri Pirko 			unsigned long age;
6035c766d64SJiri Pirko 
6045c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6055c766d64SJiri Pirko 				continue;
6065c766d64SJiri Pirko 
6075c766d64SJiri Pirko 			/* We try to batch several events at once. */
6085c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6095c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6105c766d64SJiri Pirko 
6115c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6125c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
613c988d1e8SJiri Pirko 				change_needed = true;
614c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
615c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
616c988d1e8SJiri Pirko 				continue;
617c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
618c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
619c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
620c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
621c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
622c988d1e8SJiri Pirko 
623c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
624c988d1e8SJiri Pirko 					change_needed = true;
625c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
626c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
627c988d1e8SJiri Pirko 					       next)) {
628c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
629c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
630c988d1e8SJiri Pirko 			}
631c988d1e8SJiri Pirko 		}
632c988d1e8SJiri Pirko 		rcu_read_unlock();
633c988d1e8SJiri Pirko 		if (!change_needed)
634c988d1e8SJiri Pirko 			continue;
635c988d1e8SJiri Pirko 		rtnl_lock();
636c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
637c988d1e8SJiri Pirko 			unsigned long age;
638c988d1e8SJiri Pirko 
639c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
640c988d1e8SJiri Pirko 				continue;
641c988d1e8SJiri Pirko 
642c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
643c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
644c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
645c988d1e8SJiri Pirko 
646c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
647c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
6485c766d64SJiri Pirko 				struct in_ifaddr **ifap;
6495c766d64SJiri Pirko 
6505c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
651c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
652c988d1e8SJiri Pirko 					if (*ifap == ifa) {
6535c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
6545c766d64SJiri Pirko 							     ifap, 1);
655c988d1e8SJiri Pirko 						break;
6565c766d64SJiri Pirko 					}
657c988d1e8SJiri Pirko 				}
658c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
659c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
660c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
661c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
6625c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
6635c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
6645c766d64SJiri Pirko 			}
6655c766d64SJiri Pirko 		}
666c988d1e8SJiri Pirko 		rtnl_unlock();
6675c766d64SJiri Pirko 	}
6685c766d64SJiri Pirko 
6695c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
6705c766d64SJiri Pirko 	next_sched = next;
6715c766d64SJiri Pirko 
6725c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
6735c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
6745c766d64SJiri Pirko 		next_sched = next_sec;
6755c766d64SJiri Pirko 
6765c766d64SJiri Pirko 	now = jiffies;
6775c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
6785c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
6795c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
6805c766d64SJiri Pirko 
6815c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, next_sched - now);
6825c766d64SJiri Pirko }
6835c766d64SJiri Pirko 
6845c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
6855c766d64SJiri Pirko 			     __u32 prefered_lft)
6865c766d64SJiri Pirko {
6875c766d64SJiri Pirko 	unsigned long timeout;
6885c766d64SJiri Pirko 
6895c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
6905c766d64SJiri Pirko 
6915c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
6925c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
6935c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
6945c766d64SJiri Pirko 	else
6955c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
6965c766d64SJiri Pirko 
6975c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
6985c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
6995c766d64SJiri Pirko 		if (timeout == 0)
7005c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7015c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7025c766d64SJiri Pirko 	}
7035c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7045c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7055c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7065c766d64SJiri Pirko }
7075c766d64SJiri Pirko 
7085c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
7095c766d64SJiri Pirko 				       __u32 *pvalid_lft, __u32 *pprefered_lft)
7101da177e4SLinus Torvalds {
7115c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7125c753978SThomas Graf 	struct in_ifaddr *ifa;
7135c753978SThomas Graf 	struct ifaddrmsg *ifm;
7141da177e4SLinus Torvalds 	struct net_device *dev;
7151da177e4SLinus Torvalds 	struct in_device *in_dev;
7167b218574SDenis V. Lunev 	int err;
7171da177e4SLinus Torvalds 
7185c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
7195c753978SThomas Graf 	if (err < 0)
7205c753978SThomas Graf 		goto errout;
7211da177e4SLinus Torvalds 
7225c753978SThomas Graf 	ifm = nlmsg_data(nlh);
723c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
7247b218574SDenis V. Lunev 	if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
7255c753978SThomas Graf 		goto errout;
7261da177e4SLinus Torvalds 
7274b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
7285c753978SThomas Graf 	err = -ENODEV;
7297b218574SDenis V. Lunev 	if (dev == NULL)
7305c753978SThomas Graf 		goto errout;
7311da177e4SLinus Torvalds 
7325c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
7335c753978SThomas Graf 	err = -ENOBUFS;
7347b218574SDenis V. Lunev 	if (in_dev == NULL)
7355c753978SThomas Graf 		goto errout;
73671e27da9SHerbert Xu 
7375c753978SThomas Graf 	ifa = inet_alloc_ifa();
7387b218574SDenis V. Lunev 	if (ifa == NULL)
7395c753978SThomas Graf 		/*
7405c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
7415c753978SThomas Graf 		 * assigned to its device and is destroy with it.
7425c753978SThomas Graf 		 */
7435c753978SThomas Graf 		goto errout;
7445c753978SThomas Graf 
745a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
7465c753978SThomas Graf 	in_dev_hold(in_dev);
7475c753978SThomas Graf 
7485c753978SThomas Graf 	if (tb[IFA_ADDRESS] == NULL)
7495c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
7505c753978SThomas Graf 
751fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
7521da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
7531da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
7541da177e4SLinus Torvalds 	ifa->ifa_flags = ifm->ifa_flags;
7551da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
7561da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
7575c753978SThomas Graf 
758a7a628c4SAl Viro 	ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
759a7a628c4SAl Viro 	ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
7605c753978SThomas Graf 
7615c753978SThomas Graf 	if (tb[IFA_BROADCAST])
762a7a628c4SAl Viro 		ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
7635c753978SThomas Graf 
7645c753978SThomas Graf 	if (tb[IFA_LABEL])
7655c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
7661da177e4SLinus Torvalds 	else
7671da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
7681da177e4SLinus Torvalds 
7695c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
7705c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
7715c766d64SJiri Pirko 
7725c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
7735c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
7745c766d64SJiri Pirko 			err = -EINVAL;
775446266b0SDaniel Borkmann 			goto errout_free;
7765c766d64SJiri Pirko 		}
7775c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
7785c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
7795c766d64SJiri Pirko 	}
7805c766d64SJiri Pirko 
7815c753978SThomas Graf 	return ifa;
7825c753978SThomas Graf 
783446266b0SDaniel Borkmann errout_free:
784446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
7855c753978SThomas Graf errout:
7865c753978SThomas Graf 	return ERR_PTR(err);
7875c753978SThomas Graf }
7885c753978SThomas Graf 
7895c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
7905c766d64SJiri Pirko {
7915c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
7925c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
7935c766d64SJiri Pirko 
7945c766d64SJiri Pirko 	if (!ifa->ifa_local)
7955c766d64SJiri Pirko 		return NULL;
7965c766d64SJiri Pirko 
7975c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
7985c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
7995c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8005c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8015c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8025c766d64SJiri Pirko 			return ifa1;
8035c766d64SJiri Pirko 	}
8045c766d64SJiri Pirko 	return NULL;
8055c766d64SJiri Pirko }
8065c766d64SJiri Pirko 
807661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
8085c753978SThomas Graf {
8093b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8105c753978SThomas Graf 	struct in_ifaddr *ifa;
8115c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8125c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8135c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8145c753978SThomas Graf 
8155c753978SThomas Graf 	ASSERT_RTNL();
8165c753978SThomas Graf 
8175c766d64SJiri Pirko 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
8185c753978SThomas Graf 	if (IS_ERR(ifa))
8195c753978SThomas Graf 		return PTR_ERR(ifa);
8205c753978SThomas Graf 
8215c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
8225c766d64SJiri Pirko 	if (!ifa_existing) {
8235c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
8245c766d64SJiri Pirko 		 * userspace alreay relies on not having to provide this.
8255c766d64SJiri Pirko 		 */
8265c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
82715e47304SEric W. Biederman 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
8285c766d64SJiri Pirko 	} else {
8295c766d64SJiri Pirko 		inet_free_ifa(ifa);
8305c766d64SJiri Pirko 
8315c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
8325c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
8335c766d64SJiri Pirko 			return -EEXIST;
83434e2ed34SJiri Pirko 		ifa = ifa_existing;
83534e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
83605a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
83705a324b9SJiri Pirko 		schedule_delayed_work(&check_lifetime_work, 0);
83834e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
83934e2ed34SJiri Pirko 		blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
8405c766d64SJiri Pirko 	}
8415c766d64SJiri Pirko 	return 0;
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds /*
8451da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
8461da177e4SLinus Torvalds  */
8471da177e4SLinus Torvalds 
84840384999SEric Dumazet static int inet_abc_len(__be32 addr)
8491da177e4SLinus Torvalds {
8501da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
8511da177e4SLinus Torvalds 
852f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
8531da177e4SLinus Torvalds 		rc = 0;
8541da177e4SLinus Torvalds 	else {
855714e85beSAl Viro 		__u32 haddr = ntohl(addr);
8561da177e4SLinus Torvalds 
857714e85beSAl Viro 		if (IN_CLASSA(haddr))
8581da177e4SLinus Torvalds 			rc = 8;
859714e85beSAl Viro 		else if (IN_CLASSB(haddr))
8601da177e4SLinus Torvalds 			rc = 16;
861714e85beSAl Viro 		else if (IN_CLASSC(haddr))
8621da177e4SLinus Torvalds 			rc = 24;
8631da177e4SLinus Torvalds 	}
8641da177e4SLinus Torvalds 
8651da177e4SLinus Torvalds 	return rc;
8661da177e4SLinus Torvalds }
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 
869e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
8701da177e4SLinus Torvalds {
8711da177e4SLinus Torvalds 	struct ifreq ifr;
8721da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
8731da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
8741da177e4SLinus Torvalds 	struct in_device *in_dev;
8751da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
8761da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
8771da177e4SLinus Torvalds 	struct net_device *dev;
8781da177e4SLinus Torvalds 	char *colon;
8791da177e4SLinus Torvalds 	int ret = -EFAULT;
8801da177e4SLinus Torvalds 	int tryaddrmatch = 0;
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	/*
8831da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
8841da177e4SLinus Torvalds 	 */
8851da177e4SLinus Torvalds 
8861da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
8871da177e4SLinus Torvalds 		goto out;
8881da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds 	/* save original address for comparison */
8911da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
8921da177e4SLinus Torvalds 
8931da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
8941da177e4SLinus Torvalds 	if (colon)
8951da177e4SLinus Torvalds 		*colon = 0;
8961da177e4SLinus Torvalds 
897e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds 	switch (cmd) {
9001da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9011da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9021da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9031da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9041da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
9051da177e4SLinus Torvalds 		   so that we do not impose a lock.
9061da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
9071da177e4SLinus Torvalds 		 */
9081da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
9091da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
9101da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
9111da177e4SLinus Torvalds 		break;
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
914bf5b30b8SZhao Hongjiang 		ret = -EPERM;
91552e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9161da177e4SLinus Torvalds 			goto out;
9171da177e4SLinus Torvalds 		break;
9181da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
9191da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
9201da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
9211da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
922bf5b30b8SZhao Hongjiang 		ret = -EPERM;
92352e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9241da177e4SLinus Torvalds 			goto out;
9251da177e4SLinus Torvalds 		ret = -EINVAL;
9261da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
9271da177e4SLinus Torvalds 			goto out;
9281da177e4SLinus Torvalds 		break;
9291da177e4SLinus Torvalds 	default:
9301da177e4SLinus Torvalds 		ret = -EINVAL;
9311da177e4SLinus Torvalds 		goto out;
9321da177e4SLinus Torvalds 	}
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds 	rtnl_lock();
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	ret = -ENODEV;
9379f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
9389f9354b9SEric Dumazet 	if (!dev)
9391da177e4SLinus Torvalds 		goto done;
9401da177e4SLinus Torvalds 
9411da177e4SLinus Torvalds 	if (colon)
9421da177e4SLinus Torvalds 		*colon = ':';
9431da177e4SLinus Torvalds 
9449f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
9459f9354b9SEric Dumazet 	if (in_dev) {
9461da177e4SLinus Torvalds 		if (tryaddrmatch) {
9471da177e4SLinus Torvalds 			/* Matthias Andree */
9481da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
9491da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
9501da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
9511da177e4SLinus Torvalds 			   This is checked above. */
9521da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
9531da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
9541da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
9551da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
9566c91afe1SDavid S. Miller 							ifa->ifa_local) {
9571da177e4SLinus Torvalds 					break; /* found */
9581da177e4SLinus Torvalds 				}
9591da177e4SLinus Torvalds 			}
9601da177e4SLinus Torvalds 		}
9611da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
9621da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
9631da177e4SLinus Torvalds 		   comparing just the label */
9641da177e4SLinus Torvalds 		if (!ifa) {
9651da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
9661da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
9671da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
9681da177e4SLinus Torvalds 					break;
9691da177e4SLinus Torvalds 		}
9701da177e4SLinus Torvalds 	}
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
9731da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
9741da177e4SLinus Torvalds 		goto done;
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds 	switch (cmd) {
9771da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9781da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
9791da177e4SLinus Torvalds 		goto rarok;
9801da177e4SLinus Torvalds 
9811da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9821da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
9831da177e4SLinus Torvalds 		goto rarok;
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9861da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
9871da177e4SLinus Torvalds 		goto rarok;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9901da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
9911da177e4SLinus Torvalds 		goto rarok;
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
9941da177e4SLinus Torvalds 		if (colon) {
9951da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
9961da177e4SLinus Torvalds 			if (!ifa)
9971da177e4SLinus Torvalds 				break;
9981da177e4SLinus Torvalds 			ret = 0;
9991da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
10001da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
10011da177e4SLinus Torvalds 			break;
10021da177e4SLinus Torvalds 		}
10031da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
10041da177e4SLinus Torvalds 		break;
10051da177e4SLinus Torvalds 
10061da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10071da177e4SLinus Torvalds 		ret = -EINVAL;
10081da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10091da177e4SLinus Torvalds 			break;
10101da177e4SLinus Torvalds 
10111da177e4SLinus Torvalds 		if (!ifa) {
10121da177e4SLinus Torvalds 			ret = -ENOBUFS;
10139f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
10149f9354b9SEric Dumazet 			if (!ifa)
10151da177e4SLinus Torvalds 				break;
1016c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
10171da177e4SLinus Torvalds 			if (colon)
10181da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
10191da177e4SLinus Torvalds 			else
10201da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
10211da177e4SLinus Torvalds 		} else {
10221da177e4SLinus Torvalds 			ret = 0;
10231da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
10241da177e4SLinus Torvalds 				break;
10251da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10261da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1027148f9729SBjorn Mork 			ifa->ifa_scope = 0;
10281da177e4SLinus Torvalds 		}
10291da177e4SLinus Torvalds 
10301da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
10311da177e4SLinus Torvalds 
10321da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
10331da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
10341da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
10351da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10361da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
10371da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
10381da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
10391da177e4SLinus Torvalds 		} else {
10401da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
10411da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
10421da177e4SLinus Torvalds 		}
10435c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
10441da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
10451da177e4SLinus Torvalds 		break;
10461da177e4SLinus Torvalds 
10471da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10481da177e4SLinus Torvalds 		ret = 0;
10491da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
10501da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10511da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
10521da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
10531da177e4SLinus Torvalds 		}
10541da177e4SLinus Torvalds 		break;
10551da177e4SLinus Torvalds 
10561da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
10571da177e4SLinus Torvalds 		ret = 0;
10581da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
10591da177e4SLinus Torvalds 			break;
10601da177e4SLinus Torvalds 		ret = -EINVAL;
10611da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10621da177e4SLinus Torvalds 			break;
10631da177e4SLinus Torvalds 		ret = 0;
10641da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
10651da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
10661da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
10671da177e4SLinus Torvalds 		break;
10681da177e4SLinus Torvalds 
10691da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
10701da177e4SLinus Torvalds 
10711da177e4SLinus Torvalds 		/*
10721da177e4SLinus Torvalds 		 *	The mask we set must be legal.
10731da177e4SLinus Torvalds 		 */
10741da177e4SLinus Torvalds 		ret = -EINVAL;
10751da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
10761da177e4SLinus Torvalds 			break;
10771da177e4SLinus Torvalds 		ret = 0;
10781da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1079a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
10801da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10811da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
10821da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds 			/* See if current broadcast address matches
10851da177e4SLinus Torvalds 			 * with current netmask, then recalculate
10861da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
10871da177e4SLinus Torvalds 			 * funny address, so don't touch it since
10881da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
10891da177e4SLinus Torvalds 			 */
10901da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10911da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
10921da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1093dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
10941da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
10951da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
10961da177e4SLinus Torvalds 			}
10971da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
10981da177e4SLinus Torvalds 		}
10991da177e4SLinus Torvalds 		break;
11001da177e4SLinus Torvalds 	}
11011da177e4SLinus Torvalds done:
11021da177e4SLinus Torvalds 	rtnl_unlock();
11031da177e4SLinus Torvalds out:
11041da177e4SLinus Torvalds 	return ret;
11051da177e4SLinus Torvalds rarok:
11061da177e4SLinus Torvalds 	rtnl_unlock();
11071da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
11081da177e4SLinus Torvalds 	goto out;
11091da177e4SLinus Torvalds }
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
11121da177e4SLinus Torvalds {
1113e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11141da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
11151da177e4SLinus Torvalds 	struct ifreq ifr;
11161da177e4SLinus Torvalds 	int done = 0;
11171da177e4SLinus Torvalds 
11189f9354b9SEric Dumazet 	if (!in_dev)
11191da177e4SLinus Torvalds 		goto out;
11201da177e4SLinus Torvalds 
11219f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
11221da177e4SLinus Torvalds 		if (!buf) {
11231da177e4SLinus Torvalds 			done += sizeof(ifr);
11241da177e4SLinus Torvalds 			continue;
11251da177e4SLinus Torvalds 		}
11261da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
11271da177e4SLinus Torvalds 			break;
11281da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
11291da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
11321da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
11331da177e4SLinus Torvalds 								ifa->ifa_local;
11341da177e4SLinus Torvalds 
11351da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
11361da177e4SLinus Torvalds 			done = -EFAULT;
11371da177e4SLinus Torvalds 			break;
11381da177e4SLinus Torvalds 		}
11391da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
11401da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
11411da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
11421da177e4SLinus Torvalds 	}
11431da177e4SLinus Torvalds out:
11441da177e4SLinus Torvalds 	return done;
11451da177e4SLinus Torvalds }
11461da177e4SLinus Torvalds 
1147a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
11481da177e4SLinus Torvalds {
1149a61ced5dSAl Viro 	__be32 addr = 0;
11501da177e4SLinus Torvalds 	struct in_device *in_dev;
1151c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
11521da177e4SLinus Torvalds 
11531da177e4SLinus Torvalds 	rcu_read_lock();
1154e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
11551da177e4SLinus Torvalds 	if (!in_dev)
11561da177e4SLinus Torvalds 		goto no_in_dev;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
11591da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
11601da177e4SLinus Torvalds 			continue;
11611da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
11621da177e4SLinus Torvalds 			addr = ifa->ifa_local;
11631da177e4SLinus Torvalds 			break;
11641da177e4SLinus Torvalds 		}
11651da177e4SLinus Torvalds 		if (!addr)
11661da177e4SLinus Torvalds 			addr = ifa->ifa_local;
11671da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 	if (addr)
1170c6d14c84SEric Dumazet 		goto out_unlock;
11719f9354b9SEric Dumazet no_in_dev:
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
11741da177e4SLinus Torvalds 	   in this case. It is importnat that lo is the first interface
11751da177e4SLinus Torvalds 	   in dev_base list.
11761da177e4SLinus Torvalds 	 */
1177c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
11789f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
11799f9354b9SEric Dumazet 		if (!in_dev)
11801da177e4SLinus Torvalds 			continue;
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
11831da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
11841da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
11851da177e4SLinus Torvalds 				addr = ifa->ifa_local;
1186c6d14c84SEric Dumazet 				goto out_unlock;
11871da177e4SLinus Torvalds 			}
11881da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
11891da177e4SLinus Torvalds 	}
1190c6d14c84SEric Dumazet out_unlock:
11911da177e4SLinus Torvalds 	rcu_read_unlock();
11921da177e4SLinus Torvalds 	return addr;
11931da177e4SLinus Torvalds }
11949f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
11951da177e4SLinus Torvalds 
119660cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
119760cad5daSAl Viro 			      __be32 local, int scope)
11981da177e4SLinus Torvalds {
11991da177e4SLinus Torvalds 	int same = 0;
1200a144ea4bSAl Viro 	__be32 addr = 0;
12011da177e4SLinus Torvalds 
12021da177e4SLinus Torvalds 	for_ifa(in_dev) {
12031da177e4SLinus Torvalds 		if (!addr &&
12041da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
12051da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
12061da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12071da177e4SLinus Torvalds 			if (same)
12081da177e4SLinus Torvalds 				break;
12091da177e4SLinus Torvalds 		}
12101da177e4SLinus Torvalds 		if (!same) {
12111da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
12121da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
12131da177e4SLinus Torvalds 			if (same && addr) {
12141da177e4SLinus Torvalds 				if (local || !dst)
12151da177e4SLinus Torvalds 					break;
12161da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
12171da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
12181da177e4SLinus Torvalds 					break;
12191da177e4SLinus Torvalds 				/* No, then can we use new local src? */
12201da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
12211da177e4SLinus Torvalds 					addr = ifa->ifa_local;
12221da177e4SLinus Torvalds 					break;
12231da177e4SLinus Torvalds 				}
12241da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
12251da177e4SLinus Torvalds 				same = 0;
12261da177e4SLinus Torvalds 			}
12271da177e4SLinus Torvalds 		}
12281da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds 	return same ? addr : 0;
12311da177e4SLinus Torvalds }
12321da177e4SLinus Torvalds 
12331da177e4SLinus Torvalds /*
12341da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
12359bd85e32SDenis V. Lunev  * - in_dev: only on this interface, 0=any interface
12361da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
12371da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
12381da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
12391da177e4SLinus Torvalds  */
12409bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev,
12419bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
12421da177e4SLinus Torvalds {
124360cad5daSAl Viro 	__be32 addr = 0;
12449bd85e32SDenis V. Lunev 	struct net_device *dev;
124539a6d063SDenis V. Lunev 	struct net *net;
12461da177e4SLinus Torvalds 
124739a6d063SDenis V. Lunev 	if (scope != RT_SCOPE_LINK)
12489bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
12491da177e4SLinus Torvalds 
1250c346dca1SYOSHIFUJI Hideaki 	net = dev_net(in_dev->dev);
12511da177e4SLinus Torvalds 	rcu_read_lock();
1252c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
12539f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
12549f9354b9SEric Dumazet 		if (in_dev) {
12551da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
12561da177e4SLinus Torvalds 			if (addr)
12571da177e4SLinus Torvalds 				break;
12581da177e4SLinus Torvalds 		}
12591da177e4SLinus Torvalds 	}
12601da177e4SLinus Torvalds 	rcu_read_unlock();
12611da177e4SLinus Torvalds 
12621da177e4SLinus Torvalds 	return addr;
12631da177e4SLinus Torvalds }
1264eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
12651da177e4SLinus Torvalds 
12661da177e4SLinus Torvalds /*
12671da177e4SLinus Torvalds  *	Device notifier
12681da177e4SLinus Torvalds  */
12691da177e4SLinus Torvalds 
12701da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
12711da177e4SLinus Torvalds {
1272e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
12731da177e4SLinus Torvalds }
12749f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
12771da177e4SLinus Torvalds {
1278e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
12791da177e4SLinus Torvalds }
12809f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
12811da177e4SLinus Torvalds 
12829f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
12839f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
12841da177e4SLinus Torvalds */
12851da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
12861da177e4SLinus Torvalds {
12871da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
12881da177e4SLinus Torvalds 	int named = 0;
12891da177e4SLinus Torvalds 
12901da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
12911da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
12941da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
12951da177e4SLinus Torvalds 		if (named++ == 0)
1296573bf470SThomas Graf 			goto skip;
129744344b2aSMark McLoughlin 		dot = strchr(old, ':');
12981da177e4SLinus Torvalds 		if (dot == NULL) {
12991da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
13001da177e4SLinus Torvalds 			dot = old;
13011da177e4SLinus Torvalds 		}
13029f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
13031da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
13049f9354b9SEric Dumazet 		else
13051da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1306573bf470SThomas Graf skip:
1307573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
13081da177e4SLinus Torvalds 	}
13091da177e4SLinus Torvalds }
13101da177e4SLinus Torvalds 
131140384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
131206770843SBreno Leitao {
131306770843SBreno Leitao 	return mtu >= 68;
131406770843SBreno Leitao }
131506770843SBreno Leitao 
1316d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1317d11327adSIan Campbell 					struct in_device *in_dev)
1318d11327adSIan Campbell 
1319d11327adSIan Campbell {
1320b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1321d11327adSIan Campbell 
1322b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1323b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1324d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
13256c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
13266c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1327d11327adSIan Campbell 			 dev->dev_addr, NULL);
1328d11327adSIan Campbell 	}
1329b76d0789SZoltan Kiss }
1330d11327adSIan Campbell 
13311da177e4SLinus Torvalds /* Called only under RTNL semaphore */
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
13341da177e4SLinus Torvalds 			 void *ptr)
13351da177e4SLinus Torvalds {
1336351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1337748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds 	ASSERT_RTNL();
13401da177e4SLinus Torvalds 
13411da177e4SLinus Torvalds 	if (!in_dev) {
13428030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
13431da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
13448d76527eSHerbert Xu 			if (!in_dev)
1345b217d616SHerbert Xu 				return notifier_from_errno(-ENOMEM);
13460cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
134742f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
134842f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
13491da177e4SLinus Torvalds 			}
135006770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
135106770843SBreno Leitao 			/* Re-enabling IP */
135206770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
135306770843SBreno Leitao 				in_dev = inetdev_init(dev);
13548030f544SHerbert Xu 		}
13551da177e4SLinus Torvalds 		goto out;
13561da177e4SLinus Torvalds 	}
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 	switch (event) {
13591da177e4SLinus Torvalds 	case NETDEV_REGISTER:
136091df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1361a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
13621da177e4SLinus Torvalds 		break;
13631da177e4SLinus Torvalds 	case NETDEV_UP:
136406770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
13651da177e4SLinus Torvalds 			break;
13660cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
13679f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
13689f9354b9SEric Dumazet 
13699f9354b9SEric Dumazet 			if (ifa) {
1370fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
13711da177e4SLinus Torvalds 				ifa->ifa_local =
13721da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
13731da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
13741da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
13751da177e4SLinus Torvalds 				in_dev_hold(in_dev);
13761da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
13771da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
13781da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
13795c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
13805c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
13811da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
13821da177e4SLinus Torvalds 			}
13831da177e4SLinus Torvalds 		}
13841da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1385eefef1cfSStephen Hemminger 		/* fall through */
1386eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1387d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1388d11327adSIan Campbell 			break;
1389d11327adSIan Campbell 		/* fall through */
1390d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1391a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1392d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
13931da177e4SLinus Torvalds 		break;
13941da177e4SLinus Torvalds 	case NETDEV_DOWN:
13951da177e4SLinus Torvalds 		ip_mc_down(in_dev);
13961da177e4SLinus Torvalds 		break;
139793d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
139875c78500SMoni Shoua 		ip_mc_unmap(in_dev);
139975c78500SMoni Shoua 		break;
140093d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
140175c78500SMoni Shoua 		ip_mc_remap(in_dev);
140275c78500SMoni Shoua 		break;
14031da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
140406770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
14051da177e4SLinus Torvalds 			break;
140606770843SBreno Leitao 		/* disable IP when MTU is not enough */
14071da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
14081da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
14091da177e4SLinus Torvalds 		break;
14101da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
14111da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
14121da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
14131da177e4SLinus Torvalds 		 */
14141da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
14151da177e4SLinus Torvalds 
141651602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
141766f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
14181da177e4SLinus Torvalds 		break;
14191da177e4SLinus Torvalds 	}
14201da177e4SLinus Torvalds out:
14211da177e4SLinus Torvalds 	return NOTIFY_DONE;
14221da177e4SLinus Torvalds }
14231da177e4SLinus Torvalds 
14241da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
14251da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
14261da177e4SLinus Torvalds };
14271da177e4SLinus Torvalds 
142840384999SEric Dumazet static size_t inet_nlmsg_size(void)
1429339bf98fSThomas Graf {
1430339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1431339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1432339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1433339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1434339bf98fSThomas Graf 	       + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1435339bf98fSThomas Graf }
1436339bf98fSThomas Graf 
14375c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
14385c766d64SJiri Pirko {
14395c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
14405c766d64SJiri Pirko }
14415c766d64SJiri Pirko 
14425c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
14435c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
14445c766d64SJiri Pirko {
14455c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
14465c766d64SJiri Pirko 
14475c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
14485c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
14495c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
14505c766d64SJiri Pirko 	ci.ifa_valid = valid;
14515c766d64SJiri Pirko 
14525c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
14535c766d64SJiri Pirko }
14545c766d64SJiri Pirko 
14551da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
145615e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
14571da177e4SLinus Torvalds {
14581da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
14591da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
14605c766d64SJiri Pirko 	u32 preferred, valid;
14611da177e4SLinus Torvalds 
146215e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
146347f68512SThomas Graf 	if (nlh == NULL)
146426932566SPatrick McHardy 		return -EMSGSIZE;
146547f68512SThomas Graf 
146647f68512SThomas Graf 	ifm = nlmsg_data(nlh);
14671da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
14681da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
14695c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
14701da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
14711da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
14721da177e4SLinus Torvalds 
14735c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
14745c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
14755c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
14765c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
14775c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
14785c766d64SJiri Pirko 
14795c766d64SJiri Pirko 			if (preferred > tval)
14805c766d64SJiri Pirko 				preferred -= tval;
14815c766d64SJiri Pirko 			else
14825c766d64SJiri Pirko 				preferred = 0;
14835c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
14845c766d64SJiri Pirko 				if (valid > tval)
14855c766d64SJiri Pirko 					valid -= tval;
14865c766d64SJiri Pirko 				else
14875c766d64SJiri Pirko 					valid = 0;
14885c766d64SJiri Pirko 			}
14895c766d64SJiri Pirko 		}
14905c766d64SJiri Pirko 	} else {
14915c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
14925c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
14935c766d64SJiri Pirko 	}
1494f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1495f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1496f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1497f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) ||
1498f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1499f3756b79SDavid S. Miller 	     nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1500f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
15015c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
15025c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
15035c766d64SJiri Pirko 			  preferred, valid))
1504f3756b79SDavid S. Miller 		goto nla_put_failure;
150547f68512SThomas Graf 
150647f68512SThomas Graf 	return nlmsg_end(skb, nlh);
150747f68512SThomas Graf 
150847f68512SThomas Graf nla_put_failure:
150926932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
151026932566SPatrick McHardy 	return -EMSGSIZE;
15111da177e4SLinus Torvalds }
15121da177e4SLinus Torvalds 
15131da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
15141da177e4SLinus Torvalds {
15153b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1516eec4df98SEric Dumazet 	int h, s_h;
1517eec4df98SEric Dumazet 	int idx, s_idx;
1518eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
15191da177e4SLinus Torvalds 	struct net_device *dev;
15201da177e4SLinus Torvalds 	struct in_device *in_dev;
15211da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1522eec4df98SEric Dumazet 	struct hlist_head *head;
15231da177e4SLinus Torvalds 
1524eec4df98SEric Dumazet 	s_h = cb->args[0];
1525eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1526eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1527eec4df98SEric Dumazet 
1528eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
15297562f876SPavel Emelianov 		idx = 0;
1530eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1531eec4df98SEric Dumazet 		rcu_read_lock();
15320465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
15330465277fSNicolas Dichtel 			  net->dev_base_seq;
1534b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
15351da177e4SLinus Torvalds 			if (idx < s_idx)
15367562f876SPavel Emelianov 				goto cont;
15374b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
15381da177e4SLinus Torvalds 				s_ip_idx = 0;
1539eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
15409f9354b9SEric Dumazet 			if (!in_dev)
15417562f876SPavel Emelianov 				goto cont;
15421da177e4SLinus Torvalds 
15431da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
15441da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
15451da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1546596e4150SStephen Hemminger 					continue;
1547eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
154815e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
15491da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1550eec4df98SEric Dumazet 					     RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1551eec4df98SEric Dumazet 					rcu_read_unlock();
15521da177e4SLinus Torvalds 					goto done;
15531da177e4SLinus Torvalds 				}
15540465277fSNicolas Dichtel 				nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1555eec4df98SEric Dumazet 			}
15567562f876SPavel Emelianov cont:
15577562f876SPavel Emelianov 			idx++;
15581da177e4SLinus Torvalds 		}
1559eec4df98SEric Dumazet 		rcu_read_unlock();
1560eec4df98SEric Dumazet 	}
15611da177e4SLinus Torvalds 
15621da177e4SLinus Torvalds done:
1563eec4df98SEric Dumazet 	cb->args[0] = h;
1564eec4df98SEric Dumazet 	cb->args[1] = idx;
1565eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
15661da177e4SLinus Torvalds 
15671da177e4SLinus Torvalds 	return skb->len;
15681da177e4SLinus Torvalds }
15691da177e4SLinus Torvalds 
1570d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
157115e47304SEric W. Biederman 		      u32 portid)
15721da177e4SLinus Torvalds {
157347f68512SThomas Graf 	struct sk_buff *skb;
1574d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1575d6062cbbSThomas Graf 	int err = -ENOBUFS;
15764b8aa9abSDenis V. Lunev 	struct net *net;
15771da177e4SLinus Torvalds 
1578c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1579339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
158047f68512SThomas Graf 	if (skb == NULL)
1581d6062cbbSThomas Graf 		goto errout;
1582d6062cbbSThomas Graf 
158315e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
158426932566SPatrick McHardy 	if (err < 0) {
158526932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
158626932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
158726932566SPatrick McHardy 		kfree_skb(skb);
158826932566SPatrick McHardy 		goto errout;
158926932566SPatrick McHardy 	}
159015e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
15911ce85fe4SPablo Neira Ayuso 	return;
1592d6062cbbSThomas Graf errout:
1593d6062cbbSThomas Graf 	if (err < 0)
15944b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
15951da177e4SLinus Torvalds }
15961da177e4SLinus Torvalds 
15979f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev)
15989f0f7272SThomas Graf {
15991fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16009f0f7272SThomas Graf 
16019f0f7272SThomas Graf 	if (!in_dev)
16029f0f7272SThomas Graf 		return 0;
16039f0f7272SThomas Graf 
16049f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
16059f0f7272SThomas Graf }
16069f0f7272SThomas Graf 
16079f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
16089f0f7272SThomas Graf {
16091fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16109f0f7272SThomas Graf 	struct nlattr *nla;
16119f0f7272SThomas Graf 	int i;
16129f0f7272SThomas Graf 
16139f0f7272SThomas Graf 	if (!in_dev)
16149f0f7272SThomas Graf 		return -ENODATA;
16159f0f7272SThomas Graf 
16169f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
16179f0f7272SThomas Graf 	if (nla == NULL)
16189f0f7272SThomas Graf 		return -EMSGSIZE;
16199f0f7272SThomas Graf 
16209f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
16219f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
16229f0f7272SThomas Graf 
16239f0f7272SThomas Graf 	return 0;
16249f0f7272SThomas Graf }
16259f0f7272SThomas Graf 
16269f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
16279f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
16289f0f7272SThomas Graf };
16299f0f7272SThomas Graf 
1630cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1631cf7afbfeSThomas Graf 				 const struct nlattr *nla)
16329f0f7272SThomas Graf {
16339f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
16349f0f7272SThomas Graf 	int err, rem;
16359f0f7272SThomas Graf 
1636f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1637cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
16389f0f7272SThomas Graf 
16399f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
16409f0f7272SThomas Graf 	if (err < 0)
16419f0f7272SThomas Graf 		return err;
16429f0f7272SThomas Graf 
16439f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
16449f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
16459f0f7272SThomas Graf 			int cfgid = nla_type(a);
16469f0f7272SThomas Graf 
16479f0f7272SThomas Graf 			if (nla_len(a) < 4)
16489f0f7272SThomas Graf 				return -EINVAL;
16499f0f7272SThomas Graf 
16509f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
16519f0f7272SThomas Graf 				return -EINVAL;
16529f0f7272SThomas Graf 		}
16539f0f7272SThomas Graf 	}
16549f0f7272SThomas Graf 
1655cf7afbfeSThomas Graf 	return 0;
1656cf7afbfeSThomas Graf }
1657cf7afbfeSThomas Graf 
1658cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1659cf7afbfeSThomas Graf {
1660f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1661cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1662cf7afbfeSThomas Graf 	int rem;
1663cf7afbfeSThomas Graf 
1664cf7afbfeSThomas Graf 	if (!in_dev)
1665cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1666cf7afbfeSThomas Graf 
1667cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1668cf7afbfeSThomas Graf 		BUG();
1669cf7afbfeSThomas Graf 
16709f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
16719f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
16729f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
16739f0f7272SThomas Graf 	}
16749f0f7272SThomas Graf 
16759f0f7272SThomas Graf 	return 0;
16769f0f7272SThomas Graf }
16779f0f7272SThomas Graf 
1678edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1679edc9e748SNicolas Dichtel {
1680edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1681edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1682edc9e748SNicolas Dichtel 
16839e551110SNicolas Dichtel 	/* type -1 is used for ALL */
16849e551110SNicolas Dichtel 	if (type == -1 || type == NETCONFA_FORWARDING)
1685edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1686cc535dfbSNicolas Dichtel 	if (type == -1 || type == NETCONFA_RP_FILTER)
1687cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1688d67b8c61SNicolas Dichtel 	if (type == -1 || type == NETCONFA_MC_FORWARDING)
1689d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
1690edc9e748SNicolas Dichtel 
1691edc9e748SNicolas Dichtel 	return size;
1692edc9e748SNicolas Dichtel }
1693edc9e748SNicolas Dichtel 
1694edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1695edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1696edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1697edc9e748SNicolas Dichtel 				     int type)
1698edc9e748SNicolas Dichtel {
1699edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1700edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1701edc9e748SNicolas Dichtel 
1702edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1703edc9e748SNicolas Dichtel 			flags);
1704edc9e748SNicolas Dichtel 	if (nlh == NULL)
1705edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1706edc9e748SNicolas Dichtel 
1707edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1708edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1709edc9e748SNicolas Dichtel 
1710edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1711edc9e748SNicolas Dichtel 		goto nla_put_failure;
1712edc9e748SNicolas Dichtel 
17139e551110SNicolas Dichtel 	/* type -1 is used for ALL */
17149e551110SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_FORWARDING) &&
1715edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1716edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1717edc9e748SNicolas Dichtel 		goto nla_put_failure;
1718cc535dfbSNicolas Dichtel 	if ((type == -1 || type == NETCONFA_RP_FILTER) &&
1719cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1720cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1721cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1722d67b8c61SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
1723d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1724d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1725d67b8c61SNicolas Dichtel 		goto nla_put_failure;
1726edc9e748SNicolas Dichtel 
1727edc9e748SNicolas Dichtel 	return nlmsg_end(skb, nlh);
1728edc9e748SNicolas Dichtel 
1729edc9e748SNicolas Dichtel nla_put_failure:
1730edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1731edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1732edc9e748SNicolas Dichtel }
1733edc9e748SNicolas Dichtel 
1734d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
1735edc9e748SNicolas Dichtel 				 struct ipv4_devconf *devconf)
1736edc9e748SNicolas Dichtel {
1737edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1738edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1739edc9e748SNicolas Dichtel 
1740edc9e748SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
1741edc9e748SNicolas Dichtel 	if (skb == NULL)
1742edc9e748SNicolas Dichtel 		goto errout;
1743edc9e748SNicolas Dichtel 
1744edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
1745edc9e748SNicolas Dichtel 					RTM_NEWNETCONF, 0, type);
1746edc9e748SNicolas Dichtel 	if (err < 0) {
1747edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1748edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1749edc9e748SNicolas Dichtel 		kfree_skb(skb);
1750edc9e748SNicolas Dichtel 		goto errout;
1751edc9e748SNicolas Dichtel 	}
1752edc9e748SNicolas Dichtel 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC);
1753edc9e748SNicolas Dichtel 	return;
1754edc9e748SNicolas Dichtel errout:
1755edc9e748SNicolas Dichtel 	if (err < 0)
1756edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1757edc9e748SNicolas Dichtel }
1758edc9e748SNicolas Dichtel 
17599e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
17609e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
17619e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1762cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
17639e551110SNicolas Dichtel };
17649e551110SNicolas Dichtel 
17659e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
1766661d2967SThomas Graf 				    struct nlmsghdr *nlh)
17679e551110SNicolas Dichtel {
17689e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
17699e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
17709e551110SNicolas Dichtel 	struct netconfmsg *ncm;
17719e551110SNicolas Dichtel 	struct sk_buff *skb;
17729e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
17739e551110SNicolas Dichtel 	struct in_device *in_dev;
17749e551110SNicolas Dichtel 	struct net_device *dev;
17759e551110SNicolas Dichtel 	int ifindex;
17769e551110SNicolas Dichtel 	int err;
17779e551110SNicolas Dichtel 
17789e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
17799e551110SNicolas Dichtel 			  devconf_ipv4_policy);
17809e551110SNicolas Dichtel 	if (err < 0)
17819e551110SNicolas Dichtel 		goto errout;
17829e551110SNicolas Dichtel 
17839e551110SNicolas Dichtel 	err = EINVAL;
17849e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
17859e551110SNicolas Dichtel 		goto errout;
17869e551110SNicolas Dichtel 
17879e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
17889e551110SNicolas Dichtel 	switch (ifindex) {
17899e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
17909e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
17919e551110SNicolas Dichtel 		break;
17929e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
17939e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
17949e551110SNicolas Dichtel 		break;
17959e551110SNicolas Dichtel 	default:
17969e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
17979e551110SNicolas Dichtel 		if (dev == NULL)
17989e551110SNicolas Dichtel 			goto errout;
17999e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
18009e551110SNicolas Dichtel 		if (in_dev == NULL)
18019e551110SNicolas Dichtel 			goto errout;
18029e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
18039e551110SNicolas Dichtel 		break;
18049e551110SNicolas Dichtel 	}
18059e551110SNicolas Dichtel 
18069e551110SNicolas Dichtel 	err = -ENOBUFS;
18079e551110SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
18089e551110SNicolas Dichtel 	if (skb == NULL)
18099e551110SNicolas Dichtel 		goto errout;
18109e551110SNicolas Dichtel 
18119e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
18129e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
18139e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
18149e551110SNicolas Dichtel 					-1);
18159e551110SNicolas Dichtel 	if (err < 0) {
18169e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
18179e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
18189e551110SNicolas Dichtel 		kfree_skb(skb);
18199e551110SNicolas Dichtel 		goto errout;
18209e551110SNicolas Dichtel 	}
18219e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
18229e551110SNicolas Dichtel errout:
18239e551110SNicolas Dichtel 	return err;
18249e551110SNicolas Dichtel }
18259e551110SNicolas Dichtel 
18267a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
18277a674200SNicolas Dichtel 				     struct netlink_callback *cb)
18287a674200SNicolas Dichtel {
18297a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
18307a674200SNicolas Dichtel 	int h, s_h;
18317a674200SNicolas Dichtel 	int idx, s_idx;
18327a674200SNicolas Dichtel 	struct net_device *dev;
18337a674200SNicolas Dichtel 	struct in_device *in_dev;
18347a674200SNicolas Dichtel 	struct hlist_head *head;
18357a674200SNicolas Dichtel 
18367a674200SNicolas Dichtel 	s_h = cb->args[0];
18377a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
18387a674200SNicolas Dichtel 
18397a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
18407a674200SNicolas Dichtel 		idx = 0;
18417a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
18427a674200SNicolas Dichtel 		rcu_read_lock();
18430465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
18440465277fSNicolas Dichtel 			  net->dev_base_seq;
18457a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
18467a674200SNicolas Dichtel 			if (idx < s_idx)
18477a674200SNicolas Dichtel 				goto cont;
18487a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
18497a674200SNicolas Dichtel 			if (!in_dev)
18507a674200SNicolas Dichtel 				goto cont;
18517a674200SNicolas Dichtel 
18527a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
18537a674200SNicolas Dichtel 						      &in_dev->cnf,
18547a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
18557a674200SNicolas Dichtel 						      cb->nlh->nlmsg_seq,
18567a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
18577a674200SNicolas Dichtel 						      NLM_F_MULTI,
18587a674200SNicolas Dichtel 						      -1) <= 0) {
18597a674200SNicolas Dichtel 				rcu_read_unlock();
18607a674200SNicolas Dichtel 				goto done;
18617a674200SNicolas Dichtel 			}
18620465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
18637a674200SNicolas Dichtel cont:
18647a674200SNicolas Dichtel 			idx++;
18657a674200SNicolas Dichtel 		}
18667a674200SNicolas Dichtel 		rcu_read_unlock();
18677a674200SNicolas Dichtel 	}
18687a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
18697a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
18707a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
18717a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
18727a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
18737a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
18747a674200SNicolas Dichtel 					      -1) <= 0)
18757a674200SNicolas Dichtel 			goto done;
18767a674200SNicolas Dichtel 		else
18777a674200SNicolas Dichtel 			h++;
18787a674200SNicolas Dichtel 	}
18797a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
18807a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
18817a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
18827a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
18837a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
18847a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
18857a674200SNicolas Dichtel 					      -1) <= 0)
18867a674200SNicolas Dichtel 			goto done;
18877a674200SNicolas Dichtel 		else
18887a674200SNicolas Dichtel 			h++;
18897a674200SNicolas Dichtel 	}
18907a674200SNicolas Dichtel done:
18917a674200SNicolas Dichtel 	cb->args[0] = h;
18927a674200SNicolas Dichtel 	cb->args[1] = idx;
18937a674200SNicolas Dichtel 
18947a674200SNicolas Dichtel 	return skb->len;
18957a674200SNicolas Dichtel }
18967a674200SNicolas Dichtel 
18971da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
18981da177e4SLinus Torvalds 
1899c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
190031be3085SHerbert Xu {
190131be3085SHerbert Xu 	struct net_device *dev;
190231be3085SHerbert Xu 
190331be3085SHerbert Xu 	rcu_read_lock();
1904c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1905c6d14c84SEric Dumazet 		struct in_device *in_dev;
1906c6d14c84SEric Dumazet 
190731be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
190831be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
19099355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1910c6d14c84SEric Dumazet 	}
191131be3085SHerbert Xu 	rcu_read_unlock();
191231be3085SHerbert Xu }
191331be3085SHerbert Xu 
1914c6d14c84SEric Dumazet /* called with RTNL locked */
1915c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
191668dd299bSPavel Emelyanov {
191768dd299bSPavel Emelyanov 	struct net_device *dev;
1918586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
191968dd299bSPavel Emelyanov 
1920586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
19219355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
1922edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1923edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
1924edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
1925edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1926edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
1927edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
192868dd299bSPavel Emelyanov 
1929c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
193068dd299bSPavel Emelyanov 		struct in_device *in_dev;
19310187bdfbSBen Hutchings 		if (on)
19320187bdfbSBen Hutchings 			dev_disable_lro(dev);
193368dd299bSPavel Emelyanov 		rcu_read_lock();
193468dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
1935edc9e748SNicolas Dichtel 		if (in_dev) {
193668dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1937edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1938edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
1939edc9e748SNicolas Dichtel 		}
194068dd299bSPavel Emelyanov 		rcu_read_unlock();
194168dd299bSPavel Emelyanov 	}
194268dd299bSPavel Emelyanov }
194368dd299bSPavel Emelyanov 
1944fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
19458d65af78SAlexey Dobriyan 			     void __user *buffer,
194631be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
194731be3085SHerbert Xu {
1948d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
19498d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
1950d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
195131be3085SHerbert Xu 
195231be3085SHerbert Xu 	if (write) {
195331be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
1954c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
195531be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
195631be3085SHerbert Xu 
195731be3085SHerbert Xu 		set_bit(i, cnf->state);
195831be3085SHerbert Xu 
19599355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
1960c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
1961d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
1962d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
1963d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
19644ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
1965cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
1966cc535dfbSNicolas Dichtel 		    new_value != old_value) {
1967cc535dfbSNicolas Dichtel 			int ifindex;
1968cc535dfbSNicolas Dichtel 
1969cc535dfbSNicolas Dichtel 			if (cnf == net->ipv4.devconf_dflt)
1970cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_DEFAULT;
1971cc535dfbSNicolas Dichtel 			else if (cnf == net->ipv4.devconf_all)
1972cc535dfbSNicolas Dichtel 				ifindex = NETCONFA_IFINDEX_ALL;
1973cc535dfbSNicolas Dichtel 			else {
1974cc535dfbSNicolas Dichtel 				struct in_device *idev =
1975cc535dfbSNicolas Dichtel 					container_of(cnf, struct in_device,
1976cc535dfbSNicolas Dichtel 						     cnf);
1977cc535dfbSNicolas Dichtel 				ifindex = idev->dev->ifindex;
1978cc535dfbSNicolas Dichtel 			}
1979cc535dfbSNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER,
1980cc535dfbSNicolas Dichtel 						    ifindex, cnf);
1981cc535dfbSNicolas Dichtel 		}
198231be3085SHerbert Xu 	}
198331be3085SHerbert Xu 
198431be3085SHerbert Xu 	return ret;
198531be3085SHerbert Xu }
198631be3085SHerbert Xu 
1987fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
19888d65af78SAlexey Dobriyan 				  void __user *buffer,
19891da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
19901da177e4SLinus Torvalds {
19911da177e4SLinus Torvalds 	int *valp = ctl->data;
19921da177e4SLinus Torvalds 	int val = *valp;
199388af182eSEric W. Biederman 	loff_t pos = *ppos;
19948d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
19951da177e4SLinus Torvalds 
19961da177e4SLinus Torvalds 	if (write && *valp != val) {
1997c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
1998c0ce9fb3SPavel Emelyanov 
19990187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
200088af182eSEric W. Biederman 			if (!rtnl_trylock()) {
200188af182eSEric W. Biederman 				/* Restore the original values before restarting */
200288af182eSEric W. Biederman 				*valp = val;
200388af182eSEric W. Biederman 				*ppos = pos;
20049b8adb5eSEric W. Biederman 				return restart_syscall();
200588af182eSEric W. Biederman 			}
20060187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2007c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2008edc9e748SNicolas Dichtel 			} else {
20090187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
20100187bdfbSBen Hutchings 				struct in_device *idev =
20110187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2012edc9e748SNicolas Dichtel 				if (*valp)
20130187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
2014edc9e748SNicolas Dichtel 				inet_netconf_notify_devconf(net,
2015edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2016edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2017edc9e748SNicolas Dichtel 							    cnf);
20180187bdfbSBen Hutchings 			}
20190187bdfbSBen Hutchings 			rtnl_unlock();
20204ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2021edc9e748SNicolas Dichtel 		} else
2022edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
2023edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2024edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
20250187bdfbSBen Hutchings 	}
20261da177e4SLinus Torvalds 
20271da177e4SLinus Torvalds 	return ret;
20281da177e4SLinus Torvalds }
20291da177e4SLinus Torvalds 
2030fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
20318d65af78SAlexey Dobriyan 				void __user *buffer,
20321da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
20331da177e4SLinus Torvalds {
20341da177e4SLinus Torvalds 	int *valp = ctl->data;
20351da177e4SLinus Torvalds 	int val = *valp;
20368d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
203776e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
20381da177e4SLinus Torvalds 
20391da177e4SLinus Torvalds 	if (write && *valp != val)
20404ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
20411da177e4SLinus Torvalds 
20421da177e4SLinus Torvalds 	return ret;
20431da177e4SLinus Torvalds }
20441da177e4SLinus Torvalds 
2045f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
204642f811b8SHerbert Xu 	{ \
204742f811b8SHerbert Xu 		.procname	= name, \
204842f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
204902291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
205042f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
205142f811b8SHerbert Xu 		.mode		= mval, \
205242f811b8SHerbert Xu 		.proc_handler	= proc, \
205331be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
205442f811b8SHerbert Xu 	}
205542f811b8SHerbert Xu 
205642f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2057f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
205842f811b8SHerbert Xu 
205942f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2060f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
206142f811b8SHerbert Xu 
2062f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2063f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
206442f811b8SHerbert Xu 
206542f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2066f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
206742f811b8SHerbert Xu 
20681da177e4SLinus Torvalds static struct devinet_sysctl_table {
20691da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
207002291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
20711da177e4SLinus Torvalds } devinet_sysctl = {
20721da177e4SLinus Torvalds 	.devinet_vars = {
207342f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2074f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
207542f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
207642f811b8SHerbert Xu 
207742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
207842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
207942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
208042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
208142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
208242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
208342f811b8SHerbert Xu 					"accept_source_route"),
20848153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
208528f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
208642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
208742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
208842f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
208942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
209042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
209142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
209242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
209342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
209442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2095eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
209665324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
20975c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
20985c6fe01cSWilliam Manley 					"force_igmp_version"),
209942f811b8SHerbert Xu 
210042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
210142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
210242f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
210342f811b8SHerbert Xu 					      "promote_secondaries"),
2104d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2105d0daebc3SThomas Graf 					      "route_localnet"),
21061da177e4SLinus Torvalds 	},
21071da177e4SLinus Torvalds };
21081da177e4SLinus Torvalds 
2109ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
2110f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
21111da177e4SLinus Torvalds {
21121da177e4SLinus Torvalds 	int i;
21139fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
21148607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2115bfada697SPavel Emelyanov 
21169fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
21171da177e4SLinus Torvalds 	if (!t)
21189fa89642SPavel Emelyanov 		goto out;
21199fa89642SPavel Emelyanov 
21201da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
21211da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
212231be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2123c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
21241da177e4SLinus Torvalds 	}
21251da177e4SLinus Torvalds 
21268607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
21271da177e4SLinus Torvalds 
21288607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
21291da177e4SLinus Torvalds 	if (!t->sysctl_header)
21308607ddb8SEric W. Biederman 		goto free;
21311da177e4SLinus Torvalds 
21321da177e4SLinus Torvalds 	p->sysctl = t;
2133ea40b324SPavel Emelyanov 	return 0;
21341da177e4SLinus Torvalds 
21351da177e4SLinus Torvalds free:
21361da177e4SLinus Torvalds 	kfree(t);
21379fa89642SPavel Emelyanov out:
2138ea40b324SPavel Emelyanov 	return -ENOBUFS;
21391da177e4SLinus Torvalds }
21401da177e4SLinus Torvalds 
214151602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
214266f27a52SPavel Emelyanov {
214351602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
214466f27a52SPavel Emelyanov 
214551602b2aSPavel Emelyanov 	if (t == NULL)
214651602b2aSPavel Emelyanov 		return;
214751602b2aSPavel Emelyanov 
214851602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
2149ff538818SLucian Adrian Grijincu 	unregister_net_sysctl_table(t->sysctl_header);
21501da177e4SLinus Torvalds 	kfree(t);
21511da177e4SLinus Torvalds }
215251602b2aSPavel Emelyanov 
215351602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev)
215451602b2aSPavel Emelyanov {
215554716e3bSEric W. Biederman 	neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
2156c346dca1SYOSHIFUJI Hideaki 	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
2157f8572d8fSEric W. Biederman 					&idev->cnf);
215851602b2aSPavel Emelyanov }
215951602b2aSPavel Emelyanov 
216051602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
216151602b2aSPavel Emelyanov {
216251602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
216351602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
21641da177e4SLinus Torvalds }
21651da177e4SLinus Torvalds 
216668dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
216768dd299bSPavel Emelyanov 	{
216868dd299bSPavel Emelyanov 		.procname	= "ip_forward",
216968dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
217002291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
217168dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
217268dd299bSPavel Emelyanov 		.mode		= 0644,
217368dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
217468dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2175c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
217668dd299bSPavel Emelyanov 	},
217768dd299bSPavel Emelyanov 	{ },
217868dd299bSPavel Emelyanov };
21792a75de0cSEric Dumazet #endif
218068dd299bSPavel Emelyanov 
2181752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2182752d14dcSPavel Emelyanov {
2183752d14dcSPavel Emelyanov 	int err;
2184752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
21852a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
21862a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2187752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
21882a75de0cSEric Dumazet #endif
2189752d14dcSPavel Emelyanov 
2190752d14dcSPavel Emelyanov 	err = -ENOMEM;
2191752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2192752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2193752d14dcSPavel Emelyanov 
219409ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2195752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
2196752d14dcSPavel Emelyanov 		if (all == NULL)
2197752d14dcSPavel Emelyanov 			goto err_alloc_all;
2198752d14dcSPavel Emelyanov 
2199752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
2200752d14dcSPavel Emelyanov 		if (dflt == NULL)
2201752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2202752d14dcSPavel Emelyanov 
22032a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2204752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
2205752d14dcSPavel Emelyanov 		if (tbl == NULL)
2206752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2207752d14dcSPavel Emelyanov 
220802291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2209752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2210752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
22112a75de0cSEric Dumazet #endif
2212752d14dcSPavel Emelyanov 	}
2213752d14dcSPavel Emelyanov 
2214752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2215f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
2216752d14dcSPavel Emelyanov 	if (err < 0)
2217752d14dcSPavel Emelyanov 		goto err_reg_all;
2218752d14dcSPavel Emelyanov 
2219f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
2220752d14dcSPavel Emelyanov 	if (err < 0)
2221752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2222752d14dcSPavel Emelyanov 
2223752d14dcSPavel Emelyanov 	err = -ENOMEM;
22248607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
2225752d14dcSPavel Emelyanov 	if (forw_hdr == NULL)
2226752d14dcSPavel Emelyanov 		goto err_reg_ctl;
22272a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2228752d14dcSPavel Emelyanov #endif
2229752d14dcSPavel Emelyanov 
2230752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2231752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2232752d14dcSPavel Emelyanov 	return 0;
2233752d14dcSPavel Emelyanov 
2234752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2235752d14dcSPavel Emelyanov err_reg_ctl:
2236752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
2237752d14dcSPavel Emelyanov err_reg_dflt:
2238752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
2239752d14dcSPavel Emelyanov err_reg_all:
2240752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2241752d14dcSPavel Emelyanov 		kfree(tbl);
2242752d14dcSPavel Emelyanov err_alloc_ctl:
22432a75de0cSEric Dumazet #endif
2244752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2245752d14dcSPavel Emelyanov 		kfree(dflt);
2246752d14dcSPavel Emelyanov err_alloc_dflt:
2247752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2248752d14dcSPavel Emelyanov 		kfree(all);
2249752d14dcSPavel Emelyanov err_alloc_all:
2250752d14dcSPavel Emelyanov 	return err;
2251752d14dcSPavel Emelyanov }
2252752d14dcSPavel Emelyanov 
2253752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2254752d14dcSPavel Emelyanov {
22552a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2256752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2257752d14dcSPavel Emelyanov 
2258752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2259752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2260752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
2261752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
2262752d14dcSPavel Emelyanov 	kfree(tbl);
22632a75de0cSEric Dumazet #endif
2264752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2265752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2266752d14dcSPavel Emelyanov }
2267752d14dcSPavel Emelyanov 
2268752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2269752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2270752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2271752d14dcSPavel Emelyanov };
2272752d14dcSPavel Emelyanov 
22739f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = {
22749f0f7272SThomas Graf 	.family		  = AF_INET,
22759f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
22769f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2277cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2278cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
22799f0f7272SThomas Graf };
22809f0f7272SThomas Graf 
22811da177e4SLinus Torvalds void __init devinet_init(void)
22821da177e4SLinus Torvalds {
2283fd23c3b3SDavid S. Miller 	int i;
2284fd23c3b3SDavid S. Miller 
2285fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2286fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2287fd23c3b3SDavid S. Miller 
2288752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2289752d14dcSPavel Emelyanov 
22901da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
22911da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
229263f3444fSThomas Graf 
22935c766d64SJiri Pirko 	schedule_delayed_work(&check_lifetime_work, 0);
22945c766d64SJiri Pirko 
22959f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
22969f0f7272SThomas Graf 
2297c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
2298c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
2299c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
23009e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
23017a674200SNicolas Dichtel 		      inet_netconf_dump_devconf, NULL);
23021da177e4SLinus Torvalds }
23031da177e4SLinus Torvalds 
2304