xref: /openbmc/linux/net/ipv4/devinet.c (revision b1974ed0)
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,
762690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
772690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
7842f811b8SHerbert Xu 	},
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = {
8242f811b8SHerbert Xu 	.data = {
8302291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8402291680SEric W. Biederman 		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8502291680SEric W. Biederman 		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8602291680SEric W. Biederman 		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8702291680SEric W. Biederman 		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
882690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
892690048cSWilliam Manley 		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
9042f811b8SHerbert Xu 	},
911da177e4SLinus Torvalds };
921da177e4SLinus Torvalds 
939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \
949355bbd6SPavel Emelyanov 	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9542f811b8SHerbert Xu 
96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
975c753978SThomas Graf 	[IFA_LOCAL]     	= { .type = NLA_U32 },
985c753978SThomas Graf 	[IFA_ADDRESS]   	= { .type = NLA_U32 },
995c753978SThomas Graf 	[IFA_BROADCAST] 	= { .type = NLA_U32 },
1005176f91eSThomas Graf 	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
1015c766d64SJiri Pirko 	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
102ad6c8135SJiri Pirko 	[IFA_FLAGS]		= { .type = NLA_U32 },
1035c753978SThomas Graf };
1045c753978SThomas Graf 
10540384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT	8
10640384999SEric Dumazet #define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
10740384999SEric Dumazet 
108fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
109fd23c3b3SDavid S. Miller 
1106eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr)
111fd23c3b3SDavid S. Miller {
11240384999SEric Dumazet 	u32 val = (__force u32) addr ^ net_hash_mix(net);
113fd23c3b3SDavid S. Miller 
11440384999SEric Dumazet 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
115fd23c3b3SDavid S. Miller }
116fd23c3b3SDavid S. Miller 
117fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
118fd23c3b3SDavid S. Miller {
11940384999SEric Dumazet 	u32 hash = inet_addr_hash(net, ifa->ifa_local);
120fd23c3b3SDavid S. Miller 
12132a4be48SWANG Cong 	ASSERT_RTNL();
122fd23c3b3SDavid S. Miller 	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
123fd23c3b3SDavid S. Miller }
124fd23c3b3SDavid S. Miller 
125fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa)
126fd23c3b3SDavid S. Miller {
12732a4be48SWANG Cong 	ASSERT_RTNL();
128fd23c3b3SDavid S. Miller 	hlist_del_init_rcu(&ifa->hash);
129fd23c3b3SDavid S. Miller }
130fd23c3b3SDavid S. Miller 
1319435eb1cSDavid S. Miller /**
1329435eb1cSDavid S. Miller  * __ip_dev_find - find the first device with a given source address.
1339435eb1cSDavid S. Miller  * @net: the net namespace
1349435eb1cSDavid S. Miller  * @addr: the source address
1359435eb1cSDavid S. Miller  * @devref: if true, take a reference on the found device
1369435eb1cSDavid S. Miller  *
1379435eb1cSDavid S. Miller  * If a caller uses devref=false, it should be protected by RCU, or RTNL
1389435eb1cSDavid S. Miller  */
1399435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
1409435eb1cSDavid S. Miller {
14140384999SEric Dumazet 	u32 hash = inet_addr_hash(net, addr);
1429435eb1cSDavid S. Miller 	struct net_device *result = NULL;
1439435eb1cSDavid S. Miller 	struct in_ifaddr *ifa;
1449435eb1cSDavid S. Miller 
1459435eb1cSDavid S. Miller 	rcu_read_lock();
146b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) {
14740384999SEric Dumazet 		if (ifa->ifa_local == addr) {
1489435eb1cSDavid S. Miller 			struct net_device *dev = ifa->ifa_dev->dev;
1499435eb1cSDavid S. Miller 
1509435eb1cSDavid S. Miller 			if (!net_eq(dev_net(dev), net))
1519435eb1cSDavid S. Miller 				continue;
1529435eb1cSDavid S. Miller 			result = dev;
1539435eb1cSDavid S. Miller 			break;
1549435eb1cSDavid S. Miller 		}
1559435eb1cSDavid S. Miller 	}
156406b6f97SDavid S. Miller 	if (!result) {
157406b6f97SDavid S. Miller 		struct flowi4 fl4 = { .daddr = addr };
158406b6f97SDavid S. Miller 		struct fib_result res = { 0 };
159406b6f97SDavid S. Miller 		struct fib_table *local;
160406b6f97SDavid S. Miller 
161406b6f97SDavid S. Miller 		/* Fallback to FIB local table so that communication
162406b6f97SDavid S. Miller 		 * over loopback subnets work.
163406b6f97SDavid S. Miller 		 */
164406b6f97SDavid S. Miller 		local = fib_get_table(net, RT_TABLE_LOCAL);
165406b6f97SDavid S. Miller 		if (local &&
166406b6f97SDavid S. Miller 		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
167406b6f97SDavid S. Miller 		    res.type == RTN_LOCAL)
168406b6f97SDavid S. Miller 			result = FIB_RES_DEV(res);
169406b6f97SDavid S. Miller 	}
1709435eb1cSDavid S. Miller 	if (result && devref)
1719435eb1cSDavid S. Miller 		dev_hold(result);
1729435eb1cSDavid S. Miller 	rcu_read_unlock();
1739435eb1cSDavid S. Miller 	return result;
1749435eb1cSDavid S. Miller }
1759435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find);
1769435eb1cSDavid S. Miller 
177d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
1781da177e4SLinus Torvalds 
179e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
1801da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
1811da177e4SLinus Torvalds 			 int destroy);
1821da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
18320e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev);
18451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev);
18551602b2aSPavel Emelyanov #else
18620e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
18751602b2aSPavel Emelyanov {
18820e61da7SWANG Cong 	return 0;
18951602b2aSPavel Emelyanov }
19040384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev)
19151602b2aSPavel Emelyanov {
19251602b2aSPavel Emelyanov }
1931da177e4SLinus Torvalds #endif
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds /* Locks all the inet devices. */
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void)
1981da177e4SLinus Torvalds {
19993adcc80SAlexey Dobriyan 	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head)
2031da177e4SLinus Torvalds {
2041da177e4SLinus Torvalds 	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
2051da177e4SLinus Torvalds 	if (ifa->ifa_dev)
2061da177e4SLinus Torvalds 		in_dev_put(ifa->ifa_dev);
2071da177e4SLinus Torvalds 	kfree(ifa);
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
21040384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa)
2111da177e4SLinus Torvalds {
2121da177e4SLinus Torvalds 	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev)
2161da177e4SLinus Torvalds {
2171da177e4SLinus Torvalds 	struct net_device *dev = idev->dev;
2181da177e4SLinus Torvalds 
219547b792cSIlpo Järvinen 	WARN_ON(idev->ifa_list);
220547b792cSIlpo Järvinen 	WARN_ON(idev->mc_list);
221e9897071SEric Dumazet 	kfree(rcu_dereference_protected(idev->mc_hash, 1));
2221da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG
22391df42beSJoe Perches 	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
2241da177e4SLinus Torvalds #endif
2251da177e4SLinus Torvalds 	dev_put(dev);
2261da177e4SLinus Torvalds 	if (!idev->dead)
2279f9354b9SEric Dumazet 		pr_err("Freeing alive in_device %p\n", idev);
2289f9354b9SEric Dumazet 	else
2291da177e4SLinus Torvalds 		kfree(idev);
2301da177e4SLinus Torvalds }
2319f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy);
2321da177e4SLinus Torvalds 
23371e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev)
2341da177e4SLinus Torvalds {
2351da177e4SLinus Torvalds 	struct in_device *in_dev;
23620e61da7SWANG Cong 	int err = -ENOMEM;
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	ASSERT_RTNL();
2391da177e4SLinus Torvalds 
2400da974f4SPanagiotis Issaris 	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
2411da177e4SLinus Torvalds 	if (!in_dev)
2421da177e4SLinus Torvalds 		goto out;
243c346dca1SYOSHIFUJI Hideaki 	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
2449355bbd6SPavel Emelyanov 			sizeof(in_dev->cnf));
2451da177e4SLinus Torvalds 	in_dev->cnf.sysctl = NULL;
2461da177e4SLinus Torvalds 	in_dev->dev = dev;
2479f9354b9SEric Dumazet 	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
2489f9354b9SEric Dumazet 	if (!in_dev->arp_parms)
2491da177e4SLinus Torvalds 		goto out_kfree;
2500187bdfbSBen Hutchings 	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
2510187bdfbSBen Hutchings 		dev_disable_lro(dev);
2521da177e4SLinus Torvalds 	/* Reference in_dev->dev */
2531da177e4SLinus Torvalds 	dev_hold(dev);
25430c4cf57SDavid L Stevens 	/* Account for reference dev->ip_ptr (below) */
2551da177e4SLinus Torvalds 	in_dev_hold(in_dev);
2561da177e4SLinus Torvalds 
25720e61da7SWANG Cong 	err = devinet_sysctl_register(in_dev);
25820e61da7SWANG Cong 	if (err) {
25920e61da7SWANG Cong 		in_dev->dead = 1;
26020e61da7SWANG Cong 		in_dev_put(in_dev);
26120e61da7SWANG Cong 		in_dev = NULL;
26220e61da7SWANG Cong 		goto out;
26320e61da7SWANG Cong 	}
2641da177e4SLinus Torvalds 	ip_mc_init_dev(in_dev);
2651da177e4SLinus Torvalds 	if (dev->flags & IFF_UP)
2661da177e4SLinus Torvalds 		ip_mc_up(in_dev);
267483479ecSJarek Poplawski 
26830c4cf57SDavid L Stevens 	/* we can receive as soon as ip_ptr is set -- do this last */
269cf778b00SEric Dumazet 	rcu_assign_pointer(dev->ip_ptr, in_dev);
270483479ecSJarek Poplawski out:
27120e61da7SWANG Cong 	return in_dev ?: ERR_PTR(err);
2721da177e4SLinus Torvalds out_kfree:
2731da177e4SLinus Torvalds 	kfree(in_dev);
2741da177e4SLinus Torvalds 	in_dev = NULL;
2751da177e4SLinus Torvalds 	goto out;
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head)
2791da177e4SLinus Torvalds {
2801da177e4SLinus Torvalds 	struct in_device *idev = container_of(head, struct in_device, rcu_head);
2811da177e4SLinus Torvalds 	in_dev_put(idev);
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev)
2851da177e4SLinus Torvalds {
2861da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
2871da177e4SLinus Torvalds 	struct net_device *dev;
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds 	ASSERT_RTNL();
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	dev = in_dev->dev;
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	in_dev->dead = 1;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	ip_mc_destroy_dev(in_dev);
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	while ((ifa = in_dev->ifa_list) != NULL) {
2981da177e4SLinus Torvalds 		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
2991da177e4SLinus Torvalds 		inet_free_ifa(ifa);
3001da177e4SLinus Torvalds 	}
3011da177e4SLinus Torvalds 
302a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(dev->ip_ptr, NULL);
3031da177e4SLinus Torvalds 
30451602b2aSPavel Emelyanov 	devinet_sysctl_unregister(in_dev);
3051da177e4SLinus Torvalds 	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
3061da177e4SLinus Torvalds 	arp_ifdown(dev);
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
3091da177e4SLinus Torvalds }
3101da177e4SLinus Torvalds 
311ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
3121da177e4SLinus Torvalds {
3131da177e4SLinus Torvalds 	rcu_read_lock();
3141da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
3151da177e4SLinus Torvalds 		if (inet_ifa_match(a, ifa)) {
3161da177e4SLinus Torvalds 			if (!b || inet_ifa_match(b, ifa)) {
3171da177e4SLinus Torvalds 				rcu_read_unlock();
3181da177e4SLinus Torvalds 				return 1;
3191da177e4SLinus Torvalds 			}
3201da177e4SLinus Torvalds 		}
3211da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
3221da177e4SLinus Torvalds 	rcu_read_unlock();
3231da177e4SLinus Torvalds 	return 0;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
326d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
32715e47304SEric W. Biederman 			 int destroy, struct nlmsghdr *nlh, u32 portid)
3281da177e4SLinus Torvalds {
3298f937c60SHarald Welte 	struct in_ifaddr *promote = NULL;
3300ff60a45SJamal Hadi Salim 	struct in_ifaddr *ifa, *ifa1 = *ifap;
3310ff60a45SJamal Hadi Salim 	struct in_ifaddr *last_prim = in_dev->ifa_list;
3320ff60a45SJamal Hadi Salim 	struct in_ifaddr *prev_prom = NULL;
3330ff60a45SJamal Hadi Salim 	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 	ASSERT_RTNL();
3361da177e4SLinus Torvalds 
3378f937c60SHarald Welte 	/* 1. Deleting primary ifaddr forces deletion all secondaries
3388f937c60SHarald Welte 	 * unless alias promotion is set
3398f937c60SHarald Welte 	 **/
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
3421da177e4SLinus Torvalds 		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 		while ((ifa = *ifap1) != NULL) {
3450ff60a45SJamal Hadi Salim 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
3460ff60a45SJamal Hadi Salim 			    ifa1->ifa_scope <= ifa->ifa_scope)
3470ff60a45SJamal Hadi Salim 				last_prim = ifa;
3480ff60a45SJamal Hadi Salim 
3491da177e4SLinus Torvalds 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
3501da177e4SLinus Torvalds 			    ifa1->ifa_mask != ifa->ifa_mask ||
3511da177e4SLinus Torvalds 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
3521da177e4SLinus Torvalds 				ifap1 = &ifa->ifa_next;
3530ff60a45SJamal Hadi Salim 				prev_prom = ifa;
3541da177e4SLinus Torvalds 				continue;
3551da177e4SLinus Torvalds 			}
3561da177e4SLinus Torvalds 
3570ff60a45SJamal Hadi Salim 			if (!do_promote) {
358fd23c3b3SDavid S. Miller 				inet_hash_remove(ifa);
3591da177e4SLinus Torvalds 				*ifap1 = ifa->ifa_next;
3601da177e4SLinus Torvalds 
36115e47304SEric W. Biederman 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
362e041c683SAlan Stern 				blocking_notifier_call_chain(&inetaddr_chain,
363e041c683SAlan Stern 						NETDEV_DOWN, ifa);
3641da177e4SLinus Torvalds 				inet_free_ifa(ifa);
3658f937c60SHarald Welte 			} else {
3668f937c60SHarald Welte 				promote = ifa;
3678f937c60SHarald Welte 				break;
3688f937c60SHarald Welte 			}
3691da177e4SLinus Torvalds 		}
3701da177e4SLinus Torvalds 	}
3711da177e4SLinus Torvalds 
3722d230e2bSJulian Anastasov 	/* On promotion all secondaries from subnet are changing
3732d230e2bSJulian Anastasov 	 * the primary IP, we must remove all their routes silently
3742d230e2bSJulian Anastasov 	 * and later to add them back with new prefsrc. Do this
3752d230e2bSJulian Anastasov 	 * while all addresses are on the device list.
3762d230e2bSJulian Anastasov 	 */
3772d230e2bSJulian Anastasov 	for (ifa = promote; ifa; ifa = ifa->ifa_next) {
3782d230e2bSJulian Anastasov 		if (ifa1->ifa_mask == ifa->ifa_mask &&
3792d230e2bSJulian Anastasov 		    inet_ifa_match(ifa1->ifa_address, ifa))
3802d230e2bSJulian Anastasov 			fib_del_ifaddr(ifa, ifa1);
3812d230e2bSJulian Anastasov 	}
3822d230e2bSJulian Anastasov 
3831da177e4SLinus Torvalds 	/* 2. Unlink it */
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds 	*ifap = ifa1->ifa_next;
386fd23c3b3SDavid S. Miller 	inet_hash_remove(ifa1);
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	/* 3. Announce address deletion */
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	/* Send message first, then call notifier.
3911da177e4SLinus Torvalds 	   At first sight, FIB update triggered by notifier
3921da177e4SLinus Torvalds 	   will refer to already deleted ifaddr, that could confuse
3931da177e4SLinus Torvalds 	   netlink listeners. It is not true: look, gated sees
3941da177e4SLinus Torvalds 	   that route deleted and if it still thinks that ifaddr
3951da177e4SLinus Torvalds 	   is valid, it will try to restore deleted routes... Grr.
3961da177e4SLinus Torvalds 	   So that, this order is correct.
3971da177e4SLinus Torvalds 	 */
39815e47304SEric W. Biederman 	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
399e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
4000ff60a45SJamal Hadi Salim 
4010ff60a45SJamal Hadi Salim 	if (promote) {
40204024b93SJulian Anastasov 		struct in_ifaddr *next_sec = promote->ifa_next;
4030ff60a45SJamal Hadi Salim 
4040ff60a45SJamal Hadi Salim 		if (prev_prom) {
4050ff60a45SJamal Hadi Salim 			prev_prom->ifa_next = promote->ifa_next;
4060ff60a45SJamal Hadi Salim 			promote->ifa_next = last_prim->ifa_next;
4070ff60a45SJamal Hadi Salim 			last_prim->ifa_next = promote;
4080ff60a45SJamal Hadi Salim 		}
4090ff60a45SJamal Hadi Salim 
4100ff60a45SJamal Hadi Salim 		promote->ifa_flags &= ~IFA_F_SECONDARY;
41115e47304SEric W. Biederman 		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
412e041c683SAlan Stern 		blocking_notifier_call_chain(&inetaddr_chain,
413e041c683SAlan Stern 				NETDEV_UP, promote);
41404024b93SJulian Anastasov 		for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
4150ff60a45SJamal Hadi Salim 			if (ifa1->ifa_mask != ifa->ifa_mask ||
4160ff60a45SJamal Hadi Salim 			    !inet_ifa_match(ifa1->ifa_address, ifa))
4170ff60a45SJamal Hadi Salim 					continue;
4180ff60a45SJamal Hadi Salim 			fib_add_ifaddr(ifa);
4190ff60a45SJamal Hadi Salim 		}
4200ff60a45SJamal Hadi Salim 
4210ff60a45SJamal Hadi Salim 	}
4226363097cSHerbert Xu 	if (destroy)
4231da177e4SLinus Torvalds 		inet_free_ifa(ifa1);
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds 
426d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
427d6062cbbSThomas Graf 			 int destroy)
428d6062cbbSThomas Graf {
429d6062cbbSThomas Graf 	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
430d6062cbbSThomas Graf }
431d6062cbbSThomas Graf 
4325c766d64SJiri Pirko static void check_lifetime(struct work_struct *work);
4335c766d64SJiri Pirko 
4345c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
4355c766d64SJiri Pirko 
436d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
43715e47304SEric W. Biederman 			     u32 portid)
4381da177e4SLinus Torvalds {
4391da177e4SLinus Torvalds 	struct in_device *in_dev = ifa->ifa_dev;
4401da177e4SLinus Torvalds 	struct in_ifaddr *ifa1, **ifap, **last_primary;
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	ASSERT_RTNL();
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	if (!ifa->ifa_local) {
4451da177e4SLinus Torvalds 		inet_free_ifa(ifa);
4461da177e4SLinus Torvalds 		return 0;
4471da177e4SLinus Torvalds 	}
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	ifa->ifa_flags &= ~IFA_F_SECONDARY;
4501da177e4SLinus Torvalds 	last_primary = &in_dev->ifa_list;
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
4531da177e4SLinus Torvalds 	     ifap = &ifa1->ifa_next) {
4541da177e4SLinus Torvalds 		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
4551da177e4SLinus Torvalds 		    ifa->ifa_scope <= ifa1->ifa_scope)
4561da177e4SLinus Torvalds 			last_primary = &ifa1->ifa_next;
4571da177e4SLinus Torvalds 		if (ifa1->ifa_mask == ifa->ifa_mask &&
4581da177e4SLinus Torvalds 		    inet_ifa_match(ifa1->ifa_address, ifa)) {
4591da177e4SLinus Torvalds 			if (ifa1->ifa_local == ifa->ifa_local) {
4601da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4611da177e4SLinus Torvalds 				return -EEXIST;
4621da177e4SLinus Torvalds 			}
4631da177e4SLinus Torvalds 			if (ifa1->ifa_scope != ifa->ifa_scope) {
4641da177e4SLinus Torvalds 				inet_free_ifa(ifa);
4651da177e4SLinus Torvalds 				return -EINVAL;
4661da177e4SLinus Torvalds 			}
4671da177e4SLinus Torvalds 			ifa->ifa_flags |= IFA_F_SECONDARY;
4681da177e4SLinus Torvalds 		}
4691da177e4SLinus Torvalds 	}
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 	if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
47263862b5bSAruna-Hewapathirane 		prandom_seed((__force u32) ifa->ifa_local);
4731da177e4SLinus Torvalds 		ifap = last_primary;
4741da177e4SLinus Torvalds 	}
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 	ifa->ifa_next = *ifap;
4771da177e4SLinus Torvalds 	*ifap = ifa;
4781da177e4SLinus Torvalds 
479fd23c3b3SDavid S. Miller 	inet_hash_insert(dev_net(in_dev->dev), ifa);
480fd23c3b3SDavid S. Miller 
4815c766d64SJiri Pirko 	cancel_delayed_work(&check_lifetime_work);
482906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
4835c766d64SJiri Pirko 
4841da177e4SLinus Torvalds 	/* Send message first, then call notifier.
4851da177e4SLinus Torvalds 	   Notifier will trigger FIB update, so that
4861da177e4SLinus Torvalds 	   listeners of netlink will know about new ifaddr */
48715e47304SEric W. Biederman 	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
488e041c683SAlan Stern 	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	return 0;
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds 
493d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa)
494d6062cbbSThomas Graf {
495d6062cbbSThomas Graf 	return __inet_insert_ifa(ifa, NULL, 0);
496d6062cbbSThomas Graf }
497d6062cbbSThomas Graf 
4981da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
4991da177e4SLinus Torvalds {
500e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	ASSERT_RTNL();
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds 	if (!in_dev) {
5051da177e4SLinus Torvalds 		inet_free_ifa(ifa);
5061da177e4SLinus Torvalds 		return -ENOBUFS;
5071da177e4SLinus Torvalds 	}
50871e27da9SHerbert Xu 	ipv4_devconf_setall(in_dev);
5091d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
5101da177e4SLinus Torvalds 	if (ifa->ifa_dev != in_dev) {
511547b792cSIlpo Järvinen 		WARN_ON(ifa->ifa_dev);
5121da177e4SLinus Torvalds 		in_dev_hold(in_dev);
5131da177e4SLinus Torvalds 		ifa->ifa_dev = in_dev;
5141da177e4SLinus Torvalds 	}
515f97c1e0cSJoe Perches 	if (ipv4_is_loopback(ifa->ifa_local))
5161da177e4SLinus Torvalds 		ifa->ifa_scope = RT_SCOPE_HOST;
5171da177e4SLinus Torvalds 	return inet_insert_ifa(ifa);
5181da177e4SLinus Torvalds }
5191da177e4SLinus Torvalds 
5208723e1b4SEric Dumazet /* Caller must hold RCU or RTNL :
5218723e1b4SEric Dumazet  * We dont take a reference on found in_device
5228723e1b4SEric Dumazet  */
5237fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex)
5241da177e4SLinus Torvalds {
5251da177e4SLinus Torvalds 	struct net_device *dev;
5261da177e4SLinus Torvalds 	struct in_device *in_dev = NULL;
527c148fc2eSEric Dumazet 
528c148fc2eSEric Dumazet 	rcu_read_lock();
529c148fc2eSEric Dumazet 	dev = dev_get_by_index_rcu(net, ifindex);
5301da177e4SLinus Torvalds 	if (dev)
5318723e1b4SEric Dumazet 		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
532c148fc2eSEric Dumazet 	rcu_read_unlock();
5331da177e4SLinus Torvalds 	return in_dev;
5341da177e4SLinus Torvalds }
5359f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index);
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */
5381da177e4SLinus Torvalds 
53960cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
54060cad5daSAl Viro 				    __be32 mask)
5411da177e4SLinus Torvalds {
5421da177e4SLinus Torvalds 	ASSERT_RTNL();
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
5451da177e4SLinus Torvalds 		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
5461da177e4SLinus Torvalds 			return ifa;
5471da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
5481da177e4SLinus Torvalds 	return NULL;
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds 
55193a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
55293a714d6SMadhu Challa {
55393a714d6SMadhu Challa 	struct ip_mreqn mreq = {
55493a714d6SMadhu Challa 		.imr_multiaddr.s_addr = ifa->ifa_address,
55593a714d6SMadhu Challa 		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
55693a714d6SMadhu Challa 	};
55793a714d6SMadhu Challa 	int ret;
55893a714d6SMadhu Challa 
55993a714d6SMadhu Challa 	ASSERT_RTNL();
56093a714d6SMadhu Challa 
56193a714d6SMadhu Challa 	lock_sock(sk);
56293a714d6SMadhu Challa 	if (join)
56354ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_join_group(sk, &mreq);
56493a714d6SMadhu Challa 	else
56554ff9ef3SMarcelo Ricardo Leitner 		ret = ip_mc_leave_group(sk, &mreq);
56693a714d6SMadhu Challa 	release_sock(sk);
56793a714d6SMadhu Challa 
56893a714d6SMadhu Challa 	return ret;
56993a714d6SMadhu Challa }
57093a714d6SMadhu Challa 
571661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
5721da177e4SLinus Torvalds {
5733b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
574dfdd5fd4SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
5751da177e4SLinus Torvalds 	struct in_device *in_dev;
576dfdd5fd4SThomas Graf 	struct ifaddrmsg *ifm;
5771da177e4SLinus Torvalds 	struct in_ifaddr *ifa, **ifap;
578dfdd5fd4SThomas Graf 	int err = -EINVAL;
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 	ASSERT_RTNL();
5811da177e4SLinus Torvalds 
582dfdd5fd4SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
583dfdd5fd4SThomas Graf 	if (err < 0)
584dfdd5fd4SThomas Graf 		goto errout;
585dfdd5fd4SThomas Graf 
586dfdd5fd4SThomas Graf 	ifm = nlmsg_data(nlh);
5877fee0ca2SDenis V. Lunev 	in_dev = inetdev_by_index(net, ifm->ifa_index);
58851456b29SIan Morris 	if (!in_dev) {
589dfdd5fd4SThomas Graf 		err = -ENODEV;
590dfdd5fd4SThomas Graf 		goto errout;
591dfdd5fd4SThomas Graf 	}
592dfdd5fd4SThomas Graf 
5931da177e4SLinus Torvalds 	for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
5941da177e4SLinus Torvalds 	     ifap = &ifa->ifa_next) {
595dfdd5fd4SThomas Graf 		if (tb[IFA_LOCAL] &&
59667b61f6cSJiri Benc 		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
5971da177e4SLinus Torvalds 			continue;
598dfdd5fd4SThomas Graf 
599dfdd5fd4SThomas Graf 		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
600dfdd5fd4SThomas Graf 			continue;
601dfdd5fd4SThomas Graf 
602dfdd5fd4SThomas Graf 		if (tb[IFA_ADDRESS] &&
603dfdd5fd4SThomas Graf 		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
60467b61f6cSJiri Benc 		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
605dfdd5fd4SThomas Graf 			continue;
606dfdd5fd4SThomas Graf 
60793a714d6SMadhu Challa 		if (ipv4_is_multicast(ifa->ifa_address))
60893a714d6SMadhu Challa 			ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
60915e47304SEric W. Biederman 		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
6101da177e4SLinus Torvalds 		return 0;
6111da177e4SLinus Torvalds 	}
612dfdd5fd4SThomas Graf 
613dfdd5fd4SThomas Graf 	err = -EADDRNOTAVAIL;
614dfdd5fd4SThomas Graf errout:
615dfdd5fd4SThomas Graf 	return err;
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds 
6185c766d64SJiri Pirko #define INFINITY_LIFE_TIME	0xFFFFFFFF
6195c766d64SJiri Pirko 
6205c766d64SJiri Pirko static void check_lifetime(struct work_struct *work)
6215c766d64SJiri Pirko {
6225c766d64SJiri Pirko 	unsigned long now, next, next_sec, next_sched;
6235c766d64SJiri Pirko 	struct in_ifaddr *ifa;
624c988d1e8SJiri Pirko 	struct hlist_node *n;
6255c766d64SJiri Pirko 	int i;
6265c766d64SJiri Pirko 
6275c766d64SJiri Pirko 	now = jiffies;
6285c766d64SJiri Pirko 	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
6295c766d64SJiri Pirko 
6305c766d64SJiri Pirko 	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
631c988d1e8SJiri Pirko 		bool change_needed = false;
632c988d1e8SJiri Pirko 
633c988d1e8SJiri Pirko 		rcu_read_lock();
634b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
6355c766d64SJiri Pirko 			unsigned long age;
6365c766d64SJiri Pirko 
6375c766d64SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
6385c766d64SJiri Pirko 				continue;
6395c766d64SJiri Pirko 
6405c766d64SJiri Pirko 			/* We try to batch several events at once. */
6415c766d64SJiri Pirko 			age = (now - ifa->ifa_tstamp +
6425c766d64SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
6435c766d64SJiri Pirko 
6445c766d64SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
6455c766d64SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
646c988d1e8SJiri Pirko 				change_needed = true;
647c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft ==
648c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME) {
649c988d1e8SJiri Pirko 				continue;
650c988d1e8SJiri Pirko 			} else if (age >= ifa->ifa_preferred_lft) {
651c988d1e8SJiri Pirko 				if (time_before(ifa->ifa_tstamp +
652c988d1e8SJiri Pirko 						ifa->ifa_valid_lft * HZ, next))
653c988d1e8SJiri Pirko 					next = ifa->ifa_tstamp +
654c988d1e8SJiri Pirko 					       ifa->ifa_valid_lft * HZ;
655c988d1e8SJiri Pirko 
656c988d1e8SJiri Pirko 				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
657c988d1e8SJiri Pirko 					change_needed = true;
658c988d1e8SJiri Pirko 			} else if (time_before(ifa->ifa_tstamp +
659c988d1e8SJiri Pirko 					       ifa->ifa_preferred_lft * HZ,
660c988d1e8SJiri Pirko 					       next)) {
661c988d1e8SJiri Pirko 				next = ifa->ifa_tstamp +
662c988d1e8SJiri Pirko 				       ifa->ifa_preferred_lft * HZ;
663c988d1e8SJiri Pirko 			}
664c988d1e8SJiri Pirko 		}
665c988d1e8SJiri Pirko 		rcu_read_unlock();
666c988d1e8SJiri Pirko 		if (!change_needed)
667c988d1e8SJiri Pirko 			continue;
668c988d1e8SJiri Pirko 		rtnl_lock();
669c988d1e8SJiri Pirko 		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
670c988d1e8SJiri Pirko 			unsigned long age;
671c988d1e8SJiri Pirko 
672c988d1e8SJiri Pirko 			if (ifa->ifa_flags & IFA_F_PERMANENT)
673c988d1e8SJiri Pirko 				continue;
674c988d1e8SJiri Pirko 
675c988d1e8SJiri Pirko 			/* We try to batch several events at once. */
676c988d1e8SJiri Pirko 			age = (now - ifa->ifa_tstamp +
677c988d1e8SJiri Pirko 			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
678c988d1e8SJiri Pirko 
679c988d1e8SJiri Pirko 			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
680c988d1e8SJiri Pirko 			    age >= ifa->ifa_valid_lft) {
6815c766d64SJiri Pirko 				struct in_ifaddr **ifap;
6825c766d64SJiri Pirko 
6835c766d64SJiri Pirko 				for (ifap = &ifa->ifa_dev->ifa_list;
684c988d1e8SJiri Pirko 				     *ifap != NULL; ifap = &(*ifap)->ifa_next) {
685c988d1e8SJiri Pirko 					if (*ifap == ifa) {
6865c766d64SJiri Pirko 						inet_del_ifa(ifa->ifa_dev,
6875c766d64SJiri Pirko 							     ifap, 1);
688c988d1e8SJiri Pirko 						break;
6895c766d64SJiri Pirko 					}
690c988d1e8SJiri Pirko 				}
691c988d1e8SJiri Pirko 			} else if (ifa->ifa_preferred_lft !=
692c988d1e8SJiri Pirko 				   INFINITY_LIFE_TIME &&
693c988d1e8SJiri Pirko 				   age >= ifa->ifa_preferred_lft &&
694c988d1e8SJiri Pirko 				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
6955c766d64SJiri Pirko 				ifa->ifa_flags |= IFA_F_DEPRECATED;
6965c766d64SJiri Pirko 				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
6975c766d64SJiri Pirko 			}
6985c766d64SJiri Pirko 		}
699c988d1e8SJiri Pirko 		rtnl_unlock();
7005c766d64SJiri Pirko 	}
7015c766d64SJiri Pirko 
7025c766d64SJiri Pirko 	next_sec = round_jiffies_up(next);
7035c766d64SJiri Pirko 	next_sched = next;
7045c766d64SJiri Pirko 
7055c766d64SJiri Pirko 	/* If rounded timeout is accurate enough, accept it. */
7065c766d64SJiri Pirko 	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
7075c766d64SJiri Pirko 		next_sched = next_sec;
7085c766d64SJiri Pirko 
7095c766d64SJiri Pirko 	now = jiffies;
7105c766d64SJiri Pirko 	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
7115c766d64SJiri Pirko 	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
7125c766d64SJiri Pirko 		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
7135c766d64SJiri Pirko 
714906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
715906e073fSviresh kumar 			next_sched - now);
7165c766d64SJiri Pirko }
7175c766d64SJiri Pirko 
7185c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
7195c766d64SJiri Pirko 			     __u32 prefered_lft)
7205c766d64SJiri Pirko {
7215c766d64SJiri Pirko 	unsigned long timeout;
7225c766d64SJiri Pirko 
7235c766d64SJiri Pirko 	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
7245c766d64SJiri Pirko 
7255c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(valid_lft, HZ);
7265c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout))
7275c766d64SJiri Pirko 		ifa->ifa_valid_lft = timeout;
7285c766d64SJiri Pirko 	else
7295c766d64SJiri Pirko 		ifa->ifa_flags |= IFA_F_PERMANENT;
7305c766d64SJiri Pirko 
7315c766d64SJiri Pirko 	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
7325c766d64SJiri Pirko 	if (addrconf_finite_timeout(timeout)) {
7335c766d64SJiri Pirko 		if (timeout == 0)
7345c766d64SJiri Pirko 			ifa->ifa_flags |= IFA_F_DEPRECATED;
7355c766d64SJiri Pirko 		ifa->ifa_preferred_lft = timeout;
7365c766d64SJiri Pirko 	}
7375c766d64SJiri Pirko 	ifa->ifa_tstamp = jiffies;
7385c766d64SJiri Pirko 	if (!ifa->ifa_cstamp)
7395c766d64SJiri Pirko 		ifa->ifa_cstamp = ifa->ifa_tstamp;
7405c766d64SJiri Pirko }
7415c766d64SJiri Pirko 
7425c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
7435c766d64SJiri Pirko 				       __u32 *pvalid_lft, __u32 *pprefered_lft)
7441da177e4SLinus Torvalds {
7455c753978SThomas Graf 	struct nlattr *tb[IFA_MAX+1];
7465c753978SThomas Graf 	struct in_ifaddr *ifa;
7475c753978SThomas Graf 	struct ifaddrmsg *ifm;
7481da177e4SLinus Torvalds 	struct net_device *dev;
7491da177e4SLinus Torvalds 	struct in_device *in_dev;
7507b218574SDenis V. Lunev 	int err;
7511da177e4SLinus Torvalds 
7525c753978SThomas Graf 	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
7535c753978SThomas Graf 	if (err < 0)
7545c753978SThomas Graf 		goto errout;
7551da177e4SLinus Torvalds 
7565c753978SThomas Graf 	ifm = nlmsg_data(nlh);
757c4e38f41SEvgeniy Polyakov 	err = -EINVAL;
75851456b29SIan Morris 	if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
7595c753978SThomas Graf 		goto errout;
7601da177e4SLinus Torvalds 
7614b8aa9abSDenis V. Lunev 	dev = __dev_get_by_index(net, ifm->ifa_index);
7625c753978SThomas Graf 	err = -ENODEV;
76351456b29SIan Morris 	if (!dev)
7645c753978SThomas Graf 		goto errout;
7651da177e4SLinus Torvalds 
7665c753978SThomas Graf 	in_dev = __in_dev_get_rtnl(dev);
7675c753978SThomas Graf 	err = -ENOBUFS;
76851456b29SIan Morris 	if (!in_dev)
7695c753978SThomas Graf 		goto errout;
77071e27da9SHerbert Xu 
7715c753978SThomas Graf 	ifa = inet_alloc_ifa();
77251456b29SIan Morris 	if (!ifa)
7735c753978SThomas Graf 		/*
7745c753978SThomas Graf 		 * A potential indev allocation can be left alive, it stays
7755c753978SThomas Graf 		 * assigned to its device and is destroy with it.
7765c753978SThomas Graf 		 */
7775c753978SThomas Graf 		goto errout;
7785c753978SThomas Graf 
779a4e65d36SPavel Emelyanov 	ipv4_devconf_setall(in_dev);
7801d4c8c29SJiri Pirko 	neigh_parms_data_state_setall(in_dev->arp_parms);
7815c753978SThomas Graf 	in_dev_hold(in_dev);
7825c753978SThomas Graf 
78351456b29SIan Morris 	if (!tb[IFA_ADDRESS])
7845c753978SThomas Graf 		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
7855c753978SThomas Graf 
786fd23c3b3SDavid S. Miller 	INIT_HLIST_NODE(&ifa->hash);
7871da177e4SLinus Torvalds 	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
7881da177e4SLinus Torvalds 	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
789ad6c8135SJiri Pirko 	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
790ad6c8135SJiri Pirko 					 ifm->ifa_flags;
7911da177e4SLinus Torvalds 	ifa->ifa_scope = ifm->ifa_scope;
7921da177e4SLinus Torvalds 	ifa->ifa_dev = in_dev;
7935c753978SThomas Graf 
79467b61f6cSJiri Benc 	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
79567b61f6cSJiri Benc 	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
7965c753978SThomas Graf 
7975c753978SThomas Graf 	if (tb[IFA_BROADCAST])
79867b61f6cSJiri Benc 		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
7995c753978SThomas Graf 
8005c753978SThomas Graf 	if (tb[IFA_LABEL])
8015c753978SThomas Graf 		nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
8021da177e4SLinus Torvalds 	else
8031da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
8041da177e4SLinus Torvalds 
8055c766d64SJiri Pirko 	if (tb[IFA_CACHEINFO]) {
8065c766d64SJiri Pirko 		struct ifa_cacheinfo *ci;
8075c766d64SJiri Pirko 
8085c766d64SJiri Pirko 		ci = nla_data(tb[IFA_CACHEINFO]);
8095c766d64SJiri Pirko 		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
8105c766d64SJiri Pirko 			err = -EINVAL;
811446266b0SDaniel Borkmann 			goto errout_free;
8125c766d64SJiri Pirko 		}
8135c766d64SJiri Pirko 		*pvalid_lft = ci->ifa_valid;
8145c766d64SJiri Pirko 		*pprefered_lft = ci->ifa_prefered;
8155c766d64SJiri Pirko 	}
8165c766d64SJiri Pirko 
8175c753978SThomas Graf 	return ifa;
8185c753978SThomas Graf 
819446266b0SDaniel Borkmann errout_free:
820446266b0SDaniel Borkmann 	inet_free_ifa(ifa);
8215c753978SThomas Graf errout:
8225c753978SThomas Graf 	return ERR_PTR(err);
8235c753978SThomas Graf }
8245c753978SThomas Graf 
8255c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
8265c766d64SJiri Pirko {
8275c766d64SJiri Pirko 	struct in_device *in_dev = ifa->ifa_dev;
8285c766d64SJiri Pirko 	struct in_ifaddr *ifa1, **ifap;
8295c766d64SJiri Pirko 
8305c766d64SJiri Pirko 	if (!ifa->ifa_local)
8315c766d64SJiri Pirko 		return NULL;
8325c766d64SJiri Pirko 
8335c766d64SJiri Pirko 	for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
8345c766d64SJiri Pirko 	     ifap = &ifa1->ifa_next) {
8355c766d64SJiri Pirko 		if (ifa1->ifa_mask == ifa->ifa_mask &&
8365c766d64SJiri Pirko 		    inet_ifa_match(ifa1->ifa_address, ifa) &&
8375c766d64SJiri Pirko 		    ifa1->ifa_local == ifa->ifa_local)
8385c766d64SJiri Pirko 			return ifa1;
8395c766d64SJiri Pirko 	}
8405c766d64SJiri Pirko 	return NULL;
8415c766d64SJiri Pirko }
8425c766d64SJiri Pirko 
843661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
8445c753978SThomas Graf {
8453b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
8465c753978SThomas Graf 	struct in_ifaddr *ifa;
8475c766d64SJiri Pirko 	struct in_ifaddr *ifa_existing;
8485c766d64SJiri Pirko 	__u32 valid_lft = INFINITY_LIFE_TIME;
8495c766d64SJiri Pirko 	__u32 prefered_lft = INFINITY_LIFE_TIME;
8505c753978SThomas Graf 
8515c753978SThomas Graf 	ASSERT_RTNL();
8525c753978SThomas Graf 
8535c766d64SJiri Pirko 	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft);
8545c753978SThomas Graf 	if (IS_ERR(ifa))
8555c753978SThomas Graf 		return PTR_ERR(ifa);
8565c753978SThomas Graf 
8575c766d64SJiri Pirko 	ifa_existing = find_matching_ifa(ifa);
8585c766d64SJiri Pirko 	if (!ifa_existing) {
8595c766d64SJiri Pirko 		/* It would be best to check for !NLM_F_CREATE here but
860614d056cSstephen hemminger 		 * userspace already relies on not having to provide this.
8615c766d64SJiri Pirko 		 */
8625c766d64SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
86393a714d6SMadhu Challa 		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
86493a714d6SMadhu Challa 			int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
86593a714d6SMadhu Challa 					       true, ifa);
86693a714d6SMadhu Challa 
86793a714d6SMadhu Challa 			if (ret < 0) {
86893a714d6SMadhu Challa 				inet_free_ifa(ifa);
86993a714d6SMadhu Challa 				return ret;
87093a714d6SMadhu Challa 			}
87193a714d6SMadhu Challa 		}
87215e47304SEric W. Biederman 		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
8735c766d64SJiri Pirko 	} else {
8745c766d64SJiri Pirko 		inet_free_ifa(ifa);
8755c766d64SJiri Pirko 
8765c766d64SJiri Pirko 		if (nlh->nlmsg_flags & NLM_F_EXCL ||
8775c766d64SJiri Pirko 		    !(nlh->nlmsg_flags & NLM_F_REPLACE))
8785c766d64SJiri Pirko 			return -EEXIST;
87934e2ed34SJiri Pirko 		ifa = ifa_existing;
88034e2ed34SJiri Pirko 		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
88105a324b9SJiri Pirko 		cancel_delayed_work(&check_lifetime_work);
882906e073fSviresh kumar 		queue_delayed_work(system_power_efficient_wq,
883906e073fSviresh kumar 				&check_lifetime_work, 0);
88434e2ed34SJiri Pirko 		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
8855c766d64SJiri Pirko 	}
8865c766d64SJiri Pirko 	return 0;
8871da177e4SLinus Torvalds }
8881da177e4SLinus Torvalds 
8891da177e4SLinus Torvalds /*
8901da177e4SLinus Torvalds  *	Determine a default network mask, based on the IP address.
8911da177e4SLinus Torvalds  */
8921da177e4SLinus Torvalds 
89340384999SEric Dumazet static int inet_abc_len(__be32 addr)
8941da177e4SLinus Torvalds {
8951da177e4SLinus Torvalds 	int rc = -1;	/* Something else, probably a multicast. */
8961da177e4SLinus Torvalds 
897f97c1e0cSJoe Perches 	if (ipv4_is_zeronet(addr))
8981da177e4SLinus Torvalds 		rc = 0;
8991da177e4SLinus Torvalds 	else {
900714e85beSAl Viro 		__u32 haddr = ntohl(addr);
9011da177e4SLinus Torvalds 
902714e85beSAl Viro 		if (IN_CLASSA(haddr))
9031da177e4SLinus Torvalds 			rc = 8;
904714e85beSAl Viro 		else if (IN_CLASSB(haddr))
9051da177e4SLinus Torvalds 			rc = 16;
906714e85beSAl Viro 		else if (IN_CLASSC(haddr))
9071da177e4SLinus Torvalds 			rc = 24;
9081da177e4SLinus Torvalds 	}
9091da177e4SLinus Torvalds 
9101da177e4SLinus Torvalds 	return rc;
9111da177e4SLinus Torvalds }
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds 
914e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
9151da177e4SLinus Torvalds {
9161da177e4SLinus Torvalds 	struct ifreq ifr;
9171da177e4SLinus Torvalds 	struct sockaddr_in sin_orig;
9181da177e4SLinus Torvalds 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
9191da177e4SLinus Torvalds 	struct in_device *in_dev;
9201da177e4SLinus Torvalds 	struct in_ifaddr **ifap = NULL;
9211da177e4SLinus Torvalds 	struct in_ifaddr *ifa = NULL;
9221da177e4SLinus Torvalds 	struct net_device *dev;
9231da177e4SLinus Torvalds 	char *colon;
9241da177e4SLinus Torvalds 	int ret = -EFAULT;
9251da177e4SLinus Torvalds 	int tryaddrmatch = 0;
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 	/*
9281da177e4SLinus Torvalds 	 *	Fetch the caller's info block into kernel space
9291da177e4SLinus Torvalds 	 */
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
9321da177e4SLinus Torvalds 		goto out;
9331da177e4SLinus Torvalds 	ifr.ifr_name[IFNAMSIZ - 1] = 0;
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds 	/* save original address for comparison */
9361da177e4SLinus Torvalds 	memcpy(&sin_orig, sin, sizeof(*sin));
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds 	colon = strchr(ifr.ifr_name, ':');
9391da177e4SLinus Torvalds 	if (colon)
9401da177e4SLinus Torvalds 		*colon = 0;
9411da177e4SLinus Torvalds 
942e5b13cb1SDenis V. Lunev 	dev_load(net, ifr.ifr_name);
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 	switch (cmd) {
9451da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
9461da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
9471da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
9481da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
9491da177e4SLinus Torvalds 		/* Note that these ioctls will not sleep,
9501da177e4SLinus Torvalds 		   so that we do not impose a lock.
9511da177e4SLinus Torvalds 		   One day we will be forced to put shlock here (I mean SMP)
9521da177e4SLinus Torvalds 		 */
9531da177e4SLinus Torvalds 		tryaddrmatch = (sin_orig.sin_family == AF_INET);
9541da177e4SLinus Torvalds 		memset(sin, 0, sizeof(*sin));
9551da177e4SLinus Torvalds 		sin->sin_family = AF_INET;
9561da177e4SLinus Torvalds 		break;
9571da177e4SLinus Torvalds 
9581da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
959bf5b30b8SZhao Hongjiang 		ret = -EPERM;
96052e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9611da177e4SLinus Torvalds 			goto out;
9621da177e4SLinus Torvalds 		break;
9631da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
9641da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
9651da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
9661da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
967bf5b30b8SZhao Hongjiang 		ret = -EPERM;
96852e804c6SEric W. Biederman 		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
9691da177e4SLinus Torvalds 			goto out;
9701da177e4SLinus Torvalds 		ret = -EINVAL;
9711da177e4SLinus Torvalds 		if (sin->sin_family != AF_INET)
9721da177e4SLinus Torvalds 			goto out;
9731da177e4SLinus Torvalds 		break;
9741da177e4SLinus Torvalds 	default:
9751da177e4SLinus Torvalds 		ret = -EINVAL;
9761da177e4SLinus Torvalds 		goto out;
9771da177e4SLinus Torvalds 	}
9781da177e4SLinus Torvalds 
9791da177e4SLinus Torvalds 	rtnl_lock();
9801da177e4SLinus Torvalds 
9811da177e4SLinus Torvalds 	ret = -ENODEV;
9829f9354b9SEric Dumazet 	dev = __dev_get_by_name(net, ifr.ifr_name);
9839f9354b9SEric Dumazet 	if (!dev)
9841da177e4SLinus Torvalds 		goto done;
9851da177e4SLinus Torvalds 
9861da177e4SLinus Torvalds 	if (colon)
9871da177e4SLinus Torvalds 		*colon = ':';
9881da177e4SLinus Torvalds 
9899f9354b9SEric Dumazet 	in_dev = __in_dev_get_rtnl(dev);
9909f9354b9SEric Dumazet 	if (in_dev) {
9911da177e4SLinus Torvalds 		if (tryaddrmatch) {
9921da177e4SLinus Torvalds 			/* Matthias Andree */
9931da177e4SLinus Torvalds 			/* compare label and address (4.4BSD style) */
9941da177e4SLinus Torvalds 			/* note: we only do this for a limited set of ioctls
9951da177e4SLinus Torvalds 			   and only if the original address family was AF_INET.
9961da177e4SLinus Torvalds 			   This is checked above. */
9971da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
9981da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next) {
9991da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
10001da177e4SLinus Torvalds 				    sin_orig.sin_addr.s_addr ==
10016c91afe1SDavid S. Miller 							ifa->ifa_local) {
10021da177e4SLinus Torvalds 					break; /* found */
10031da177e4SLinus Torvalds 				}
10041da177e4SLinus Torvalds 			}
10051da177e4SLinus Torvalds 		}
10061da177e4SLinus Torvalds 		/* we didn't get a match, maybe the application is
10071da177e4SLinus Torvalds 		   4.3BSD-style and passed in junk so we fall back to
10081da177e4SLinus Torvalds 		   comparing just the label */
10091da177e4SLinus Torvalds 		if (!ifa) {
10101da177e4SLinus Torvalds 			for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
10111da177e4SLinus Torvalds 			     ifap = &ifa->ifa_next)
10121da177e4SLinus Torvalds 				if (!strcmp(ifr.ifr_name, ifa->ifa_label))
10131da177e4SLinus Torvalds 					break;
10141da177e4SLinus Torvalds 		}
10151da177e4SLinus Torvalds 	}
10161da177e4SLinus Torvalds 
10171da177e4SLinus Torvalds 	ret = -EADDRNOTAVAIL;
10181da177e4SLinus Torvalds 	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
10191da177e4SLinus Torvalds 		goto done;
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds 	switch (cmd) {
10221da177e4SLinus Torvalds 	case SIOCGIFADDR:	/* Get interface address */
10231da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_local;
10241da177e4SLinus Torvalds 		goto rarok;
10251da177e4SLinus Torvalds 
10261da177e4SLinus Torvalds 	case SIOCGIFBRDADDR:	/* Get the broadcast address */
10271da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_broadcast;
10281da177e4SLinus Torvalds 		goto rarok;
10291da177e4SLinus Torvalds 
10301da177e4SLinus Torvalds 	case SIOCGIFDSTADDR:	/* Get the destination address */
10311da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_address;
10321da177e4SLinus Torvalds 		goto rarok;
10331da177e4SLinus Torvalds 
10341da177e4SLinus Torvalds 	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
10351da177e4SLinus Torvalds 		sin->sin_addr.s_addr = ifa->ifa_mask;
10361da177e4SLinus Torvalds 		goto rarok;
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds 	case SIOCSIFFLAGS:
10391da177e4SLinus Torvalds 		if (colon) {
10401da177e4SLinus Torvalds 			ret = -EADDRNOTAVAIL;
10411da177e4SLinus Torvalds 			if (!ifa)
10421da177e4SLinus Torvalds 				break;
10431da177e4SLinus Torvalds 			ret = 0;
10441da177e4SLinus Torvalds 			if (!(ifr.ifr_flags & IFF_UP))
10451da177e4SLinus Torvalds 				inet_del_ifa(in_dev, ifap, 1);
10461da177e4SLinus Torvalds 			break;
10471da177e4SLinus Torvalds 		}
10481da177e4SLinus Torvalds 		ret = dev_change_flags(dev, ifr.ifr_flags);
10491da177e4SLinus Torvalds 		break;
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	case SIOCSIFADDR:	/* Set interface address (and family) */
10521da177e4SLinus Torvalds 		ret = -EINVAL;
10531da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
10541da177e4SLinus Torvalds 			break;
10551da177e4SLinus Torvalds 
10561da177e4SLinus Torvalds 		if (!ifa) {
10571da177e4SLinus Torvalds 			ret = -ENOBUFS;
10589f9354b9SEric Dumazet 			ifa = inet_alloc_ifa();
10599f9354b9SEric Dumazet 			if (!ifa)
10601da177e4SLinus Torvalds 				break;
1061c7e2e1d7SXi Wang 			INIT_HLIST_NODE(&ifa->hash);
10621da177e4SLinus Torvalds 			if (colon)
10631da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
10641da177e4SLinus Torvalds 			else
10651da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
10661da177e4SLinus Torvalds 		} else {
10671da177e4SLinus Torvalds 			ret = 0;
10681da177e4SLinus Torvalds 			if (ifa->ifa_local == sin->sin_addr.s_addr)
10691da177e4SLinus Torvalds 				break;
10701da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10711da177e4SLinus Torvalds 			ifa->ifa_broadcast = 0;
1072148f9729SBjorn Mork 			ifa->ifa_scope = 0;
10731da177e4SLinus Torvalds 		}
10741da177e4SLinus Torvalds 
10751da177e4SLinus Torvalds 		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
10761da177e4SLinus Torvalds 
10771da177e4SLinus Torvalds 		if (!(dev->flags & IFF_POINTOPOINT)) {
10781da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
10791da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
10801da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
10811da177e4SLinus Torvalds 			    ifa->ifa_prefixlen < 31)
10821da177e4SLinus Torvalds 				ifa->ifa_broadcast = ifa->ifa_address |
10831da177e4SLinus Torvalds 						     ~ifa->ifa_mask;
10841da177e4SLinus Torvalds 		} else {
10851da177e4SLinus Torvalds 			ifa->ifa_prefixlen = 32;
10861da177e4SLinus Torvalds 			ifa->ifa_mask = inet_make_mask(32);
10871da177e4SLinus Torvalds 		}
10885c766d64SJiri Pirko 		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
10891da177e4SLinus Torvalds 		ret = inet_set_ifa(dev, ifa);
10901da177e4SLinus Torvalds 		break;
10911da177e4SLinus Torvalds 
10921da177e4SLinus Torvalds 	case SIOCSIFBRDADDR:	/* Set the broadcast address */
10931da177e4SLinus Torvalds 		ret = 0;
10941da177e4SLinus Torvalds 		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
10951da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
10961da177e4SLinus Torvalds 			ifa->ifa_broadcast = sin->sin_addr.s_addr;
10971da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
10981da177e4SLinus Torvalds 		}
10991da177e4SLinus Torvalds 		break;
11001da177e4SLinus Torvalds 
11011da177e4SLinus Torvalds 	case SIOCSIFDSTADDR:	/* Set the destination address */
11021da177e4SLinus Torvalds 		ret = 0;
11031da177e4SLinus Torvalds 		if (ifa->ifa_address == sin->sin_addr.s_addr)
11041da177e4SLinus Torvalds 			break;
11051da177e4SLinus Torvalds 		ret = -EINVAL;
11061da177e4SLinus Torvalds 		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
11071da177e4SLinus Torvalds 			break;
11081da177e4SLinus Torvalds 		ret = 0;
11091da177e4SLinus Torvalds 		inet_del_ifa(in_dev, ifap, 0);
11101da177e4SLinus Torvalds 		ifa->ifa_address = sin->sin_addr.s_addr;
11111da177e4SLinus Torvalds 		inet_insert_ifa(ifa);
11121da177e4SLinus Torvalds 		break;
11131da177e4SLinus Torvalds 
11141da177e4SLinus Torvalds 	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
11151da177e4SLinus Torvalds 
11161da177e4SLinus Torvalds 		/*
11171da177e4SLinus Torvalds 		 *	The mask we set must be legal.
11181da177e4SLinus Torvalds 		 */
11191da177e4SLinus Torvalds 		ret = -EINVAL;
11201da177e4SLinus Torvalds 		if (bad_mask(sin->sin_addr.s_addr, 0))
11211da177e4SLinus Torvalds 			break;
11221da177e4SLinus Torvalds 		ret = 0;
11231da177e4SLinus Torvalds 		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
1124a144ea4bSAl Viro 			__be32 old_mask = ifa->ifa_mask;
11251da177e4SLinus Torvalds 			inet_del_ifa(in_dev, ifap, 0);
11261da177e4SLinus Torvalds 			ifa->ifa_mask = sin->sin_addr.s_addr;
11271da177e4SLinus Torvalds 			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
11281da177e4SLinus Torvalds 
11291da177e4SLinus Torvalds 			/* See if current broadcast address matches
11301da177e4SLinus Torvalds 			 * with current netmask, then recalculate
11311da177e4SLinus Torvalds 			 * the broadcast address. Otherwise it's a
11321da177e4SLinus Torvalds 			 * funny address, so don't touch it since
11331da177e4SLinus Torvalds 			 * the user seems to know what (s)he's doing...
11341da177e4SLinus Torvalds 			 */
11351da177e4SLinus Torvalds 			if ((dev->flags & IFF_BROADCAST) &&
11361da177e4SLinus Torvalds 			    (ifa->ifa_prefixlen < 31) &&
11371da177e4SLinus Torvalds 			    (ifa->ifa_broadcast ==
1138dcab5e1eSDavid Engel 			     (ifa->ifa_local|~old_mask))) {
11391da177e4SLinus Torvalds 				ifa->ifa_broadcast = (ifa->ifa_local |
11401da177e4SLinus Torvalds 						      ~sin->sin_addr.s_addr);
11411da177e4SLinus Torvalds 			}
11421da177e4SLinus Torvalds 			inet_insert_ifa(ifa);
11431da177e4SLinus Torvalds 		}
11441da177e4SLinus Torvalds 		break;
11451da177e4SLinus Torvalds 	}
11461da177e4SLinus Torvalds done:
11471da177e4SLinus Torvalds 	rtnl_unlock();
11481da177e4SLinus Torvalds out:
11491da177e4SLinus Torvalds 	return ret;
11501da177e4SLinus Torvalds rarok:
11511da177e4SLinus Torvalds 	rtnl_unlock();
11521da177e4SLinus Torvalds 	ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
11531da177e4SLinus Torvalds 	goto out;
11541da177e4SLinus Torvalds }
11551da177e4SLinus Torvalds 
11561da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
11571da177e4SLinus Torvalds {
1158e5ed6399SHerbert Xu 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
11591da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
11601da177e4SLinus Torvalds 	struct ifreq ifr;
11611da177e4SLinus Torvalds 	int done = 0;
11621da177e4SLinus Torvalds 
11639f9354b9SEric Dumazet 	if (!in_dev)
11641da177e4SLinus Torvalds 		goto out;
11651da177e4SLinus Torvalds 
11669f9354b9SEric Dumazet 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
11671da177e4SLinus Torvalds 		if (!buf) {
11681da177e4SLinus Torvalds 			done += sizeof(ifr);
11691da177e4SLinus Torvalds 			continue;
11701da177e4SLinus Torvalds 		}
11711da177e4SLinus Torvalds 		if (len < (int) sizeof(ifr))
11721da177e4SLinus Torvalds 			break;
11731da177e4SLinus Torvalds 		memset(&ifr, 0, sizeof(struct ifreq));
11741da177e4SLinus Torvalds 		strcpy(ifr.ifr_name, ifa->ifa_label);
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
11771da177e4SLinus Torvalds 		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
11781da177e4SLinus Torvalds 								ifa->ifa_local;
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds 		if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
11811da177e4SLinus Torvalds 			done = -EFAULT;
11821da177e4SLinus Torvalds 			break;
11831da177e4SLinus Torvalds 		}
11841da177e4SLinus Torvalds 		buf  += sizeof(struct ifreq);
11851da177e4SLinus Torvalds 		len  -= sizeof(struct ifreq);
11861da177e4SLinus Torvalds 		done += sizeof(struct ifreq);
11871da177e4SLinus Torvalds 	}
11881da177e4SLinus Torvalds out:
11891da177e4SLinus Torvalds 	return done;
11901da177e4SLinus Torvalds }
11911da177e4SLinus Torvalds 
1192a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
11931da177e4SLinus Torvalds {
1194a61ced5dSAl Viro 	__be32 addr = 0;
11951da177e4SLinus Torvalds 	struct in_device *in_dev;
1196c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
11971da177e4SLinus Torvalds 
11981da177e4SLinus Torvalds 	rcu_read_lock();
1199e5ed6399SHerbert Xu 	in_dev = __in_dev_get_rcu(dev);
12001da177e4SLinus Torvalds 	if (!in_dev)
12011da177e4SLinus Torvalds 		goto no_in_dev;
12021da177e4SLinus Torvalds 
12031da177e4SLinus Torvalds 	for_primary_ifa(in_dev) {
12041da177e4SLinus Torvalds 		if (ifa->ifa_scope > scope)
12051da177e4SLinus Torvalds 			continue;
12061da177e4SLinus Torvalds 		if (!dst || inet_ifa_match(dst, ifa)) {
12071da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12081da177e4SLinus Torvalds 			break;
12091da177e4SLinus Torvalds 		}
12101da177e4SLinus Torvalds 		if (!addr)
12111da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12121da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 	if (addr)
1215c6d14c84SEric Dumazet 		goto out_unlock;
12169f9354b9SEric Dumazet no_in_dev:
12171da177e4SLinus Torvalds 
12181da177e4SLinus Torvalds 	/* Not loopback addresses on loopback should be preferred
1219ca9f1fd2SStephen Hemminger 	   in this case. It is important that lo is the first interface
12201da177e4SLinus Torvalds 	   in dev_base list.
12211da177e4SLinus Torvalds 	 */
1222c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
12239f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
12249f9354b9SEric Dumazet 		if (!in_dev)
12251da177e4SLinus Torvalds 			continue;
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds 		for_primary_ifa(in_dev) {
12281da177e4SLinus Torvalds 			if (ifa->ifa_scope != RT_SCOPE_LINK &&
12291da177e4SLinus Torvalds 			    ifa->ifa_scope <= scope) {
12301da177e4SLinus Torvalds 				addr = ifa->ifa_local;
1231c6d14c84SEric Dumazet 				goto out_unlock;
12321da177e4SLinus Torvalds 			}
12331da177e4SLinus Torvalds 		} endfor_ifa(in_dev);
12341da177e4SLinus Torvalds 	}
1235c6d14c84SEric Dumazet out_unlock:
12361da177e4SLinus Torvalds 	rcu_read_unlock();
12371da177e4SLinus Torvalds 	return addr;
12381da177e4SLinus Torvalds }
12399f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr);
12401da177e4SLinus Torvalds 
124160cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
124260cad5daSAl Viro 			      __be32 local, int scope)
12431da177e4SLinus Torvalds {
12441da177e4SLinus Torvalds 	int same = 0;
1245a144ea4bSAl Viro 	__be32 addr = 0;
12461da177e4SLinus Torvalds 
12471da177e4SLinus Torvalds 	for_ifa(in_dev) {
12481da177e4SLinus Torvalds 		if (!addr &&
12491da177e4SLinus Torvalds 		    (local == ifa->ifa_local || !local) &&
12501da177e4SLinus Torvalds 		    ifa->ifa_scope <= scope) {
12511da177e4SLinus Torvalds 			addr = ifa->ifa_local;
12521da177e4SLinus Torvalds 			if (same)
12531da177e4SLinus Torvalds 				break;
12541da177e4SLinus Torvalds 		}
12551da177e4SLinus Torvalds 		if (!same) {
12561da177e4SLinus Torvalds 			same = (!local || inet_ifa_match(local, ifa)) &&
12571da177e4SLinus Torvalds 				(!dst || inet_ifa_match(dst, ifa));
12581da177e4SLinus Torvalds 			if (same && addr) {
12591da177e4SLinus Torvalds 				if (local || !dst)
12601da177e4SLinus Torvalds 					break;
12611da177e4SLinus Torvalds 				/* Is the selected addr into dst subnet? */
12621da177e4SLinus Torvalds 				if (inet_ifa_match(addr, ifa))
12631da177e4SLinus Torvalds 					break;
12641da177e4SLinus Torvalds 				/* No, then can we use new local src? */
12651da177e4SLinus Torvalds 				if (ifa->ifa_scope <= scope) {
12661da177e4SLinus Torvalds 					addr = ifa->ifa_local;
12671da177e4SLinus Torvalds 					break;
12681da177e4SLinus Torvalds 				}
12691da177e4SLinus Torvalds 				/* search for large dst subnet for addr */
12701da177e4SLinus Torvalds 				same = 0;
12711da177e4SLinus Torvalds 			}
12721da177e4SLinus Torvalds 		}
12731da177e4SLinus Torvalds 	} endfor_ifa(in_dev);
12741da177e4SLinus Torvalds 
12751da177e4SLinus Torvalds 	return same ? addr : 0;
12761da177e4SLinus Torvalds }
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds /*
12791da177e4SLinus Torvalds  * Confirm that local IP address exists using wildcards:
1280b601fa19SNicolas Dichtel  * - net: netns to check, cannot be NULL
1281b601fa19SNicolas Dichtel  * - in_dev: only on this interface, NULL=any interface
12821da177e4SLinus Torvalds  * - dst: only in the same subnet as dst, 0=any dst
12831da177e4SLinus Torvalds  * - local: address, 0=autoselect the local address
12841da177e4SLinus Torvalds  * - scope: maximum allowed scope value for the local address
12851da177e4SLinus Torvalds  */
1286b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
12879bd85e32SDenis V. Lunev 			 __be32 dst, __be32 local, int scope)
12881da177e4SLinus Torvalds {
128960cad5daSAl Viro 	__be32 addr = 0;
12909bd85e32SDenis V. Lunev 	struct net_device *dev;
12911da177e4SLinus Torvalds 
129200db4124SIan Morris 	if (in_dev)
12939bd85e32SDenis V. Lunev 		return confirm_addr_indev(in_dev, dst, local, scope);
12941da177e4SLinus Torvalds 
12951da177e4SLinus Torvalds 	rcu_read_lock();
1296c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
12979f9354b9SEric Dumazet 		in_dev = __in_dev_get_rcu(dev);
12989f9354b9SEric Dumazet 		if (in_dev) {
12991da177e4SLinus Torvalds 			addr = confirm_addr_indev(in_dev, dst, local, scope);
13001da177e4SLinus Torvalds 			if (addr)
13011da177e4SLinus Torvalds 				break;
13021da177e4SLinus Torvalds 		}
13031da177e4SLinus Torvalds 	}
13041da177e4SLinus Torvalds 	rcu_read_unlock();
13051da177e4SLinus Torvalds 
13061da177e4SLinus Torvalds 	return addr;
13071da177e4SLinus Torvalds }
1308eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr);
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds /*
13111da177e4SLinus Torvalds  *	Device notifier
13121da177e4SLinus Torvalds  */
13131da177e4SLinus Torvalds 
13141da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb)
13151da177e4SLinus Torvalds {
1316e041c683SAlan Stern 	return blocking_notifier_chain_register(&inetaddr_chain, nb);
13171da177e4SLinus Torvalds }
13189f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier);
13191da177e4SLinus Torvalds 
13201da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb)
13211da177e4SLinus Torvalds {
1322e041c683SAlan Stern 	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
13231da177e4SLinus Torvalds }
13249f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier);
13251da177e4SLinus Torvalds 
13269f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve
13279f9354b9SEric Dumazet  * existing alias numbering and to create unique labels if possible.
13281da177e4SLinus Torvalds */
13291da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
13301da177e4SLinus Torvalds {
13311da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
13321da177e4SLinus Torvalds 	int named = 0;
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
13351da177e4SLinus Torvalds 		char old[IFNAMSIZ], *dot;
13361da177e4SLinus Torvalds 
13371da177e4SLinus Torvalds 		memcpy(old, ifa->ifa_label, IFNAMSIZ);
13381da177e4SLinus Torvalds 		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
13391da177e4SLinus Torvalds 		if (named++ == 0)
1340573bf470SThomas Graf 			goto skip;
134144344b2aSMark McLoughlin 		dot = strchr(old, ':');
134251456b29SIan Morris 		if (!dot) {
13431da177e4SLinus Torvalds 			sprintf(old, ":%d", named);
13441da177e4SLinus Torvalds 			dot = old;
13451da177e4SLinus Torvalds 		}
13469f9354b9SEric Dumazet 		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
13471da177e4SLinus Torvalds 			strcat(ifa->ifa_label, dot);
13489f9354b9SEric Dumazet 		else
13491da177e4SLinus Torvalds 			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
1350573bf470SThomas Graf skip:
1351573bf470SThomas Graf 		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
13521da177e4SLinus Torvalds 	}
13531da177e4SLinus Torvalds }
13541da177e4SLinus Torvalds 
135540384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu)
135606770843SBreno Leitao {
135706770843SBreno Leitao 	return mtu >= 68;
135806770843SBreno Leitao }
135906770843SBreno Leitao 
1360d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev,
1361d11327adSIan Campbell 					struct in_device *in_dev)
1362d11327adSIan Campbell 
1363d11327adSIan Campbell {
1364b76d0789SZoltan Kiss 	struct in_ifaddr *ifa;
1365d11327adSIan Campbell 
1366b76d0789SZoltan Kiss 	for (ifa = in_dev->ifa_list; ifa;
1367b76d0789SZoltan Kiss 	     ifa = ifa->ifa_next) {
1368d11327adSIan Campbell 		arp_send(ARPOP_REQUEST, ETH_P_ARP,
13696c91afe1SDavid S. Miller 			 ifa->ifa_local, dev,
13706c91afe1SDavid S. Miller 			 ifa->ifa_local, NULL,
1371d11327adSIan Campbell 			 dev->dev_addr, NULL);
1372d11327adSIan Campbell 	}
1373b76d0789SZoltan Kiss }
1374d11327adSIan Campbell 
13751da177e4SLinus Torvalds /* Called only under RTNL semaphore */
13761da177e4SLinus Torvalds 
13771da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event,
13781da177e4SLinus Torvalds 			 void *ptr)
13791da177e4SLinus Torvalds {
1380351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1381748e2d93SEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
13821da177e4SLinus Torvalds 
13831da177e4SLinus Torvalds 	ASSERT_RTNL();
13841da177e4SLinus Torvalds 
13851da177e4SLinus Torvalds 	if (!in_dev) {
13868030f544SHerbert Xu 		if (event == NETDEV_REGISTER) {
13871da177e4SLinus Torvalds 			in_dev = inetdev_init(dev);
138820e61da7SWANG Cong 			if (IS_ERR(in_dev))
138920e61da7SWANG Cong 				return notifier_from_errno(PTR_ERR(in_dev));
13900cc217e1SEric W. Biederman 			if (dev->flags & IFF_LOOPBACK) {
139142f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
139242f811b8SHerbert Xu 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
13931da177e4SLinus Torvalds 			}
139406770843SBreno Leitao 		} else if (event == NETDEV_CHANGEMTU) {
139506770843SBreno Leitao 			/* Re-enabling IP */
139606770843SBreno Leitao 			if (inetdev_valid_mtu(dev->mtu))
139706770843SBreno Leitao 				in_dev = inetdev_init(dev);
13988030f544SHerbert Xu 		}
13991da177e4SLinus Torvalds 		goto out;
14001da177e4SLinus Torvalds 	}
14011da177e4SLinus Torvalds 
14021da177e4SLinus Torvalds 	switch (event) {
14031da177e4SLinus Torvalds 	case NETDEV_REGISTER:
140491df42beSJoe Perches 		pr_debug("%s: bug\n", __func__);
1405a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(dev->ip_ptr, NULL);
14061da177e4SLinus Torvalds 		break;
14071da177e4SLinus Torvalds 	case NETDEV_UP:
140806770843SBreno Leitao 		if (!inetdev_valid_mtu(dev->mtu))
14091da177e4SLinus Torvalds 			break;
14100cc217e1SEric W. Biederman 		if (dev->flags & IFF_LOOPBACK) {
14119f9354b9SEric Dumazet 			struct in_ifaddr *ifa = inet_alloc_ifa();
14129f9354b9SEric Dumazet 
14139f9354b9SEric Dumazet 			if (ifa) {
1414fd23c3b3SDavid S. Miller 				INIT_HLIST_NODE(&ifa->hash);
14151da177e4SLinus Torvalds 				ifa->ifa_local =
14161da177e4SLinus Torvalds 				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
14171da177e4SLinus Torvalds 				ifa->ifa_prefixlen = 8;
14181da177e4SLinus Torvalds 				ifa->ifa_mask = inet_make_mask(8);
14191da177e4SLinus Torvalds 				in_dev_hold(in_dev);
14201da177e4SLinus Torvalds 				ifa->ifa_dev = in_dev;
14211da177e4SLinus Torvalds 				ifa->ifa_scope = RT_SCOPE_HOST;
14221da177e4SLinus Torvalds 				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
14235c766d64SJiri Pirko 				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
14245c766d64SJiri Pirko 						 INFINITY_LIFE_TIME);
1425dfd1582dSJiri Pirko 				ipv4_devconf_setall(in_dev);
1426dfd1582dSJiri Pirko 				neigh_parms_data_state_setall(in_dev->arp_parms);
14271da177e4SLinus Torvalds 				inet_insert_ifa(ifa);
14281da177e4SLinus Torvalds 			}
14291da177e4SLinus Torvalds 		}
14301da177e4SLinus Torvalds 		ip_mc_up(in_dev);
1431eefef1cfSStephen Hemminger 		/* fall through */
1432eefef1cfSStephen Hemminger 	case NETDEV_CHANGEADDR:
1433d11327adSIan Campbell 		if (!IN_DEV_ARP_NOTIFY(in_dev))
1434d11327adSIan Campbell 			break;
1435d11327adSIan Campbell 		/* fall through */
1436d11327adSIan Campbell 	case NETDEV_NOTIFY_PEERS:
1437a21090cfSStephen Hemminger 		/* Send gratuitous ARP to notify of link change */
1438d11327adSIan Campbell 		inetdev_send_gratuitous_arp(dev, in_dev);
14391da177e4SLinus Torvalds 		break;
14401da177e4SLinus Torvalds 	case NETDEV_DOWN:
14411da177e4SLinus Torvalds 		ip_mc_down(in_dev);
14421da177e4SLinus Torvalds 		break;
144393d9b7d7SJiri Pirko 	case NETDEV_PRE_TYPE_CHANGE:
144475c78500SMoni Shoua 		ip_mc_unmap(in_dev);
144575c78500SMoni Shoua 		break;
144693d9b7d7SJiri Pirko 	case NETDEV_POST_TYPE_CHANGE:
144775c78500SMoni Shoua 		ip_mc_remap(in_dev);
144875c78500SMoni Shoua 		break;
14491da177e4SLinus Torvalds 	case NETDEV_CHANGEMTU:
145006770843SBreno Leitao 		if (inetdev_valid_mtu(dev->mtu))
14511da177e4SLinus Torvalds 			break;
145206770843SBreno Leitao 		/* disable IP when MTU is not enough */
14531da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
14541da177e4SLinus Torvalds 		inetdev_destroy(in_dev);
14551da177e4SLinus Torvalds 		break;
14561da177e4SLinus Torvalds 	case NETDEV_CHANGENAME:
14571da177e4SLinus Torvalds 		/* Do not notify about label change, this event is
14581da177e4SLinus Torvalds 		 * not interesting to applications using netlink.
14591da177e4SLinus Torvalds 		 */
14601da177e4SLinus Torvalds 		inetdev_changename(dev, in_dev);
14611da177e4SLinus Torvalds 
146251602b2aSPavel Emelyanov 		devinet_sysctl_unregister(in_dev);
146366f27a52SPavel Emelyanov 		devinet_sysctl_register(in_dev);
14641da177e4SLinus Torvalds 		break;
14651da177e4SLinus Torvalds 	}
14661da177e4SLinus Torvalds out:
14671da177e4SLinus Torvalds 	return NOTIFY_DONE;
14681da177e4SLinus Torvalds }
14691da177e4SLinus Torvalds 
14701da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = {
14711da177e4SLinus Torvalds 	.notifier_call = inetdev_event,
14721da177e4SLinus Torvalds };
14731da177e4SLinus Torvalds 
147440384999SEric Dumazet static size_t inet_nlmsg_size(void)
1475339bf98fSThomas Graf {
1476339bf98fSThomas Graf 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1477339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_ADDRESS */
1478339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_LOCAL */
1479339bf98fSThomas Graf 	       + nla_total_size(4) /* IFA_BROADCAST */
1480ad6c8135SJiri Pirko 	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
148163b5f152SGeert Uytterhoeven 	       + nla_total_size(4)  /* IFA_FLAGS */
148263b5f152SGeert Uytterhoeven 	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
1483339bf98fSThomas Graf }
1484339bf98fSThomas Graf 
14855c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp)
14865c766d64SJiri Pirko {
14875c766d64SJiri Pirko 	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
14885c766d64SJiri Pirko }
14895c766d64SJiri Pirko 
14905c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
14915c766d64SJiri Pirko 			 unsigned long tstamp, u32 preferred, u32 valid)
14925c766d64SJiri Pirko {
14935c766d64SJiri Pirko 	struct ifa_cacheinfo ci;
14945c766d64SJiri Pirko 
14955c766d64SJiri Pirko 	ci.cstamp = cstamp_delta(cstamp);
14965c766d64SJiri Pirko 	ci.tstamp = cstamp_delta(tstamp);
14975c766d64SJiri Pirko 	ci.ifa_prefered = preferred;
14985c766d64SJiri Pirko 	ci.ifa_valid = valid;
14995c766d64SJiri Pirko 
15005c766d64SJiri Pirko 	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
15015c766d64SJiri Pirko }
15025c766d64SJiri Pirko 
15031da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
150415e47304SEric W. Biederman 			    u32 portid, u32 seq, int event, unsigned int flags)
15051da177e4SLinus Torvalds {
15061da177e4SLinus Torvalds 	struct ifaddrmsg *ifm;
15071da177e4SLinus Torvalds 	struct nlmsghdr  *nlh;
15085c766d64SJiri Pirko 	u32 preferred, valid;
15091da177e4SLinus Torvalds 
151015e47304SEric W. Biederman 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
151151456b29SIan Morris 	if (!nlh)
151226932566SPatrick McHardy 		return -EMSGSIZE;
151347f68512SThomas Graf 
151447f68512SThomas Graf 	ifm = nlmsg_data(nlh);
15151da177e4SLinus Torvalds 	ifm->ifa_family = AF_INET;
15161da177e4SLinus Torvalds 	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
15175c766d64SJiri Pirko 	ifm->ifa_flags = ifa->ifa_flags;
15181da177e4SLinus Torvalds 	ifm->ifa_scope = ifa->ifa_scope;
15191da177e4SLinus Torvalds 	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
15201da177e4SLinus Torvalds 
15215c766d64SJiri Pirko 	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
15225c766d64SJiri Pirko 		preferred = ifa->ifa_preferred_lft;
15235c766d64SJiri Pirko 		valid = ifa->ifa_valid_lft;
15245c766d64SJiri Pirko 		if (preferred != INFINITY_LIFE_TIME) {
15255c766d64SJiri Pirko 			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
15265c766d64SJiri Pirko 
15275c766d64SJiri Pirko 			if (preferred > tval)
15285c766d64SJiri Pirko 				preferred -= tval;
15295c766d64SJiri Pirko 			else
15305c766d64SJiri Pirko 				preferred = 0;
15315c766d64SJiri Pirko 			if (valid != INFINITY_LIFE_TIME) {
15325c766d64SJiri Pirko 				if (valid > tval)
15335c766d64SJiri Pirko 					valid -= tval;
15345c766d64SJiri Pirko 				else
15355c766d64SJiri Pirko 					valid = 0;
15365c766d64SJiri Pirko 			}
15375c766d64SJiri Pirko 		}
15385c766d64SJiri Pirko 	} else {
15395c766d64SJiri Pirko 		preferred = INFINITY_LIFE_TIME;
15405c766d64SJiri Pirko 		valid = INFINITY_LIFE_TIME;
15415c766d64SJiri Pirko 	}
1542f3756b79SDavid S. Miller 	if ((ifa->ifa_address &&
1543930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1544f3756b79SDavid S. Miller 	    (ifa->ifa_local &&
1545930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
1546f3756b79SDavid S. Miller 	    (ifa->ifa_broadcast &&
1547930345eaSJiri Benc 	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1548f3756b79SDavid S. Miller 	    (ifa->ifa_label[0] &&
15495c766d64SJiri Pirko 	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
1550ad6c8135SJiri Pirko 	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
15515c766d64SJiri Pirko 	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
15525c766d64SJiri Pirko 			  preferred, valid))
1553f3756b79SDavid S. Miller 		goto nla_put_failure;
155447f68512SThomas Graf 
1555053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1556053c095aSJohannes Berg 	return 0;
155747f68512SThomas Graf 
155847f68512SThomas Graf nla_put_failure:
155926932566SPatrick McHardy 	nlmsg_cancel(skb, nlh);
156026932566SPatrick McHardy 	return -EMSGSIZE;
15611da177e4SLinus Torvalds }
15621da177e4SLinus Torvalds 
15631da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
15641da177e4SLinus Torvalds {
15653b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1566eec4df98SEric Dumazet 	int h, s_h;
1567eec4df98SEric Dumazet 	int idx, s_idx;
1568eec4df98SEric Dumazet 	int ip_idx, s_ip_idx;
15691da177e4SLinus Torvalds 	struct net_device *dev;
15701da177e4SLinus Torvalds 	struct in_device *in_dev;
15711da177e4SLinus Torvalds 	struct in_ifaddr *ifa;
1572eec4df98SEric Dumazet 	struct hlist_head *head;
15731da177e4SLinus Torvalds 
1574eec4df98SEric Dumazet 	s_h = cb->args[0];
1575eec4df98SEric Dumazet 	s_idx = idx = cb->args[1];
1576eec4df98SEric Dumazet 	s_ip_idx = ip_idx = cb->args[2];
1577eec4df98SEric Dumazet 
1578eec4df98SEric Dumazet 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
15797562f876SPavel Emelianov 		idx = 0;
1580eec4df98SEric Dumazet 		head = &net->dev_index_head[h];
1581eec4df98SEric Dumazet 		rcu_read_lock();
15820465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
15830465277fSNicolas Dichtel 			  net->dev_base_seq;
1584b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
15851da177e4SLinus Torvalds 			if (idx < s_idx)
15867562f876SPavel Emelianov 				goto cont;
15874b97efdfSPatrick McHardy 			if (h > s_h || idx > s_idx)
15881da177e4SLinus Torvalds 				s_ip_idx = 0;
1589eec4df98SEric Dumazet 			in_dev = __in_dev_get_rcu(dev);
15909f9354b9SEric Dumazet 			if (!in_dev)
15917562f876SPavel Emelianov 				goto cont;
15921da177e4SLinus Torvalds 
15931da177e4SLinus Torvalds 			for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
15941da177e4SLinus Torvalds 			     ifa = ifa->ifa_next, ip_idx++) {
15951da177e4SLinus Torvalds 				if (ip_idx < s_ip_idx)
1596596e4150SStephen Hemminger 					continue;
1597eec4df98SEric Dumazet 				if (inet_fill_ifaddr(skb, ifa,
159815e47304SEric W. Biederman 					     NETLINK_CB(cb->skb).portid,
15991da177e4SLinus Torvalds 					     cb->nlh->nlmsg_seq,
1600053c095aSJohannes Berg 					     RTM_NEWADDR, NLM_F_MULTI) < 0) {
1601eec4df98SEric Dumazet 					rcu_read_unlock();
16021da177e4SLinus Torvalds 					goto done;
16031da177e4SLinus Torvalds 				}
16040465277fSNicolas Dichtel 				nl_dump_check_consistent(cb, nlmsg_hdr(skb));
1605eec4df98SEric Dumazet 			}
16067562f876SPavel Emelianov cont:
16077562f876SPavel Emelianov 			idx++;
16081da177e4SLinus Torvalds 		}
1609eec4df98SEric Dumazet 		rcu_read_unlock();
1610eec4df98SEric Dumazet 	}
16111da177e4SLinus Torvalds 
16121da177e4SLinus Torvalds done:
1613eec4df98SEric Dumazet 	cb->args[0] = h;
1614eec4df98SEric Dumazet 	cb->args[1] = idx;
1615eec4df98SEric Dumazet 	cb->args[2] = ip_idx;
16161da177e4SLinus Torvalds 
16171da177e4SLinus Torvalds 	return skb->len;
16181da177e4SLinus Torvalds }
16191da177e4SLinus Torvalds 
1620d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
162115e47304SEric W. Biederman 		      u32 portid)
16221da177e4SLinus Torvalds {
162347f68512SThomas Graf 	struct sk_buff *skb;
1624d6062cbbSThomas Graf 	u32 seq = nlh ? nlh->nlmsg_seq : 0;
1625d6062cbbSThomas Graf 	int err = -ENOBUFS;
16264b8aa9abSDenis V. Lunev 	struct net *net;
16271da177e4SLinus Torvalds 
1628c346dca1SYOSHIFUJI Hideaki 	net = dev_net(ifa->ifa_dev->dev);
1629339bf98fSThomas Graf 	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
163051456b29SIan Morris 	if (!skb)
1631d6062cbbSThomas Graf 		goto errout;
1632d6062cbbSThomas Graf 
163315e47304SEric W. Biederman 	err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
163426932566SPatrick McHardy 	if (err < 0) {
163526932566SPatrick McHardy 		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
163626932566SPatrick McHardy 		WARN_ON(err == -EMSGSIZE);
163726932566SPatrick McHardy 		kfree_skb(skb);
163826932566SPatrick McHardy 		goto errout;
163926932566SPatrick McHardy 	}
164015e47304SEric W. Biederman 	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
16411ce85fe4SPablo Neira Ayuso 	return;
1642d6062cbbSThomas Graf errout:
1643d6062cbbSThomas Graf 	if (err < 0)
16444b8aa9abSDenis V. Lunev 		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
16451da177e4SLinus Torvalds }
16461da177e4SLinus Torvalds 
1647b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev,
1648b1974ed0SArad, Ronen 				    u32 ext_filter_mask)
16499f0f7272SThomas Graf {
16501fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16519f0f7272SThomas Graf 
16529f0f7272SThomas Graf 	if (!in_dev)
16539f0f7272SThomas Graf 		return 0;
16549f0f7272SThomas Graf 
16559f0f7272SThomas Graf 	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
16569f0f7272SThomas Graf }
16579f0f7272SThomas Graf 
1658d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
1659d5566fd7SSowmini Varadhan 			     u32 ext_filter_mask)
16609f0f7272SThomas Graf {
16611fc19affSEric Dumazet 	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
16629f0f7272SThomas Graf 	struct nlattr *nla;
16639f0f7272SThomas Graf 	int i;
16649f0f7272SThomas Graf 
16659f0f7272SThomas Graf 	if (!in_dev)
16669f0f7272SThomas Graf 		return -ENODATA;
16679f0f7272SThomas Graf 
16689f0f7272SThomas Graf 	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
166951456b29SIan Morris 	if (!nla)
16709f0f7272SThomas Graf 		return -EMSGSIZE;
16719f0f7272SThomas Graf 
16729f0f7272SThomas Graf 	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
16739f0f7272SThomas Graf 		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
16749f0f7272SThomas Graf 
16759f0f7272SThomas Graf 	return 0;
16769f0f7272SThomas Graf }
16779f0f7272SThomas Graf 
16789f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
16799f0f7272SThomas Graf 	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
16809f0f7272SThomas Graf };
16819f0f7272SThomas Graf 
1682cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev,
1683cf7afbfeSThomas Graf 				 const struct nlattr *nla)
16849f0f7272SThomas Graf {
16859f0f7272SThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
16869f0f7272SThomas Graf 	int err, rem;
16879f0f7272SThomas Graf 
1688f7fce74eSEric Dumazet 	if (dev && !__in_dev_get_rtnl(dev))
1689cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
16909f0f7272SThomas Graf 
16919f0f7272SThomas Graf 	err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
16929f0f7272SThomas Graf 	if (err < 0)
16939f0f7272SThomas Graf 		return err;
16949f0f7272SThomas Graf 
16959f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
16969f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
16979f0f7272SThomas Graf 			int cfgid = nla_type(a);
16989f0f7272SThomas Graf 
16999f0f7272SThomas Graf 			if (nla_len(a) < 4)
17009f0f7272SThomas Graf 				return -EINVAL;
17019f0f7272SThomas Graf 
17029f0f7272SThomas Graf 			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
17039f0f7272SThomas Graf 				return -EINVAL;
17049f0f7272SThomas Graf 		}
17059f0f7272SThomas Graf 	}
17069f0f7272SThomas Graf 
1707cf7afbfeSThomas Graf 	return 0;
1708cf7afbfeSThomas Graf }
1709cf7afbfeSThomas Graf 
1710cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1711cf7afbfeSThomas Graf {
1712f7fce74eSEric Dumazet 	struct in_device *in_dev = __in_dev_get_rtnl(dev);
1713cf7afbfeSThomas Graf 	struct nlattr *a, *tb[IFLA_INET_MAX+1];
1714cf7afbfeSThomas Graf 	int rem;
1715cf7afbfeSThomas Graf 
1716cf7afbfeSThomas Graf 	if (!in_dev)
1717cf7afbfeSThomas Graf 		return -EAFNOSUPPORT;
1718cf7afbfeSThomas Graf 
1719cf7afbfeSThomas Graf 	if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1720cf7afbfeSThomas Graf 		BUG();
1721cf7afbfeSThomas Graf 
17229f0f7272SThomas Graf 	if (tb[IFLA_INET_CONF]) {
17239f0f7272SThomas Graf 		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
17249f0f7272SThomas Graf 			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
17259f0f7272SThomas Graf 	}
17269f0f7272SThomas Graf 
17279f0f7272SThomas Graf 	return 0;
17289f0f7272SThomas Graf }
17299f0f7272SThomas Graf 
1730edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type)
1731edc9e748SNicolas Dichtel {
1732edc9e748SNicolas Dichtel 	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
1733edc9e748SNicolas Dichtel 		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
1734edc9e748SNicolas Dichtel 
17359e551110SNicolas Dichtel 	/* type -1 is used for ALL */
17369e551110SNicolas Dichtel 	if (type == -1 || type == NETCONFA_FORWARDING)
1737edc9e748SNicolas Dichtel 		size += nla_total_size(4);
1738cc535dfbSNicolas Dichtel 	if (type == -1 || type == NETCONFA_RP_FILTER)
1739cc535dfbSNicolas Dichtel 		size += nla_total_size(4);
1740d67b8c61SNicolas Dichtel 	if (type == -1 || type == NETCONFA_MC_FORWARDING)
1741d67b8c61SNicolas Dichtel 		size += nla_total_size(4);
174209aea5dfSstephen hemminger 	if (type == -1 || type == NETCONFA_PROXY_NEIGH)
1743f085ff1cSstephen hemminger 		size += nla_total_size(4);
1744974d7af5SAndy Gospodarek 	if (type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
1745974d7af5SAndy Gospodarek 		size += nla_total_size(4);
1746edc9e748SNicolas Dichtel 
1747edc9e748SNicolas Dichtel 	return size;
1748edc9e748SNicolas Dichtel }
1749edc9e748SNicolas Dichtel 
1750edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
1751edc9e748SNicolas Dichtel 				     struct ipv4_devconf *devconf, u32 portid,
1752edc9e748SNicolas Dichtel 				     u32 seq, int event, unsigned int flags,
1753edc9e748SNicolas Dichtel 				     int type)
1754edc9e748SNicolas Dichtel {
1755edc9e748SNicolas Dichtel 	struct nlmsghdr  *nlh;
1756edc9e748SNicolas Dichtel 	struct netconfmsg *ncm;
1757edc9e748SNicolas Dichtel 
1758edc9e748SNicolas Dichtel 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
1759edc9e748SNicolas Dichtel 			flags);
176051456b29SIan Morris 	if (!nlh)
1761edc9e748SNicolas Dichtel 		return -EMSGSIZE;
1762edc9e748SNicolas Dichtel 
1763edc9e748SNicolas Dichtel 	ncm = nlmsg_data(nlh);
1764edc9e748SNicolas Dichtel 	ncm->ncm_family = AF_INET;
1765edc9e748SNicolas Dichtel 
1766edc9e748SNicolas Dichtel 	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
1767edc9e748SNicolas Dichtel 		goto nla_put_failure;
1768edc9e748SNicolas Dichtel 
17699e551110SNicolas Dichtel 	/* type -1 is used for ALL */
17709e551110SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_FORWARDING) &&
1771edc9e748SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_FORWARDING,
1772edc9e748SNicolas Dichtel 			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
1773edc9e748SNicolas Dichtel 		goto nla_put_failure;
1774cc535dfbSNicolas Dichtel 	if ((type == -1 || type == NETCONFA_RP_FILTER) &&
1775cc535dfbSNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_RP_FILTER,
1776cc535dfbSNicolas Dichtel 			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
1777cc535dfbSNicolas Dichtel 		goto nla_put_failure;
1778d67b8c61SNicolas Dichtel 	if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
1779d67b8c61SNicolas Dichtel 	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
1780d67b8c61SNicolas Dichtel 			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
1781d67b8c61SNicolas Dichtel 		goto nla_put_failure;
178209aea5dfSstephen hemminger 	if ((type == -1 || type == NETCONFA_PROXY_NEIGH) &&
178309aea5dfSstephen hemminger 	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
1784f085ff1cSstephen hemminger 			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
1785f085ff1cSstephen hemminger 		goto nla_put_failure;
1786974d7af5SAndy Gospodarek 	if ((type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
1787974d7af5SAndy Gospodarek 	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
1788974d7af5SAndy Gospodarek 			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
1789974d7af5SAndy Gospodarek 		goto nla_put_failure;
1790edc9e748SNicolas Dichtel 
1791053c095aSJohannes Berg 	nlmsg_end(skb, nlh);
1792053c095aSJohannes Berg 	return 0;
1793edc9e748SNicolas Dichtel 
1794edc9e748SNicolas Dichtel nla_put_failure:
1795edc9e748SNicolas Dichtel 	nlmsg_cancel(skb, nlh);
1796edc9e748SNicolas Dichtel 	return -EMSGSIZE;
1797edc9e748SNicolas Dichtel }
1798edc9e748SNicolas Dichtel 
1799d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
1800edc9e748SNicolas Dichtel 				 struct ipv4_devconf *devconf)
1801edc9e748SNicolas Dichtel {
1802edc9e748SNicolas Dichtel 	struct sk_buff *skb;
1803edc9e748SNicolas Dichtel 	int err = -ENOBUFS;
1804edc9e748SNicolas Dichtel 
1805edc9e748SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
180651456b29SIan Morris 	if (!skb)
1807edc9e748SNicolas Dichtel 		goto errout;
1808edc9e748SNicolas Dichtel 
1809edc9e748SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
1810edc9e748SNicolas Dichtel 					RTM_NEWNETCONF, 0, type);
1811edc9e748SNicolas Dichtel 	if (err < 0) {
1812edc9e748SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
1813edc9e748SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
1814edc9e748SNicolas Dichtel 		kfree_skb(skb);
1815edc9e748SNicolas Dichtel 		goto errout;
1816edc9e748SNicolas Dichtel 	}
1817edc9e748SNicolas Dichtel 	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC);
1818edc9e748SNicolas Dichtel 	return;
1819edc9e748SNicolas Dichtel errout:
1820edc9e748SNicolas Dichtel 	if (err < 0)
1821edc9e748SNicolas Dichtel 		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
1822edc9e748SNicolas Dichtel }
1823edc9e748SNicolas Dichtel 
18249e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
18259e551110SNicolas Dichtel 	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
18269e551110SNicolas Dichtel 	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
1827cc535dfbSNicolas Dichtel 	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
182809aea5dfSstephen hemminger 	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
1829974d7af5SAndy Gospodarek 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
18309e551110SNicolas Dichtel };
18319e551110SNicolas Dichtel 
18329e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb,
1833661d2967SThomas Graf 				    struct nlmsghdr *nlh)
18349e551110SNicolas Dichtel {
18359e551110SNicolas Dichtel 	struct net *net = sock_net(in_skb->sk);
18369e551110SNicolas Dichtel 	struct nlattr *tb[NETCONFA_MAX+1];
18379e551110SNicolas Dichtel 	struct netconfmsg *ncm;
18389e551110SNicolas Dichtel 	struct sk_buff *skb;
18399e551110SNicolas Dichtel 	struct ipv4_devconf *devconf;
18409e551110SNicolas Dichtel 	struct in_device *in_dev;
18419e551110SNicolas Dichtel 	struct net_device *dev;
18429e551110SNicolas Dichtel 	int ifindex;
18439e551110SNicolas Dichtel 	int err;
18449e551110SNicolas Dichtel 
18459e551110SNicolas Dichtel 	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
18469e551110SNicolas Dichtel 			  devconf_ipv4_policy);
18479e551110SNicolas Dichtel 	if (err < 0)
18489e551110SNicolas Dichtel 		goto errout;
18499e551110SNicolas Dichtel 
18509e551110SNicolas Dichtel 	err = EINVAL;
18519e551110SNicolas Dichtel 	if (!tb[NETCONFA_IFINDEX])
18529e551110SNicolas Dichtel 		goto errout;
18539e551110SNicolas Dichtel 
18549e551110SNicolas Dichtel 	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
18559e551110SNicolas Dichtel 	switch (ifindex) {
18569e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_ALL:
18579e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_all;
18589e551110SNicolas Dichtel 		break;
18599e551110SNicolas Dichtel 	case NETCONFA_IFINDEX_DEFAULT:
18609e551110SNicolas Dichtel 		devconf = net->ipv4.devconf_dflt;
18619e551110SNicolas Dichtel 		break;
18629e551110SNicolas Dichtel 	default:
18639e551110SNicolas Dichtel 		dev = __dev_get_by_index(net, ifindex);
186451456b29SIan Morris 		if (!dev)
18659e551110SNicolas Dichtel 			goto errout;
18669e551110SNicolas Dichtel 		in_dev = __in_dev_get_rtnl(dev);
186751456b29SIan Morris 		if (!in_dev)
18689e551110SNicolas Dichtel 			goto errout;
18699e551110SNicolas Dichtel 		devconf = &in_dev->cnf;
18709e551110SNicolas Dichtel 		break;
18719e551110SNicolas Dichtel 	}
18729e551110SNicolas Dichtel 
18739e551110SNicolas Dichtel 	err = -ENOBUFS;
18749e551110SNicolas Dichtel 	skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
187551456b29SIan Morris 	if (!skb)
18769e551110SNicolas Dichtel 		goto errout;
18779e551110SNicolas Dichtel 
18789e551110SNicolas Dichtel 	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
18799e551110SNicolas Dichtel 					NETLINK_CB(in_skb).portid,
18809e551110SNicolas Dichtel 					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
18819e551110SNicolas Dichtel 					-1);
18829e551110SNicolas Dichtel 	if (err < 0) {
18839e551110SNicolas Dichtel 		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
18849e551110SNicolas Dichtel 		WARN_ON(err == -EMSGSIZE);
18859e551110SNicolas Dichtel 		kfree_skb(skb);
18869e551110SNicolas Dichtel 		goto errout;
18879e551110SNicolas Dichtel 	}
18889e551110SNicolas Dichtel 	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
18899e551110SNicolas Dichtel errout:
18909e551110SNicolas Dichtel 	return err;
18919e551110SNicolas Dichtel }
18929e551110SNicolas Dichtel 
18937a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb,
18947a674200SNicolas Dichtel 				     struct netlink_callback *cb)
18957a674200SNicolas Dichtel {
18967a674200SNicolas Dichtel 	struct net *net = sock_net(skb->sk);
18977a674200SNicolas Dichtel 	int h, s_h;
18987a674200SNicolas Dichtel 	int idx, s_idx;
18997a674200SNicolas Dichtel 	struct net_device *dev;
19007a674200SNicolas Dichtel 	struct in_device *in_dev;
19017a674200SNicolas Dichtel 	struct hlist_head *head;
19027a674200SNicolas Dichtel 
19037a674200SNicolas Dichtel 	s_h = cb->args[0];
19047a674200SNicolas Dichtel 	s_idx = idx = cb->args[1];
19057a674200SNicolas Dichtel 
19067a674200SNicolas Dichtel 	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
19077a674200SNicolas Dichtel 		idx = 0;
19087a674200SNicolas Dichtel 		head = &net->dev_index_head[h];
19097a674200SNicolas Dichtel 		rcu_read_lock();
19100465277fSNicolas Dichtel 		cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
19110465277fSNicolas Dichtel 			  net->dev_base_seq;
19127a674200SNicolas Dichtel 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
19137a674200SNicolas Dichtel 			if (idx < s_idx)
19147a674200SNicolas Dichtel 				goto cont;
19157a674200SNicolas Dichtel 			in_dev = __in_dev_get_rcu(dev);
19167a674200SNicolas Dichtel 			if (!in_dev)
19177a674200SNicolas Dichtel 				goto cont;
19187a674200SNicolas Dichtel 
19197a674200SNicolas Dichtel 			if (inet_netconf_fill_devconf(skb, dev->ifindex,
19207a674200SNicolas Dichtel 						      &in_dev->cnf,
19217a674200SNicolas Dichtel 						      NETLINK_CB(cb->skb).portid,
19227a674200SNicolas Dichtel 						      cb->nlh->nlmsg_seq,
19237a674200SNicolas Dichtel 						      RTM_NEWNETCONF,
19247a674200SNicolas Dichtel 						      NLM_F_MULTI,
19257b46a644SDavid S. Miller 						      -1) < 0) {
19267a674200SNicolas Dichtel 				rcu_read_unlock();
19277a674200SNicolas Dichtel 				goto done;
19287a674200SNicolas Dichtel 			}
19290465277fSNicolas Dichtel 			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
19307a674200SNicolas Dichtel cont:
19317a674200SNicolas Dichtel 			idx++;
19327a674200SNicolas Dichtel 		}
19337a674200SNicolas Dichtel 		rcu_read_unlock();
19347a674200SNicolas Dichtel 	}
19357a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES) {
19367a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
19377a674200SNicolas Dichtel 					      net->ipv4.devconf_all,
19387a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
19397a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
19407a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
19417b46a644SDavid S. Miller 					      -1) < 0)
19427a674200SNicolas Dichtel 			goto done;
19437a674200SNicolas Dichtel 		else
19447a674200SNicolas Dichtel 			h++;
19457a674200SNicolas Dichtel 	}
19467a674200SNicolas Dichtel 	if (h == NETDEV_HASHENTRIES + 1) {
19477a674200SNicolas Dichtel 		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
19487a674200SNicolas Dichtel 					      net->ipv4.devconf_dflt,
19497a674200SNicolas Dichtel 					      NETLINK_CB(cb->skb).portid,
19507a674200SNicolas Dichtel 					      cb->nlh->nlmsg_seq,
19517a674200SNicolas Dichtel 					      RTM_NEWNETCONF, NLM_F_MULTI,
19527b46a644SDavid S. Miller 					      -1) < 0)
19537a674200SNicolas Dichtel 			goto done;
19547a674200SNicolas Dichtel 		else
19557a674200SNicolas Dichtel 			h++;
19567a674200SNicolas Dichtel 	}
19577a674200SNicolas Dichtel done:
19587a674200SNicolas Dichtel 	cb->args[0] = h;
19597a674200SNicolas Dichtel 	cb->args[1] = idx;
19607a674200SNicolas Dichtel 
19617a674200SNicolas Dichtel 	return skb->len;
19627a674200SNicolas Dichtel }
19637a674200SNicolas Dichtel 
19641da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
19651da177e4SLinus Torvalds 
1966c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i)
196731be3085SHerbert Xu {
196831be3085SHerbert Xu 	struct net_device *dev;
196931be3085SHerbert Xu 
197031be3085SHerbert Xu 	rcu_read_lock();
1971c6d14c84SEric Dumazet 	for_each_netdev_rcu(net, dev) {
1972c6d14c84SEric Dumazet 		struct in_device *in_dev;
1973c6d14c84SEric Dumazet 
197431be3085SHerbert Xu 		in_dev = __in_dev_get_rcu(dev);
197531be3085SHerbert Xu 		if (in_dev && !test_bit(i, in_dev->cnf.state))
19769355bbd6SPavel Emelyanov 			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
1977c6d14c84SEric Dumazet 	}
197831be3085SHerbert Xu 	rcu_read_unlock();
197931be3085SHerbert Xu }
198031be3085SHerbert Xu 
1981c6d14c84SEric Dumazet /* called with RTNL locked */
1982c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net)
198368dd299bSPavel Emelyanov {
198468dd299bSPavel Emelyanov 	struct net_device *dev;
1985586f1211SPavel Emelyanov 	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
198668dd299bSPavel Emelyanov 
1987586f1211SPavel Emelyanov 	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
19889355bbd6SPavel Emelyanov 	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
1989edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1990edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_ALL,
1991edc9e748SNicolas Dichtel 				    net->ipv4.devconf_all);
1992edc9e748SNicolas Dichtel 	inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
1993edc9e748SNicolas Dichtel 				    NETCONFA_IFINDEX_DEFAULT,
1994edc9e748SNicolas Dichtel 				    net->ipv4.devconf_dflt);
199568dd299bSPavel Emelyanov 
1996c0ce9fb3SPavel Emelyanov 	for_each_netdev(net, dev) {
199768dd299bSPavel Emelyanov 		struct in_device *in_dev;
19980187bdfbSBen Hutchings 		if (on)
19990187bdfbSBen Hutchings 			dev_disable_lro(dev);
200068dd299bSPavel Emelyanov 		rcu_read_lock();
200168dd299bSPavel Emelyanov 		in_dev = __in_dev_get_rcu(dev);
2002edc9e748SNicolas Dichtel 		if (in_dev) {
200368dd299bSPavel Emelyanov 			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
2004edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
2005edc9e748SNicolas Dichtel 						    dev->ifindex, &in_dev->cnf);
2006edc9e748SNicolas Dichtel 		}
200768dd299bSPavel Emelyanov 		rcu_read_unlock();
200868dd299bSPavel Emelyanov 	}
200968dd299bSPavel Emelyanov }
201068dd299bSPavel Emelyanov 
2011f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
2012f085ff1cSstephen hemminger {
2013f085ff1cSstephen hemminger 	if (cnf == net->ipv4.devconf_dflt)
2014f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_DEFAULT;
2015f085ff1cSstephen hemminger 	else if (cnf == net->ipv4.devconf_all)
2016f085ff1cSstephen hemminger 		return NETCONFA_IFINDEX_ALL;
2017f085ff1cSstephen hemminger 	else {
2018f085ff1cSstephen hemminger 		struct in_device *idev
2019f085ff1cSstephen hemminger 			= container_of(cnf, struct in_device, cnf);
2020f085ff1cSstephen hemminger 		return idev->dev->ifindex;
2021f085ff1cSstephen hemminger 	}
2022f085ff1cSstephen hemminger }
2023f085ff1cSstephen hemminger 
2024fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write,
20258d65af78SAlexey Dobriyan 			     void __user *buffer,
202631be3085SHerbert Xu 			     size_t *lenp, loff_t *ppos)
202731be3085SHerbert Xu {
2028d01ff0a0SPeter Pan(潘卫平) 	int old_value = *(int *)ctl->data;
20298d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
2030d01ff0a0SPeter Pan(潘卫平) 	int new_value = *(int *)ctl->data;
203131be3085SHerbert Xu 
203231be3085SHerbert Xu 	if (write) {
203331be3085SHerbert Xu 		struct ipv4_devconf *cnf = ctl->extra1;
2034c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
203531be3085SHerbert Xu 		int i = (int *)ctl->data - cnf->data;
2036f085ff1cSstephen hemminger 		int ifindex;
203731be3085SHerbert Xu 
203831be3085SHerbert Xu 		set_bit(i, cnf->state);
203931be3085SHerbert Xu 
20409355bbd6SPavel Emelyanov 		if (cnf == net->ipv4.devconf_dflt)
2041c0ce9fb3SPavel Emelyanov 			devinet_copy_dflt_conf(net, i);
2042d0daebc3SThomas Graf 		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
2043d0daebc3SThomas Graf 		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
2044d01ff0a0SPeter Pan(潘卫平) 			if ((new_value == 0) && (old_value != 0))
20454ccfe6d4SNicolas Dichtel 				rt_cache_flush(net);
2046f085ff1cSstephen hemminger 
2047cc535dfbSNicolas Dichtel 		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
2048cc535dfbSNicolas Dichtel 		    new_value != old_value) {
2049f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
2050cc535dfbSNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER,
2051cc535dfbSNicolas Dichtel 						    ifindex, cnf);
2052cc535dfbSNicolas Dichtel 		}
2053f085ff1cSstephen hemminger 		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
2054f085ff1cSstephen hemminger 		    new_value != old_value) {
2055f085ff1cSstephen hemminger 			ifindex = devinet_conf_ifindex(net, cnf);
205609aea5dfSstephen hemminger 			inet_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH,
2057f085ff1cSstephen hemminger 						    ifindex, cnf);
2058f085ff1cSstephen hemminger 		}
2059974d7af5SAndy Gospodarek 		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
2060974d7af5SAndy Gospodarek 		    new_value != old_value) {
2061974d7af5SAndy Gospodarek 			ifindex = devinet_conf_ifindex(net, cnf);
2062974d7af5SAndy Gospodarek 			inet_netconf_notify_devconf(net, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
2063974d7af5SAndy Gospodarek 						    ifindex, cnf);
2064974d7af5SAndy Gospodarek 		}
206531be3085SHerbert Xu 	}
206631be3085SHerbert Xu 
206731be3085SHerbert Xu 	return ret;
206831be3085SHerbert Xu }
206931be3085SHerbert Xu 
2070fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
20718d65af78SAlexey Dobriyan 				  void __user *buffer,
20721da177e4SLinus Torvalds 				  size_t *lenp, loff_t *ppos)
20731da177e4SLinus Torvalds {
20741da177e4SLinus Torvalds 	int *valp = ctl->data;
20751da177e4SLinus Torvalds 	int val = *valp;
207688af182eSEric W. Biederman 	loff_t pos = *ppos;
20778d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
20781da177e4SLinus Torvalds 
20791da177e4SLinus Torvalds 	if (write && *valp != val) {
2080c0ce9fb3SPavel Emelyanov 		struct net *net = ctl->extra2;
2081c0ce9fb3SPavel Emelyanov 
20820187bdfbSBen Hutchings 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
208388af182eSEric W. Biederman 			if (!rtnl_trylock()) {
208488af182eSEric W. Biederman 				/* Restore the original values before restarting */
208588af182eSEric W. Biederman 				*valp = val;
208688af182eSEric W. Biederman 				*ppos = pos;
20879b8adb5eSEric W. Biederman 				return restart_syscall();
208888af182eSEric W. Biederman 			}
20890187bdfbSBen Hutchings 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
2090c0ce9fb3SPavel Emelyanov 				inet_forward_change(net);
2091edc9e748SNicolas Dichtel 			} else {
20920187bdfbSBen Hutchings 				struct ipv4_devconf *cnf = ctl->extra1;
20930187bdfbSBen Hutchings 				struct in_device *idev =
20940187bdfbSBen Hutchings 					container_of(cnf, struct in_device, cnf);
2095edc9e748SNicolas Dichtel 				if (*valp)
20960187bdfbSBen Hutchings 					dev_disable_lro(idev->dev);
2097edc9e748SNicolas Dichtel 				inet_netconf_notify_devconf(net,
2098edc9e748SNicolas Dichtel 							    NETCONFA_FORWARDING,
2099edc9e748SNicolas Dichtel 							    idev->dev->ifindex,
2100edc9e748SNicolas Dichtel 							    cnf);
21010187bdfbSBen Hutchings 			}
21020187bdfbSBen Hutchings 			rtnl_unlock();
21034ccfe6d4SNicolas Dichtel 			rt_cache_flush(net);
2104edc9e748SNicolas Dichtel 		} else
2105edc9e748SNicolas Dichtel 			inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
2106edc9e748SNicolas Dichtel 						    NETCONFA_IFINDEX_DEFAULT,
2107edc9e748SNicolas Dichtel 						    net->ipv4.devconf_dflt);
21080187bdfbSBen Hutchings 	}
21091da177e4SLinus Torvalds 
21101da177e4SLinus Torvalds 	return ret;
21111da177e4SLinus Torvalds }
21121da177e4SLinus Torvalds 
2113fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
21148d65af78SAlexey Dobriyan 				void __user *buffer,
21151da177e4SLinus Torvalds 				size_t *lenp, loff_t *ppos)
21161da177e4SLinus Torvalds {
21171da177e4SLinus Torvalds 	int *valp = ctl->data;
21181da177e4SLinus Torvalds 	int val = *valp;
21198d65af78SAlexey Dobriyan 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
212076e6ebfbSDenis V. Lunev 	struct net *net = ctl->extra2;
21211da177e4SLinus Torvalds 
21221da177e4SLinus Torvalds 	if (write && *valp != val)
21234ccfe6d4SNicolas Dichtel 		rt_cache_flush(net);
21241da177e4SLinus Torvalds 
21251da177e4SLinus Torvalds 	return ret;
21261da177e4SLinus Torvalds }
21271da177e4SLinus Torvalds 
2128f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
212942f811b8SHerbert Xu 	{ \
213042f811b8SHerbert Xu 		.procname	= name, \
213142f811b8SHerbert Xu 		.data		= ipv4_devconf.data + \
213202291680SEric W. Biederman 				  IPV4_DEVCONF_ ## attr - 1, \
213342f811b8SHerbert Xu 		.maxlen		= sizeof(int), \
213442f811b8SHerbert Xu 		.mode		= mval, \
213542f811b8SHerbert Xu 		.proc_handler	= proc, \
213631be3085SHerbert Xu 		.extra1		= &ipv4_devconf, \
213742f811b8SHerbert Xu 	}
213842f811b8SHerbert Xu 
213942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
2140f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
214142f811b8SHerbert Xu 
214242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
2143f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
214442f811b8SHerbert Xu 
2145f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
2146f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
214742f811b8SHerbert Xu 
214842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
2149f8572d8fSEric W. Biederman 	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
215042f811b8SHerbert Xu 
21511da177e4SLinus Torvalds static struct devinet_sysctl_table {
21521da177e4SLinus Torvalds 	struct ctl_table_header *sysctl_header;
215302291680SEric W. Biederman 	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
21541da177e4SLinus Torvalds } devinet_sysctl = {
21551da177e4SLinus Torvalds 	.devinet_vars = {
215642f811b8SHerbert Xu 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
2157f8572d8fSEric W. Biederman 					     devinet_sysctl_forward),
215842f811b8SHerbert Xu 		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
215942f811b8SHerbert Xu 
216042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
216142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
216242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
216342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
216442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
216542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
216642f811b8SHerbert Xu 					"accept_source_route"),
21678153a10cSPatrick McHardy 		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
216828f6aeeaSJamal Hadi Salim 		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
216942f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
217042f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
217142f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
217242f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
217342f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
217442f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
217542f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
217642f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
217742f811b8SHerbert Xu 		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
2178eefef1cfSStephen Hemminger 		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
217965324144SJesper Dangaard Brouer 		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
21805c6fe01cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
21815c6fe01cSWilliam Manley 					"force_igmp_version"),
21822690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
21832690048cSWilliam Manley 					"igmpv2_unsolicited_report_interval"),
21842690048cSWilliam Manley 		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
21852690048cSWilliam Manley 					"igmpv3_unsolicited_report_interval"),
21860eeb075fSAndy Gospodarek 		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
21870eeb075fSAndy Gospodarek 					"ignore_routes_with_linkdown"),
218842f811b8SHerbert Xu 
218942f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
219042f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
219142f811b8SHerbert Xu 		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
219242f811b8SHerbert Xu 					      "promote_secondaries"),
2193d0daebc3SThomas Graf 		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
2194d0daebc3SThomas Graf 					      "route_localnet"),
21951da177e4SLinus Torvalds 	},
21961da177e4SLinus Torvalds };
21971da177e4SLinus Torvalds 
2198ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name,
2199f8572d8fSEric W. Biederman 					struct ipv4_devconf *p)
22001da177e4SLinus Torvalds {
22011da177e4SLinus Torvalds 	int i;
22029fa89642SPavel Emelyanov 	struct devinet_sysctl_table *t;
22038607ddb8SEric W. Biederman 	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
2204bfada697SPavel Emelyanov 
22059fa89642SPavel Emelyanov 	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
22061da177e4SLinus Torvalds 	if (!t)
22079fa89642SPavel Emelyanov 		goto out;
22089fa89642SPavel Emelyanov 
22091da177e4SLinus Torvalds 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
22101da177e4SLinus Torvalds 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
221131be3085SHerbert Xu 		t->devinet_vars[i].extra1 = p;
2212c0ce9fb3SPavel Emelyanov 		t->devinet_vars[i].extra2 = net;
22131da177e4SLinus Torvalds 	}
22141da177e4SLinus Torvalds 
22158607ddb8SEric W. Biederman 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
22161da177e4SLinus Torvalds 
22178607ddb8SEric W. Biederman 	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
22181da177e4SLinus Torvalds 	if (!t->sysctl_header)
22198607ddb8SEric W. Biederman 		goto free;
22201da177e4SLinus Torvalds 
22211da177e4SLinus Torvalds 	p->sysctl = t;
2222ea40b324SPavel Emelyanov 	return 0;
22231da177e4SLinus Torvalds 
22241da177e4SLinus Torvalds free:
22251da177e4SLinus Torvalds 	kfree(t);
22269fa89642SPavel Emelyanov out:
2227ea40b324SPavel Emelyanov 	return -ENOBUFS;
22281da177e4SLinus Torvalds }
22291da177e4SLinus Torvalds 
223051602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
223166f27a52SPavel Emelyanov {
223251602b2aSPavel Emelyanov 	struct devinet_sysctl_table *t = cnf->sysctl;
223366f27a52SPavel Emelyanov 
223451456b29SIan Morris 	if (!t)
223551602b2aSPavel Emelyanov 		return;
223651602b2aSPavel Emelyanov 
223751602b2aSPavel Emelyanov 	cnf->sysctl = NULL;
2238ff538818SLucian Adrian Grijincu 	unregister_net_sysctl_table(t->sysctl_header);
22391da177e4SLinus Torvalds 	kfree(t);
22401da177e4SLinus Torvalds }
224151602b2aSPavel Emelyanov 
224220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev)
224351602b2aSPavel Emelyanov {
224420e61da7SWANG Cong 	int err;
224520e61da7SWANG Cong 
224620e61da7SWANG Cong 	if (!sysctl_dev_name_is_allowed(idev->dev->name))
224720e61da7SWANG Cong 		return -EINVAL;
224820e61da7SWANG Cong 
224920e61da7SWANG Cong 	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
225020e61da7SWANG Cong 	if (err)
225120e61da7SWANG Cong 		return err;
225220e61da7SWANG Cong 	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
2253f8572d8fSEric W. Biederman 					&idev->cnf);
225420e61da7SWANG Cong 	if (err)
225520e61da7SWANG Cong 		neigh_sysctl_unregister(idev->arp_parms);
225620e61da7SWANG Cong 	return err;
225751602b2aSPavel Emelyanov }
225851602b2aSPavel Emelyanov 
225951602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev)
226051602b2aSPavel Emelyanov {
226151602b2aSPavel Emelyanov 	__devinet_sysctl_unregister(&idev->cnf);
226251602b2aSPavel Emelyanov 	neigh_sysctl_unregister(idev->arp_parms);
22631da177e4SLinus Torvalds }
22641da177e4SLinus Torvalds 
226568dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = {
226668dd299bSPavel Emelyanov 	{
226768dd299bSPavel Emelyanov 		.procname	= "ip_forward",
226868dd299bSPavel Emelyanov 		.data		= &ipv4_devconf.data[
226902291680SEric W. Biederman 					IPV4_DEVCONF_FORWARDING - 1],
227068dd299bSPavel Emelyanov 		.maxlen		= sizeof(int),
227168dd299bSPavel Emelyanov 		.mode		= 0644,
227268dd299bSPavel Emelyanov 		.proc_handler	= devinet_sysctl_forward,
227368dd299bSPavel Emelyanov 		.extra1		= &ipv4_devconf,
2274c0ce9fb3SPavel Emelyanov 		.extra2		= &init_net,
227568dd299bSPavel Emelyanov 	},
227668dd299bSPavel Emelyanov 	{ },
227768dd299bSPavel Emelyanov };
22782a75de0cSEric Dumazet #endif
227968dd299bSPavel Emelyanov 
2280752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net)
2281752d14dcSPavel Emelyanov {
2282752d14dcSPavel Emelyanov 	int err;
2283752d14dcSPavel Emelyanov 	struct ipv4_devconf *all, *dflt;
22842a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
22852a75de0cSEric Dumazet 	struct ctl_table *tbl = ctl_forward_entry;
2286752d14dcSPavel Emelyanov 	struct ctl_table_header *forw_hdr;
22872a75de0cSEric Dumazet #endif
2288752d14dcSPavel Emelyanov 
2289752d14dcSPavel Emelyanov 	err = -ENOMEM;
2290752d14dcSPavel Emelyanov 	all = &ipv4_devconf;
2291752d14dcSPavel Emelyanov 	dflt = &ipv4_devconf_dflt;
2292752d14dcSPavel Emelyanov 
229309ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net)) {
2294752d14dcSPavel Emelyanov 		all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
229551456b29SIan Morris 		if (!all)
2296752d14dcSPavel Emelyanov 			goto err_alloc_all;
2297752d14dcSPavel Emelyanov 
2298752d14dcSPavel Emelyanov 		dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
229951456b29SIan Morris 		if (!dflt)
2300752d14dcSPavel Emelyanov 			goto err_alloc_dflt;
2301752d14dcSPavel Emelyanov 
23022a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2303752d14dcSPavel Emelyanov 		tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
230451456b29SIan Morris 		if (!tbl)
2305752d14dcSPavel Emelyanov 			goto err_alloc_ctl;
2306752d14dcSPavel Emelyanov 
230702291680SEric W. Biederman 		tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
2308752d14dcSPavel Emelyanov 		tbl[0].extra1 = all;
2309752d14dcSPavel Emelyanov 		tbl[0].extra2 = net;
23102a75de0cSEric Dumazet #endif
2311752d14dcSPavel Emelyanov 	}
2312752d14dcSPavel Emelyanov 
2313752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2314f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "all", all);
2315752d14dcSPavel Emelyanov 	if (err < 0)
2316752d14dcSPavel Emelyanov 		goto err_reg_all;
2317752d14dcSPavel Emelyanov 
2318f8572d8fSEric W. Biederman 	err = __devinet_sysctl_register(net, "default", dflt);
2319752d14dcSPavel Emelyanov 	if (err < 0)
2320752d14dcSPavel Emelyanov 		goto err_reg_dflt;
2321752d14dcSPavel Emelyanov 
2322752d14dcSPavel Emelyanov 	err = -ENOMEM;
23238607ddb8SEric W. Biederman 	forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
232451456b29SIan Morris 	if (!forw_hdr)
2325752d14dcSPavel Emelyanov 		goto err_reg_ctl;
23262a75de0cSEric Dumazet 	net->ipv4.forw_hdr = forw_hdr;
2327752d14dcSPavel Emelyanov #endif
2328752d14dcSPavel Emelyanov 
2329752d14dcSPavel Emelyanov 	net->ipv4.devconf_all = all;
2330752d14dcSPavel Emelyanov 	net->ipv4.devconf_dflt = dflt;
2331752d14dcSPavel Emelyanov 	return 0;
2332752d14dcSPavel Emelyanov 
2333752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL
2334752d14dcSPavel Emelyanov err_reg_ctl:
2335752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(dflt);
2336752d14dcSPavel Emelyanov err_reg_dflt:
2337752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(all);
2338752d14dcSPavel Emelyanov err_reg_all:
2339752d14dcSPavel Emelyanov 	if (tbl != ctl_forward_entry)
2340752d14dcSPavel Emelyanov 		kfree(tbl);
2341752d14dcSPavel Emelyanov err_alloc_ctl:
23422a75de0cSEric Dumazet #endif
2343752d14dcSPavel Emelyanov 	if (dflt != &ipv4_devconf_dflt)
2344752d14dcSPavel Emelyanov 		kfree(dflt);
2345752d14dcSPavel Emelyanov err_alloc_dflt:
2346752d14dcSPavel Emelyanov 	if (all != &ipv4_devconf)
2347752d14dcSPavel Emelyanov 		kfree(all);
2348752d14dcSPavel Emelyanov err_alloc_all:
2349752d14dcSPavel Emelyanov 	return err;
2350752d14dcSPavel Emelyanov }
2351752d14dcSPavel Emelyanov 
2352752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net)
2353752d14dcSPavel Emelyanov {
23542a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL
2355752d14dcSPavel Emelyanov 	struct ctl_table *tbl;
2356752d14dcSPavel Emelyanov 
2357752d14dcSPavel Emelyanov 	tbl = net->ipv4.forw_hdr->ctl_table_arg;
2358752d14dcSPavel Emelyanov 	unregister_net_sysctl_table(net->ipv4.forw_hdr);
2359752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_dflt);
2360752d14dcSPavel Emelyanov 	__devinet_sysctl_unregister(net->ipv4.devconf_all);
2361752d14dcSPavel Emelyanov 	kfree(tbl);
23622a75de0cSEric Dumazet #endif
2363752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_dflt);
2364752d14dcSPavel Emelyanov 	kfree(net->ipv4.devconf_all);
2365752d14dcSPavel Emelyanov }
2366752d14dcSPavel Emelyanov 
2367752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = {
2368752d14dcSPavel Emelyanov 	.init = devinet_init_net,
2369752d14dcSPavel Emelyanov 	.exit = devinet_exit_net,
2370752d14dcSPavel Emelyanov };
2371752d14dcSPavel Emelyanov 
2372207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = {
23739f0f7272SThomas Graf 	.family		  = AF_INET,
23749f0f7272SThomas Graf 	.fill_link_af	  = inet_fill_link_af,
23759f0f7272SThomas Graf 	.get_link_af_size = inet_get_link_af_size,
2376cf7afbfeSThomas Graf 	.validate_link_af = inet_validate_link_af,
2377cf7afbfeSThomas Graf 	.set_link_af	  = inet_set_link_af,
23789f0f7272SThomas Graf };
23799f0f7272SThomas Graf 
23801da177e4SLinus Torvalds void __init devinet_init(void)
23811da177e4SLinus Torvalds {
2382fd23c3b3SDavid S. Miller 	int i;
2383fd23c3b3SDavid S. Miller 
2384fd23c3b3SDavid S. Miller 	for (i = 0; i < IN4_ADDR_HSIZE; i++)
2385fd23c3b3SDavid S. Miller 		INIT_HLIST_HEAD(&inet_addr_lst[i]);
2386fd23c3b3SDavid S. Miller 
2387752d14dcSPavel Emelyanov 	register_pernet_subsys(&devinet_ops);
2388752d14dcSPavel Emelyanov 
23891da177e4SLinus Torvalds 	register_gifconf(PF_INET, inet_gifconf);
23901da177e4SLinus Torvalds 	register_netdevice_notifier(&ip_netdev_notifier);
239163f3444fSThomas Graf 
2392906e073fSviresh kumar 	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
23935c766d64SJiri Pirko 
23949f0f7272SThomas Graf 	rtnl_af_register(&inet_af_ops);
23959f0f7272SThomas Graf 
2396c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
2397c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
2398c7ac8679SGreg Rose 	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
23999e551110SNicolas Dichtel 	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
24007a674200SNicolas Dichtel 		      inet_netconf_dump_devconf, NULL);
24011da177e4SLinus Torvalds }
2402