162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	NET3	IP device support routines.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Derived from the IP parts of dev.c 1.0.19
662306a36Sopenharmony_ci * 		Authors:	Ross Biro
762306a36Sopenharmony_ci *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
862306a36Sopenharmony_ci *				Mark Evans, <evansmp@uhura.aston.ac.uk>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *	Additional Authors:
1162306a36Sopenharmony_ci *		Alan Cox, <gw4pts@gw4pts.ampr.org>
1262306a36Sopenharmony_ci *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *	Changes:
1562306a36Sopenharmony_ci *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr
1662306a36Sopenharmony_ci *					lists.
1762306a36Sopenharmony_ci *		Cyrus Durgin:		updated for kmod
1862306a36Sopenharmony_ci *		Matthias Andree:	in devinet_ioctl, compare label and
1962306a36Sopenharmony_ci *					address (4.4BSD alias style support),
2062306a36Sopenharmony_ci *					fall back to comparing just the label
2162306a36Sopenharmony_ci *					if no match found.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/uaccess.h>
2662306a36Sopenharmony_ci#include <linux/bitops.h>
2762306a36Sopenharmony_ci#include <linux/capability.h>
2862306a36Sopenharmony_ci#include <linux/module.h>
2962306a36Sopenharmony_ci#include <linux/types.h>
3062306a36Sopenharmony_ci#include <linux/kernel.h>
3162306a36Sopenharmony_ci#include <linux/sched/signal.h>
3262306a36Sopenharmony_ci#include <linux/string.h>
3362306a36Sopenharmony_ci#include <linux/mm.h>
3462306a36Sopenharmony_ci#include <linux/socket.h>
3562306a36Sopenharmony_ci#include <linux/sockios.h>
3662306a36Sopenharmony_ci#include <linux/in.h>
3762306a36Sopenharmony_ci#include <linux/errno.h>
3862306a36Sopenharmony_ci#include <linux/interrupt.h>
3962306a36Sopenharmony_ci#include <linux/if_addr.h>
4062306a36Sopenharmony_ci#include <linux/if_ether.h>
4162306a36Sopenharmony_ci#include <linux/inet.h>
4262306a36Sopenharmony_ci#include <linux/netdevice.h>
4362306a36Sopenharmony_ci#include <linux/etherdevice.h>
4462306a36Sopenharmony_ci#include <linux/skbuff.h>
4562306a36Sopenharmony_ci#include <linux/init.h>
4662306a36Sopenharmony_ci#include <linux/notifier.h>
4762306a36Sopenharmony_ci#include <linux/inetdevice.h>
4862306a36Sopenharmony_ci#include <linux/igmp.h>
4962306a36Sopenharmony_ci#include <linux/slab.h>
5062306a36Sopenharmony_ci#include <linux/hash.h>
5162306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
5262306a36Sopenharmony_ci#include <linux/sysctl.h>
5362306a36Sopenharmony_ci#endif
5462306a36Sopenharmony_ci#include <linux/kmod.h>
5562306a36Sopenharmony_ci#include <linux/netconf.h>
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#include <net/arp.h>
5862306a36Sopenharmony_ci#include <net/ip.h>
5962306a36Sopenharmony_ci#include <net/route.h>
6062306a36Sopenharmony_ci#include <net/ip_fib.h>
6162306a36Sopenharmony_ci#include <net/rtnetlink.h>
6262306a36Sopenharmony_ci#include <net/net_namespace.h>
6362306a36Sopenharmony_ci#include <net/addrconf.h>
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define IPV6ONLY_FLAGS	\
6662306a36Sopenharmony_ci		(IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \
6762306a36Sopenharmony_ci		 IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \
6862306a36Sopenharmony_ci		 IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic struct ipv4_devconf ipv4_devconf = {
7162306a36Sopenharmony_ci	.data = {
7262306a36Sopenharmony_ci		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
7362306a36Sopenharmony_ci		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
7462306a36Sopenharmony_ci		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
7562306a36Sopenharmony_ci		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
7662306a36Sopenharmony_ci		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
7762306a36Sopenharmony_ci		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
7862306a36Sopenharmony_ci		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
7962306a36Sopenharmony_ci	},
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic struct ipv4_devconf ipv4_devconf_dflt = {
8362306a36Sopenharmony_ci	.data = {
8462306a36Sopenharmony_ci		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
8562306a36Sopenharmony_ci		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
8662306a36Sopenharmony_ci		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
8762306a36Sopenharmony_ci		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
8862306a36Sopenharmony_ci		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
8962306a36Sopenharmony_ci		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,
9062306a36Sopenharmony_ci		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,
9162306a36Sopenharmony_ci		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,
9262306a36Sopenharmony_ci	},
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define IPV4_DEVCONF_DFLT(net, attr) \
9662306a36Sopenharmony_ci	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
9962306a36Sopenharmony_ci	[IFA_LOCAL]     	= { .type = NLA_U32 },
10062306a36Sopenharmony_ci	[IFA_ADDRESS]   	= { .type = NLA_U32 },
10162306a36Sopenharmony_ci	[IFA_BROADCAST] 	= { .type = NLA_U32 },
10262306a36Sopenharmony_ci	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
10362306a36Sopenharmony_ci	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
10462306a36Sopenharmony_ci	[IFA_FLAGS]		= { .type = NLA_U32 },
10562306a36Sopenharmony_ci	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },
10662306a36Sopenharmony_ci	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
10762306a36Sopenharmony_ci	[IFA_PROTO]		= { .type = NLA_U8 },
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistruct inet_fill_args {
11162306a36Sopenharmony_ci	u32 portid;
11262306a36Sopenharmony_ci	u32 seq;
11362306a36Sopenharmony_ci	int event;
11462306a36Sopenharmony_ci	unsigned int flags;
11562306a36Sopenharmony_ci	int netnsid;
11662306a36Sopenharmony_ci	int ifindex;
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define IN4_ADDR_HSIZE_SHIFT	8
12062306a36Sopenharmony_ci#define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT)
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic u32 inet_addr_hash(const struct net *net, __be32 addr)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	u32 val = (__force u32) addr ^ net_hash_mix(net);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	u32 hash = inet_addr_hash(net, ifa->ifa_local);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	ASSERT_RTNL();
13662306a36Sopenharmony_ci	hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void inet_hash_remove(struct in_ifaddr *ifa)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	ASSERT_RTNL();
14262306a36Sopenharmony_ci	hlist_del_init_rcu(&ifa->hash);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * __ip_dev_find - find the first device with a given source address.
14762306a36Sopenharmony_ci * @net: the net namespace
14862306a36Sopenharmony_ci * @addr: the source address
14962306a36Sopenharmony_ci * @devref: if true, take a reference on the found device
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci * If a caller uses devref=false, it should be protected by RCU, or RTNL
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_cistruct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct net_device *result = NULL;
15662306a36Sopenharmony_ci	struct in_ifaddr *ifa;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	rcu_read_lock();
15962306a36Sopenharmony_ci	ifa = inet_lookup_ifaddr_rcu(net, addr);
16062306a36Sopenharmony_ci	if (!ifa) {
16162306a36Sopenharmony_ci		struct flowi4 fl4 = { .daddr = addr };
16262306a36Sopenharmony_ci		struct fib_result res = { 0 };
16362306a36Sopenharmony_ci		struct fib_table *local;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		/* Fallback to FIB local table so that communication
16662306a36Sopenharmony_ci		 * over loopback subnets work.
16762306a36Sopenharmony_ci		 */
16862306a36Sopenharmony_ci		local = fib_get_table(net, RT_TABLE_LOCAL);
16962306a36Sopenharmony_ci		if (local &&
17062306a36Sopenharmony_ci		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
17162306a36Sopenharmony_ci		    res.type == RTN_LOCAL)
17262306a36Sopenharmony_ci			result = FIB_RES_DEV(res);
17362306a36Sopenharmony_ci	} else {
17462306a36Sopenharmony_ci		result = ifa->ifa_dev->dev;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	if (result && devref)
17762306a36Sopenharmony_ci		dev_hold(result);
17862306a36Sopenharmony_ci	rcu_read_unlock();
17962306a36Sopenharmony_ci	return result;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ciEXPORT_SYMBOL(__ip_dev_find);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* called under RCU lock */
18462306a36Sopenharmony_cistruct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	u32 hash = inet_addr_hash(net, addr);
18762306a36Sopenharmony_ci	struct in_ifaddr *ifa;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash)
19062306a36Sopenharmony_ci		if (ifa->ifa_local == addr &&
19162306a36Sopenharmony_ci		    net_eq(dev_net(ifa->ifa_dev->dev), net))
19262306a36Sopenharmony_ci			return ifa;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return NULL;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
20062306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
20162306a36Sopenharmony_cistatic void inet_del_ifa(struct in_device *in_dev,
20262306a36Sopenharmony_ci			 struct in_ifaddr __rcu **ifap,
20362306a36Sopenharmony_ci			 int destroy);
20462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
20562306a36Sopenharmony_cistatic int devinet_sysctl_register(struct in_device *idev);
20662306a36Sopenharmony_cistatic void devinet_sysctl_unregister(struct in_device *idev);
20762306a36Sopenharmony_ci#else
20862306a36Sopenharmony_cistatic int devinet_sysctl_register(struct in_device *idev)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_cistatic void devinet_sysctl_unregister(struct in_device *idev)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci#endif
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/* Locks all the inet devices. */
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic struct in_ifaddr *inet_alloc_ifa(void)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void inet_rcu_free_ifa(struct rcu_head *head)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
22762306a36Sopenharmony_ci	if (ifa->ifa_dev)
22862306a36Sopenharmony_ci		in_dev_put(ifa->ifa_dev);
22962306a36Sopenharmony_ci	kfree(ifa);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic void inet_free_ifa(struct in_ifaddr *ifa)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void in_dev_free_rcu(struct rcu_head *head)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct in_device *idev = container_of(head, struct in_device, rcu_head);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	kfree(rcu_dereference_protected(idev->mc_hash, 1));
24262306a36Sopenharmony_ci	kfree(idev);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_civoid in_dev_finish_destroy(struct in_device *idev)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct net_device *dev = idev->dev;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	WARN_ON(idev->ifa_list);
25062306a36Sopenharmony_ci	WARN_ON(idev->mc_list);
25162306a36Sopenharmony_ci#ifdef NET_REFCNT_DEBUG
25262306a36Sopenharmony_ci	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
25362306a36Sopenharmony_ci#endif
25462306a36Sopenharmony_ci	netdev_put(dev, &idev->dev_tracker);
25562306a36Sopenharmony_ci	if (!idev->dead)
25662306a36Sopenharmony_ci		pr_err("Freeing alive in_device %p\n", idev);
25762306a36Sopenharmony_ci	else
25862306a36Sopenharmony_ci		call_rcu(&idev->rcu_head, in_dev_free_rcu);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ciEXPORT_SYMBOL(in_dev_finish_destroy);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic struct in_device *inetdev_init(struct net_device *dev)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct in_device *in_dev;
26562306a36Sopenharmony_ci	int err = -ENOMEM;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	ASSERT_RTNL();
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
27062306a36Sopenharmony_ci	if (!in_dev)
27162306a36Sopenharmony_ci		goto out;
27262306a36Sopenharmony_ci	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
27362306a36Sopenharmony_ci			sizeof(in_dev->cnf));
27462306a36Sopenharmony_ci	in_dev->cnf.sysctl = NULL;
27562306a36Sopenharmony_ci	in_dev->dev = dev;
27662306a36Sopenharmony_ci	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
27762306a36Sopenharmony_ci	if (!in_dev->arp_parms)
27862306a36Sopenharmony_ci		goto out_kfree;
27962306a36Sopenharmony_ci	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
28062306a36Sopenharmony_ci		dev_disable_lro(dev);
28162306a36Sopenharmony_ci	/* Reference in_dev->dev */
28262306a36Sopenharmony_ci	netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL);
28362306a36Sopenharmony_ci	/* Account for reference dev->ip_ptr (below) */
28462306a36Sopenharmony_ci	refcount_set(&in_dev->refcnt, 1);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	err = devinet_sysctl_register(in_dev);
28762306a36Sopenharmony_ci	if (err) {
28862306a36Sopenharmony_ci		in_dev->dead = 1;
28962306a36Sopenharmony_ci		neigh_parms_release(&arp_tbl, in_dev->arp_parms);
29062306a36Sopenharmony_ci		in_dev_put(in_dev);
29162306a36Sopenharmony_ci		in_dev = NULL;
29262306a36Sopenharmony_ci		goto out;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	ip_mc_init_dev(in_dev);
29562306a36Sopenharmony_ci	if (dev->flags & IFF_UP)
29662306a36Sopenharmony_ci		ip_mc_up(in_dev);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* we can receive as soon as ip_ptr is set -- do this last */
29962306a36Sopenharmony_ci	rcu_assign_pointer(dev->ip_ptr, in_dev);
30062306a36Sopenharmony_ciout:
30162306a36Sopenharmony_ci	return in_dev ?: ERR_PTR(err);
30262306a36Sopenharmony_ciout_kfree:
30362306a36Sopenharmony_ci	kfree(in_dev);
30462306a36Sopenharmony_ci	in_dev = NULL;
30562306a36Sopenharmony_ci	goto out;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void inetdev_destroy(struct in_device *in_dev)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct net_device *dev;
31162306a36Sopenharmony_ci	struct in_ifaddr *ifa;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ASSERT_RTNL();
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	dev = in_dev->dev;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	in_dev->dead = 1;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	ip_mc_destroy_dev(in_dev);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {
32262306a36Sopenharmony_ci		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
32362306a36Sopenharmony_ci		inet_free_ifa(ifa);
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	RCU_INIT_POINTER(dev->ip_ptr, NULL);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	devinet_sysctl_unregister(in_dev);
32962306a36Sopenharmony_ci	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
33062306a36Sopenharmony_ci	arp_ifdown(dev);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	in_dev_put(in_dev);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ciint inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	const struct in_ifaddr *ifa;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	rcu_read_lock();
34062306a36Sopenharmony_ci	in_dev_for_each_ifa_rcu(ifa, in_dev) {
34162306a36Sopenharmony_ci		if (inet_ifa_match(a, ifa)) {
34262306a36Sopenharmony_ci			if (!b || inet_ifa_match(b, ifa)) {
34362306a36Sopenharmony_ci				rcu_read_unlock();
34462306a36Sopenharmony_ci				return 1;
34562306a36Sopenharmony_ci			}
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	rcu_read_unlock();
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic void __inet_del_ifa(struct in_device *in_dev,
35362306a36Sopenharmony_ci			   struct in_ifaddr __rcu **ifap,
35462306a36Sopenharmony_ci			   int destroy, struct nlmsghdr *nlh, u32 portid)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct in_ifaddr *promote = NULL;
35762306a36Sopenharmony_ci	struct in_ifaddr *ifa, *ifa1;
35862306a36Sopenharmony_ci	struct in_ifaddr __rcu **last_prim;
35962306a36Sopenharmony_ci	struct in_ifaddr *prev_prom = NULL;
36062306a36Sopenharmony_ci	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ASSERT_RTNL();
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	ifa1 = rtnl_dereference(*ifap);
36562306a36Sopenharmony_ci	last_prim = ifap;
36662306a36Sopenharmony_ci	if (in_dev->dead)
36762306a36Sopenharmony_ci		goto no_promotions;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* 1. Deleting primary ifaddr forces deletion all secondaries
37062306a36Sopenharmony_ci	 * unless alias promotion is set
37162306a36Sopenharmony_ci	 **/
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
37462306a36Sopenharmony_ci		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
37762306a36Sopenharmony_ci			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
37862306a36Sopenharmony_ci			    ifa1->ifa_scope <= ifa->ifa_scope)
37962306a36Sopenharmony_ci				last_prim = &ifa->ifa_next;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
38262306a36Sopenharmony_ci			    ifa1->ifa_mask != ifa->ifa_mask ||
38362306a36Sopenharmony_ci			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
38462306a36Sopenharmony_ci				ifap1 = &ifa->ifa_next;
38562306a36Sopenharmony_ci				prev_prom = ifa;
38662306a36Sopenharmony_ci				continue;
38762306a36Sopenharmony_ci			}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci			if (!do_promote) {
39062306a36Sopenharmony_ci				inet_hash_remove(ifa);
39162306a36Sopenharmony_ci				*ifap1 = ifa->ifa_next;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);
39462306a36Sopenharmony_ci				blocking_notifier_call_chain(&inetaddr_chain,
39562306a36Sopenharmony_ci						NETDEV_DOWN, ifa);
39662306a36Sopenharmony_ci				inet_free_ifa(ifa);
39762306a36Sopenharmony_ci			} else {
39862306a36Sopenharmony_ci				promote = ifa;
39962306a36Sopenharmony_ci				break;
40062306a36Sopenharmony_ci			}
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* On promotion all secondaries from subnet are changing
40562306a36Sopenharmony_ci	 * the primary IP, we must remove all their routes silently
40662306a36Sopenharmony_ci	 * and later to add them back with new prefsrc. Do this
40762306a36Sopenharmony_ci	 * while all addresses are on the device list.
40862306a36Sopenharmony_ci	 */
40962306a36Sopenharmony_ci	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {
41062306a36Sopenharmony_ci		if (ifa1->ifa_mask == ifa->ifa_mask &&
41162306a36Sopenharmony_ci		    inet_ifa_match(ifa1->ifa_address, ifa))
41262306a36Sopenharmony_ci			fib_del_ifaddr(ifa, ifa1);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cino_promotions:
41662306a36Sopenharmony_ci	/* 2. Unlink it */
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	*ifap = ifa1->ifa_next;
41962306a36Sopenharmony_ci	inet_hash_remove(ifa1);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* 3. Announce address deletion */
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* Send message first, then call notifier.
42462306a36Sopenharmony_ci	   At first sight, FIB update triggered by notifier
42562306a36Sopenharmony_ci	   will refer to already deleted ifaddr, that could confuse
42662306a36Sopenharmony_ci	   netlink listeners. It is not true: look, gated sees
42762306a36Sopenharmony_ci	   that route deleted and if it still thinks that ifaddr
42862306a36Sopenharmony_ci	   is valid, it will try to restore deleted routes... Grr.
42962306a36Sopenharmony_ci	   So that, this order is correct.
43062306a36Sopenharmony_ci	 */
43162306a36Sopenharmony_ci	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);
43262306a36Sopenharmony_ci	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (promote) {
43562306a36Sopenharmony_ci		struct in_ifaddr *next_sec;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		next_sec = rtnl_dereference(promote->ifa_next);
43862306a36Sopenharmony_ci		if (prev_prom) {
43962306a36Sopenharmony_ci			struct in_ifaddr *last_sec;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci			rcu_assign_pointer(prev_prom->ifa_next, next_sec);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci			last_sec = rtnl_dereference(*last_prim);
44462306a36Sopenharmony_ci			rcu_assign_pointer(promote->ifa_next, last_sec);
44562306a36Sopenharmony_ci			rcu_assign_pointer(*last_prim, promote);
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		promote->ifa_flags &= ~IFA_F_SECONDARY;
44962306a36Sopenharmony_ci		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);
45062306a36Sopenharmony_ci		blocking_notifier_call_chain(&inetaddr_chain,
45162306a36Sopenharmony_ci				NETDEV_UP, promote);
45262306a36Sopenharmony_ci		for (ifa = next_sec; ifa;
45362306a36Sopenharmony_ci		     ifa = rtnl_dereference(ifa->ifa_next)) {
45462306a36Sopenharmony_ci			if (ifa1->ifa_mask != ifa->ifa_mask ||
45562306a36Sopenharmony_ci			    !inet_ifa_match(ifa1->ifa_address, ifa))
45662306a36Sopenharmony_ci					continue;
45762306a36Sopenharmony_ci			fib_add_ifaddr(ifa);
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	if (destroy)
46262306a36Sopenharmony_ci		inet_free_ifa(ifa1);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic void inet_del_ifa(struct in_device *in_dev,
46662306a36Sopenharmony_ci			 struct in_ifaddr __rcu **ifap,
46762306a36Sopenharmony_ci			 int destroy)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void check_lifetime(struct work_struct *work);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
47762306a36Sopenharmony_ci			     u32 portid, struct netlink_ext_ack *extack)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct in_ifaddr __rcu **last_primary, **ifap;
48062306a36Sopenharmony_ci	struct in_device *in_dev = ifa->ifa_dev;
48162306a36Sopenharmony_ci	struct in_validator_info ivi;
48262306a36Sopenharmony_ci	struct in_ifaddr *ifa1;
48362306a36Sopenharmony_ci	int ret;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ASSERT_RTNL();
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (!ifa->ifa_local) {
48862306a36Sopenharmony_ci		inet_free_ifa(ifa);
48962306a36Sopenharmony_ci		return 0;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ifa->ifa_flags &= ~IFA_F_SECONDARY;
49362306a36Sopenharmony_ci	last_primary = &in_dev->ifa_list;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* Don't set IPv6 only flags to IPv4 addresses */
49662306a36Sopenharmony_ci	ifa->ifa_flags &= ~IPV6ONLY_FLAGS;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	ifap = &in_dev->ifa_list;
49962306a36Sopenharmony_ci	ifa1 = rtnl_dereference(*ifap);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	while (ifa1) {
50262306a36Sopenharmony_ci		if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
50362306a36Sopenharmony_ci		    ifa->ifa_scope <= ifa1->ifa_scope)
50462306a36Sopenharmony_ci			last_primary = &ifa1->ifa_next;
50562306a36Sopenharmony_ci		if (ifa1->ifa_mask == ifa->ifa_mask &&
50662306a36Sopenharmony_ci		    inet_ifa_match(ifa1->ifa_address, ifa)) {
50762306a36Sopenharmony_ci			if (ifa1->ifa_local == ifa->ifa_local) {
50862306a36Sopenharmony_ci				inet_free_ifa(ifa);
50962306a36Sopenharmony_ci				return -EEXIST;
51062306a36Sopenharmony_ci			}
51162306a36Sopenharmony_ci			if (ifa1->ifa_scope != ifa->ifa_scope) {
51262306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value");
51362306a36Sopenharmony_ci				inet_free_ifa(ifa);
51462306a36Sopenharmony_ci				return -EINVAL;
51562306a36Sopenharmony_ci			}
51662306a36Sopenharmony_ci			ifa->ifa_flags |= IFA_F_SECONDARY;
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		ifap = &ifa1->ifa_next;
52062306a36Sopenharmony_ci		ifa1 = rtnl_dereference(*ifap);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Allow any devices that wish to register ifaddr validtors to weigh
52462306a36Sopenharmony_ci	 * in now, before changes are committed.  The rntl lock is serializing
52562306a36Sopenharmony_ci	 * access here, so the state should not change between a validator call
52662306a36Sopenharmony_ci	 * and a final notify on commit.  This isn't invoked on promotion under
52762306a36Sopenharmony_ci	 * the assumption that validators are checking the address itself, and
52862306a36Sopenharmony_ci	 * not the flags.
52962306a36Sopenharmony_ci	 */
53062306a36Sopenharmony_ci	ivi.ivi_addr = ifa->ifa_address;
53162306a36Sopenharmony_ci	ivi.ivi_dev = ifa->ifa_dev;
53262306a36Sopenharmony_ci	ivi.extack = extack;
53362306a36Sopenharmony_ci	ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
53462306a36Sopenharmony_ci					   NETDEV_UP, &ivi);
53562306a36Sopenharmony_ci	ret = notifier_to_errno(ret);
53662306a36Sopenharmony_ci	if (ret) {
53762306a36Sopenharmony_ci		inet_free_ifa(ifa);
53862306a36Sopenharmony_ci		return ret;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (!(ifa->ifa_flags & IFA_F_SECONDARY))
54262306a36Sopenharmony_ci		ifap = last_primary;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	rcu_assign_pointer(ifa->ifa_next, *ifap);
54562306a36Sopenharmony_ci	rcu_assign_pointer(*ifap, ifa);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	inet_hash_insert(dev_net(in_dev->dev), ifa);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	cancel_delayed_work(&check_lifetime_work);
55062306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Send message first, then call notifier.
55362306a36Sopenharmony_ci	   Notifier will trigger FIB update, so that
55462306a36Sopenharmony_ci	   listeners of netlink will know about new ifaddr */
55562306a36Sopenharmony_ci	rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid);
55662306a36Sopenharmony_ci	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return 0;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int inet_insert_ifa(struct in_ifaddr *ifa)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	return __inet_insert_ifa(ifa, NULL, 0, NULL);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct in_device *in_dev = __in_dev_get_rtnl(dev);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	ASSERT_RTNL();
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (!in_dev) {
57362306a36Sopenharmony_ci		inet_free_ifa(ifa);
57462306a36Sopenharmony_ci		return -ENOBUFS;
57562306a36Sopenharmony_ci	}
57662306a36Sopenharmony_ci	ipv4_devconf_setall(in_dev);
57762306a36Sopenharmony_ci	neigh_parms_data_state_setall(in_dev->arp_parms);
57862306a36Sopenharmony_ci	if (ifa->ifa_dev != in_dev) {
57962306a36Sopenharmony_ci		WARN_ON(ifa->ifa_dev);
58062306a36Sopenharmony_ci		in_dev_hold(in_dev);
58162306a36Sopenharmony_ci		ifa->ifa_dev = in_dev;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	if (ipv4_is_loopback(ifa->ifa_local))
58462306a36Sopenharmony_ci		ifa->ifa_scope = RT_SCOPE_HOST;
58562306a36Sopenharmony_ci	return inet_insert_ifa(ifa);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/* Caller must hold RCU or RTNL :
58962306a36Sopenharmony_ci * We dont take a reference on found in_device
59062306a36Sopenharmony_ci */
59162306a36Sopenharmony_cistruct in_device *inetdev_by_index(struct net *net, int ifindex)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	struct net_device *dev;
59462306a36Sopenharmony_ci	struct in_device *in_dev = NULL;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	rcu_read_lock();
59762306a36Sopenharmony_ci	dev = dev_get_by_index_rcu(net, ifindex);
59862306a36Sopenharmony_ci	if (dev)
59962306a36Sopenharmony_ci		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
60062306a36Sopenharmony_ci	rcu_read_unlock();
60162306a36Sopenharmony_ci	return in_dev;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ciEXPORT_SYMBOL(inetdev_by_index);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/* Called only from RTNL semaphored context. No locks. */
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistruct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
60862306a36Sopenharmony_ci				    __be32 mask)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct in_ifaddr *ifa;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	ASSERT_RTNL();
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
61562306a36Sopenharmony_ci		if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
61662306a36Sopenharmony_ci			return ifa;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci	return NULL;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic int ip_mc_autojoin_config(struct net *net, bool join,
62262306a36Sopenharmony_ci				 const struct in_ifaddr *ifa)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci#if defined(CONFIG_IP_MULTICAST)
62562306a36Sopenharmony_ci	struct ip_mreqn mreq = {
62662306a36Sopenharmony_ci		.imr_multiaddr.s_addr = ifa->ifa_address,
62762306a36Sopenharmony_ci		.imr_ifindex = ifa->ifa_dev->dev->ifindex,
62862306a36Sopenharmony_ci	};
62962306a36Sopenharmony_ci	struct sock *sk = net->ipv4.mc_autojoin_sk;
63062306a36Sopenharmony_ci	int ret;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	ASSERT_RTNL();
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	lock_sock(sk);
63562306a36Sopenharmony_ci	if (join)
63662306a36Sopenharmony_ci		ret = ip_mc_join_group(sk, &mreq);
63762306a36Sopenharmony_ci	else
63862306a36Sopenharmony_ci		ret = ip_mc_leave_group(sk, &mreq);
63962306a36Sopenharmony_ci	release_sock(sk);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return ret;
64262306a36Sopenharmony_ci#else
64362306a36Sopenharmony_ci	return -EOPNOTSUPP;
64462306a36Sopenharmony_ci#endif
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
64862306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
65162306a36Sopenharmony_ci	struct in_ifaddr __rcu **ifap;
65262306a36Sopenharmony_ci	struct nlattr *tb[IFA_MAX+1];
65362306a36Sopenharmony_ci	struct in_device *in_dev;
65462306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
65562306a36Sopenharmony_ci	struct in_ifaddr *ifa;
65662306a36Sopenharmony_ci	int err;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	ASSERT_RTNL();
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
66162306a36Sopenharmony_ci				     ifa_ipv4_policy, extack);
66262306a36Sopenharmony_ci	if (err < 0)
66362306a36Sopenharmony_ci		goto errout;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
66662306a36Sopenharmony_ci	in_dev = inetdev_by_index(net, ifm->ifa_index);
66762306a36Sopenharmony_ci	if (!in_dev) {
66862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
66962306a36Sopenharmony_ci		err = -ENODEV;
67062306a36Sopenharmony_ci		goto errout;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL;
67462306a36Sopenharmony_ci	     ifap = &ifa->ifa_next) {
67562306a36Sopenharmony_ci		if (tb[IFA_LOCAL] &&
67662306a36Sopenharmony_ci		    ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL]))
67762306a36Sopenharmony_ci			continue;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
68062306a36Sopenharmony_ci			continue;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		if (tb[IFA_ADDRESS] &&
68362306a36Sopenharmony_ci		    (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
68462306a36Sopenharmony_ci		    !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa)))
68562306a36Sopenharmony_ci			continue;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci		if (ipv4_is_multicast(ifa->ifa_address))
68862306a36Sopenharmony_ci			ip_mc_autojoin_config(net, false, ifa);
68962306a36Sopenharmony_ci		__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
69062306a36Sopenharmony_ci		return 0;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	NL_SET_ERR_MSG(extack, "ipv4: Address not found");
69462306a36Sopenharmony_ci	err = -EADDRNOTAVAIL;
69562306a36Sopenharmony_cierrout:
69662306a36Sopenharmony_ci	return err;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci#define INFINITY_LIFE_TIME	0xFFFFFFFF
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic void check_lifetime(struct work_struct *work)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	unsigned long now, next, next_sec, next_sched;
70462306a36Sopenharmony_ci	struct in_ifaddr *ifa;
70562306a36Sopenharmony_ci	struct hlist_node *n;
70662306a36Sopenharmony_ci	int i;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	now = jiffies;
70962306a36Sopenharmony_ci	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	for (i = 0; i < IN4_ADDR_HSIZE; i++) {
71262306a36Sopenharmony_ci		bool change_needed = false;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		rcu_read_lock();
71562306a36Sopenharmony_ci		hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
71662306a36Sopenharmony_ci			unsigned long age;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci			if (ifa->ifa_flags & IFA_F_PERMANENT)
71962306a36Sopenharmony_ci				continue;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci			/* We try to batch several events at once. */
72262306a36Sopenharmony_ci			age = (now - ifa->ifa_tstamp +
72362306a36Sopenharmony_ci			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
72662306a36Sopenharmony_ci			    age >= ifa->ifa_valid_lft) {
72762306a36Sopenharmony_ci				change_needed = true;
72862306a36Sopenharmony_ci			} else if (ifa->ifa_preferred_lft ==
72962306a36Sopenharmony_ci				   INFINITY_LIFE_TIME) {
73062306a36Sopenharmony_ci				continue;
73162306a36Sopenharmony_ci			} else if (age >= ifa->ifa_preferred_lft) {
73262306a36Sopenharmony_ci				if (time_before(ifa->ifa_tstamp +
73362306a36Sopenharmony_ci						ifa->ifa_valid_lft * HZ, next))
73462306a36Sopenharmony_ci					next = ifa->ifa_tstamp +
73562306a36Sopenharmony_ci					       ifa->ifa_valid_lft * HZ;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci				if (!(ifa->ifa_flags & IFA_F_DEPRECATED))
73862306a36Sopenharmony_ci					change_needed = true;
73962306a36Sopenharmony_ci			} else if (time_before(ifa->ifa_tstamp +
74062306a36Sopenharmony_ci					       ifa->ifa_preferred_lft * HZ,
74162306a36Sopenharmony_ci					       next)) {
74262306a36Sopenharmony_ci				next = ifa->ifa_tstamp +
74362306a36Sopenharmony_ci				       ifa->ifa_preferred_lft * HZ;
74462306a36Sopenharmony_ci			}
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci		rcu_read_unlock();
74762306a36Sopenharmony_ci		if (!change_needed)
74862306a36Sopenharmony_ci			continue;
74962306a36Sopenharmony_ci		rtnl_lock();
75062306a36Sopenharmony_ci		hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) {
75162306a36Sopenharmony_ci			unsigned long age;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci			if (ifa->ifa_flags & IFA_F_PERMANENT)
75462306a36Sopenharmony_ci				continue;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci			/* We try to batch several events at once. */
75762306a36Sopenharmony_ci			age = (now - ifa->ifa_tstamp +
75862306a36Sopenharmony_ci			       ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci			if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME &&
76162306a36Sopenharmony_ci			    age >= ifa->ifa_valid_lft) {
76262306a36Sopenharmony_ci				struct in_ifaddr __rcu **ifap;
76362306a36Sopenharmony_ci				struct in_ifaddr *tmp;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci				ifap = &ifa->ifa_dev->ifa_list;
76662306a36Sopenharmony_ci				tmp = rtnl_dereference(*ifap);
76762306a36Sopenharmony_ci				while (tmp) {
76862306a36Sopenharmony_ci					if (tmp == ifa) {
76962306a36Sopenharmony_ci						inet_del_ifa(ifa->ifa_dev,
77062306a36Sopenharmony_ci							     ifap, 1);
77162306a36Sopenharmony_ci						break;
77262306a36Sopenharmony_ci					}
77362306a36Sopenharmony_ci					ifap = &tmp->ifa_next;
77462306a36Sopenharmony_ci					tmp = rtnl_dereference(*ifap);
77562306a36Sopenharmony_ci				}
77662306a36Sopenharmony_ci			} else if (ifa->ifa_preferred_lft !=
77762306a36Sopenharmony_ci				   INFINITY_LIFE_TIME &&
77862306a36Sopenharmony_ci				   age >= ifa->ifa_preferred_lft &&
77962306a36Sopenharmony_ci				   !(ifa->ifa_flags & IFA_F_DEPRECATED)) {
78062306a36Sopenharmony_ci				ifa->ifa_flags |= IFA_F_DEPRECATED;
78162306a36Sopenharmony_ci				rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
78262306a36Sopenharmony_ci			}
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci		rtnl_unlock();
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	next_sec = round_jiffies_up(next);
78862306a36Sopenharmony_ci	next_sched = next;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	/* If rounded timeout is accurate enough, accept it. */
79162306a36Sopenharmony_ci	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
79262306a36Sopenharmony_ci		next_sched = next_sec;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	now = jiffies;
79562306a36Sopenharmony_ci	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
79662306a36Sopenharmony_ci	if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX))
79762306a36Sopenharmony_ci		next_sched = now + ADDRCONF_TIMER_FUZZ_MAX;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work,
80062306a36Sopenharmony_ci			next_sched - now);
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft,
80462306a36Sopenharmony_ci			     __u32 prefered_lft)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	unsigned long timeout;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	timeout = addrconf_timeout_fixup(valid_lft, HZ);
81162306a36Sopenharmony_ci	if (addrconf_finite_timeout(timeout))
81262306a36Sopenharmony_ci		ifa->ifa_valid_lft = timeout;
81362306a36Sopenharmony_ci	else
81462306a36Sopenharmony_ci		ifa->ifa_flags |= IFA_F_PERMANENT;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	timeout = addrconf_timeout_fixup(prefered_lft, HZ);
81762306a36Sopenharmony_ci	if (addrconf_finite_timeout(timeout)) {
81862306a36Sopenharmony_ci		if (timeout == 0)
81962306a36Sopenharmony_ci			ifa->ifa_flags |= IFA_F_DEPRECATED;
82062306a36Sopenharmony_ci		ifa->ifa_preferred_lft = timeout;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci	ifa->ifa_tstamp = jiffies;
82362306a36Sopenharmony_ci	if (!ifa->ifa_cstamp)
82462306a36Sopenharmony_ci		ifa->ifa_cstamp = ifa->ifa_tstamp;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
82862306a36Sopenharmony_ci				       __u32 *pvalid_lft, __u32 *pprefered_lft,
82962306a36Sopenharmony_ci				       struct netlink_ext_ack *extack)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct nlattr *tb[IFA_MAX+1];
83262306a36Sopenharmony_ci	struct in_ifaddr *ifa;
83362306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
83462306a36Sopenharmony_ci	struct net_device *dev;
83562306a36Sopenharmony_ci	struct in_device *in_dev;
83662306a36Sopenharmony_ci	int err;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
83962306a36Sopenharmony_ci				     ifa_ipv4_policy, extack);
84062306a36Sopenharmony_ci	if (err < 0)
84162306a36Sopenharmony_ci		goto errout;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
84462306a36Sopenharmony_ci	err = -EINVAL;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (ifm->ifa_prefixlen > 32) {
84762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length");
84862306a36Sopenharmony_ci		goto errout;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (!tb[IFA_LOCAL]) {
85262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied");
85362306a36Sopenharmony_ci		goto errout;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ifm->ifa_index);
85762306a36Sopenharmony_ci	err = -ENODEV;
85862306a36Sopenharmony_ci	if (!dev) {
85962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ipv4: Device not found");
86062306a36Sopenharmony_ci		goto errout;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	in_dev = __in_dev_get_rtnl(dev);
86462306a36Sopenharmony_ci	err = -ENOBUFS;
86562306a36Sopenharmony_ci	if (!in_dev)
86662306a36Sopenharmony_ci		goto errout;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	ifa = inet_alloc_ifa();
86962306a36Sopenharmony_ci	if (!ifa)
87062306a36Sopenharmony_ci		/*
87162306a36Sopenharmony_ci		 * A potential indev allocation can be left alive, it stays
87262306a36Sopenharmony_ci		 * assigned to its device and is destroy with it.
87362306a36Sopenharmony_ci		 */
87462306a36Sopenharmony_ci		goto errout;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	ipv4_devconf_setall(in_dev);
87762306a36Sopenharmony_ci	neigh_parms_data_state_setall(in_dev->arp_parms);
87862306a36Sopenharmony_ci	in_dev_hold(in_dev);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (!tb[IFA_ADDRESS])
88162306a36Sopenharmony_ci		tb[IFA_ADDRESS] = tb[IFA_LOCAL];
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	INIT_HLIST_NODE(&ifa->hash);
88462306a36Sopenharmony_ci	ifa->ifa_prefixlen = ifm->ifa_prefixlen;
88562306a36Sopenharmony_ci	ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
88662306a36Sopenharmony_ci	ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
88762306a36Sopenharmony_ci					 ifm->ifa_flags;
88862306a36Sopenharmony_ci	ifa->ifa_scope = ifm->ifa_scope;
88962306a36Sopenharmony_ci	ifa->ifa_dev = in_dev;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]);
89262306a36Sopenharmony_ci	ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (tb[IFA_BROADCAST])
89562306a36Sopenharmony_ci		ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (tb[IFA_LABEL])
89862306a36Sopenharmony_ci		nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
89962306a36Sopenharmony_ci	else
90062306a36Sopenharmony_ci		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (tb[IFA_RT_PRIORITY])
90362306a36Sopenharmony_ci		ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (tb[IFA_PROTO])
90662306a36Sopenharmony_ci		ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (tb[IFA_CACHEINFO]) {
90962306a36Sopenharmony_ci		struct ifa_cacheinfo *ci;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		ci = nla_data(tb[IFA_CACHEINFO]);
91262306a36Sopenharmony_ci		if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) {
91362306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid");
91462306a36Sopenharmony_ci			err = -EINVAL;
91562306a36Sopenharmony_ci			goto errout_free;
91662306a36Sopenharmony_ci		}
91762306a36Sopenharmony_ci		*pvalid_lft = ci->ifa_valid;
91862306a36Sopenharmony_ci		*pprefered_lft = ci->ifa_prefered;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return ifa;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cierrout_free:
92462306a36Sopenharmony_ci	inet_free_ifa(ifa);
92562306a36Sopenharmony_cierrout:
92662306a36Sopenharmony_ci	return ERR_PTR(err);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	struct in_device *in_dev = ifa->ifa_dev;
93262306a36Sopenharmony_ci	struct in_ifaddr *ifa1;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if (!ifa->ifa_local)
93562306a36Sopenharmony_ci		return NULL;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	in_dev_for_each_ifa_rtnl(ifa1, in_dev) {
93862306a36Sopenharmony_ci		if (ifa1->ifa_mask == ifa->ifa_mask &&
93962306a36Sopenharmony_ci		    inet_ifa_match(ifa1->ifa_address, ifa) &&
94062306a36Sopenharmony_ci		    ifa1->ifa_local == ifa->ifa_local)
94162306a36Sopenharmony_ci			return ifa1;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci	return NULL;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
94762306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
95062306a36Sopenharmony_ci	struct in_ifaddr *ifa;
95162306a36Sopenharmony_ci	struct in_ifaddr *ifa_existing;
95262306a36Sopenharmony_ci	__u32 valid_lft = INFINITY_LIFE_TIME;
95362306a36Sopenharmony_ci	__u32 prefered_lft = INFINITY_LIFE_TIME;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	ASSERT_RTNL();
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack);
95862306a36Sopenharmony_ci	if (IS_ERR(ifa))
95962306a36Sopenharmony_ci		return PTR_ERR(ifa);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	ifa_existing = find_matching_ifa(ifa);
96262306a36Sopenharmony_ci	if (!ifa_existing) {
96362306a36Sopenharmony_ci		/* It would be best to check for !NLM_F_CREATE here but
96462306a36Sopenharmony_ci		 * userspace already relies on not having to provide this.
96562306a36Sopenharmony_ci		 */
96662306a36Sopenharmony_ci		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
96762306a36Sopenharmony_ci		if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
96862306a36Sopenharmony_ci			int ret = ip_mc_autojoin_config(net, true, ifa);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci			if (ret < 0) {
97162306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed");
97262306a36Sopenharmony_ci				inet_free_ifa(ifa);
97362306a36Sopenharmony_ci				return ret;
97462306a36Sopenharmony_ci			}
97562306a36Sopenharmony_ci		}
97662306a36Sopenharmony_ci		return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid,
97762306a36Sopenharmony_ci					 extack);
97862306a36Sopenharmony_ci	} else {
97962306a36Sopenharmony_ci		u32 new_metric = ifa->ifa_rt_priority;
98062306a36Sopenharmony_ci		u8 new_proto = ifa->ifa_proto;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		inet_free_ifa(ifa);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		if (nlh->nlmsg_flags & NLM_F_EXCL ||
98562306a36Sopenharmony_ci		    !(nlh->nlmsg_flags & NLM_F_REPLACE)) {
98662306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "ipv4: Address already assigned");
98762306a36Sopenharmony_ci			return -EEXIST;
98862306a36Sopenharmony_ci		}
98962306a36Sopenharmony_ci		ifa = ifa_existing;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		if (ifa->ifa_rt_priority != new_metric) {
99262306a36Sopenharmony_ci			fib_modify_prefix_metric(ifa, new_metric);
99362306a36Sopenharmony_ci			ifa->ifa_rt_priority = new_metric;
99462306a36Sopenharmony_ci		}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci		ifa->ifa_proto = new_proto;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
99962306a36Sopenharmony_ci		cancel_delayed_work(&check_lifetime_work);
100062306a36Sopenharmony_ci		queue_delayed_work(system_power_efficient_wq,
100162306a36Sopenharmony_ci				&check_lifetime_work, 0);
100262306a36Sopenharmony_ci		rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci	return 0;
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci/*
100862306a36Sopenharmony_ci *	Determine a default network mask, based on the IP address.
100962306a36Sopenharmony_ci */
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int inet_abc_len(__be32 addr)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	int rc = -1;	/* Something else, probably a multicast. */
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr))
101662306a36Sopenharmony_ci		rc = 0;
101762306a36Sopenharmony_ci	else {
101862306a36Sopenharmony_ci		__u32 haddr = ntohl(addr);
101962306a36Sopenharmony_ci		if (IN_CLASSA(haddr))
102062306a36Sopenharmony_ci			rc = 8;
102162306a36Sopenharmony_ci		else if (IN_CLASSB(haddr))
102262306a36Sopenharmony_ci			rc = 16;
102362306a36Sopenharmony_ci		else if (IN_CLASSC(haddr))
102462306a36Sopenharmony_ci			rc = 24;
102562306a36Sopenharmony_ci		else if (IN_CLASSE(haddr))
102662306a36Sopenharmony_ci			rc = 32;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return rc;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ciint devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct sockaddr_in sin_orig;
103662306a36Sopenharmony_ci	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
103762306a36Sopenharmony_ci	struct in_ifaddr __rcu **ifap = NULL;
103862306a36Sopenharmony_ci	struct in_device *in_dev;
103962306a36Sopenharmony_ci	struct in_ifaddr *ifa = NULL;
104062306a36Sopenharmony_ci	struct net_device *dev;
104162306a36Sopenharmony_ci	char *colon;
104262306a36Sopenharmony_ci	int ret = -EFAULT;
104362306a36Sopenharmony_ci	int tryaddrmatch = 0;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	ifr->ifr_name[IFNAMSIZ - 1] = 0;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* save original address for comparison */
104862306a36Sopenharmony_ci	memcpy(&sin_orig, sin, sizeof(*sin));
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	colon = strchr(ifr->ifr_name, ':');
105162306a36Sopenharmony_ci	if (colon)
105262306a36Sopenharmony_ci		*colon = 0;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	dev_load(net, ifr->ifr_name);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	switch (cmd) {
105762306a36Sopenharmony_ci	case SIOCGIFADDR:	/* Get interface address */
105862306a36Sopenharmony_ci	case SIOCGIFBRDADDR:	/* Get the broadcast address */
105962306a36Sopenharmony_ci	case SIOCGIFDSTADDR:	/* Get the destination address */
106062306a36Sopenharmony_ci	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
106162306a36Sopenharmony_ci		/* Note that these ioctls will not sleep,
106262306a36Sopenharmony_ci		   so that we do not impose a lock.
106362306a36Sopenharmony_ci		   One day we will be forced to put shlock here (I mean SMP)
106462306a36Sopenharmony_ci		 */
106562306a36Sopenharmony_ci		tryaddrmatch = (sin_orig.sin_family == AF_INET);
106662306a36Sopenharmony_ci		memset(sin, 0, sizeof(*sin));
106762306a36Sopenharmony_ci		sin->sin_family = AF_INET;
106862306a36Sopenharmony_ci		break;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	case SIOCSIFFLAGS:
107162306a36Sopenharmony_ci		ret = -EPERM;
107262306a36Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
107362306a36Sopenharmony_ci			goto out;
107462306a36Sopenharmony_ci		break;
107562306a36Sopenharmony_ci	case SIOCSIFADDR:	/* Set interface address (and family) */
107662306a36Sopenharmony_ci	case SIOCSIFBRDADDR:	/* Set the broadcast address */
107762306a36Sopenharmony_ci	case SIOCSIFDSTADDR:	/* Set the destination address */
107862306a36Sopenharmony_ci	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
107962306a36Sopenharmony_ci		ret = -EPERM;
108062306a36Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
108162306a36Sopenharmony_ci			goto out;
108262306a36Sopenharmony_ci		ret = -EINVAL;
108362306a36Sopenharmony_ci		if (sin->sin_family != AF_INET)
108462306a36Sopenharmony_ci			goto out;
108562306a36Sopenharmony_ci		break;
108662306a36Sopenharmony_ci	default:
108762306a36Sopenharmony_ci		ret = -EINVAL;
108862306a36Sopenharmony_ci		goto out;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	rtnl_lock();
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	ret = -ENODEV;
109462306a36Sopenharmony_ci	dev = __dev_get_by_name(net, ifr->ifr_name);
109562306a36Sopenharmony_ci	if (!dev)
109662306a36Sopenharmony_ci		goto done;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (colon)
109962306a36Sopenharmony_ci		*colon = ':';
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	in_dev = __in_dev_get_rtnl(dev);
110262306a36Sopenharmony_ci	if (in_dev) {
110362306a36Sopenharmony_ci		if (tryaddrmatch) {
110462306a36Sopenharmony_ci			/* Matthias Andree */
110562306a36Sopenharmony_ci			/* compare label and address (4.4BSD style) */
110662306a36Sopenharmony_ci			/* note: we only do this for a limited set of ioctls
110762306a36Sopenharmony_ci			   and only if the original address family was AF_INET.
110862306a36Sopenharmony_ci			   This is checked above. */
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci			for (ifap = &in_dev->ifa_list;
111162306a36Sopenharmony_ci			     (ifa = rtnl_dereference(*ifap)) != NULL;
111262306a36Sopenharmony_ci			     ifap = &ifa->ifa_next) {
111362306a36Sopenharmony_ci				if (!strcmp(ifr->ifr_name, ifa->ifa_label) &&
111462306a36Sopenharmony_ci				    sin_orig.sin_addr.s_addr ==
111562306a36Sopenharmony_ci							ifa->ifa_local) {
111662306a36Sopenharmony_ci					break; /* found */
111762306a36Sopenharmony_ci				}
111862306a36Sopenharmony_ci			}
111962306a36Sopenharmony_ci		}
112062306a36Sopenharmony_ci		/* we didn't get a match, maybe the application is
112162306a36Sopenharmony_ci		   4.3BSD-style and passed in junk so we fall back to
112262306a36Sopenharmony_ci		   comparing just the label */
112362306a36Sopenharmony_ci		if (!ifa) {
112462306a36Sopenharmony_ci			for (ifap = &in_dev->ifa_list;
112562306a36Sopenharmony_ci			     (ifa = rtnl_dereference(*ifap)) != NULL;
112662306a36Sopenharmony_ci			     ifap = &ifa->ifa_next)
112762306a36Sopenharmony_ci				if (!strcmp(ifr->ifr_name, ifa->ifa_label))
112862306a36Sopenharmony_ci					break;
112962306a36Sopenharmony_ci		}
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	ret = -EADDRNOTAVAIL;
113362306a36Sopenharmony_ci	if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
113462306a36Sopenharmony_ci		goto done;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	switch (cmd) {
113762306a36Sopenharmony_ci	case SIOCGIFADDR:	/* Get interface address */
113862306a36Sopenharmony_ci		ret = 0;
113962306a36Sopenharmony_ci		sin->sin_addr.s_addr = ifa->ifa_local;
114062306a36Sopenharmony_ci		break;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	case SIOCGIFBRDADDR:	/* Get the broadcast address */
114362306a36Sopenharmony_ci		ret = 0;
114462306a36Sopenharmony_ci		sin->sin_addr.s_addr = ifa->ifa_broadcast;
114562306a36Sopenharmony_ci		break;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	case SIOCGIFDSTADDR:	/* Get the destination address */
114862306a36Sopenharmony_ci		ret = 0;
114962306a36Sopenharmony_ci		sin->sin_addr.s_addr = ifa->ifa_address;
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	case SIOCGIFNETMASK:	/* Get the netmask for the interface */
115362306a36Sopenharmony_ci		ret = 0;
115462306a36Sopenharmony_ci		sin->sin_addr.s_addr = ifa->ifa_mask;
115562306a36Sopenharmony_ci		break;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	case SIOCSIFFLAGS:
115862306a36Sopenharmony_ci		if (colon) {
115962306a36Sopenharmony_ci			ret = -EADDRNOTAVAIL;
116062306a36Sopenharmony_ci			if (!ifa)
116162306a36Sopenharmony_ci				break;
116262306a36Sopenharmony_ci			ret = 0;
116362306a36Sopenharmony_ci			if (!(ifr->ifr_flags & IFF_UP))
116462306a36Sopenharmony_ci				inet_del_ifa(in_dev, ifap, 1);
116562306a36Sopenharmony_ci			break;
116662306a36Sopenharmony_ci		}
116762306a36Sopenharmony_ci		ret = dev_change_flags(dev, ifr->ifr_flags, NULL);
116862306a36Sopenharmony_ci		break;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	case SIOCSIFADDR:	/* Set interface address (and family) */
117162306a36Sopenharmony_ci		ret = -EINVAL;
117262306a36Sopenharmony_ci		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
117362306a36Sopenharmony_ci			break;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci		if (!ifa) {
117662306a36Sopenharmony_ci			ret = -ENOBUFS;
117762306a36Sopenharmony_ci			ifa = inet_alloc_ifa();
117862306a36Sopenharmony_ci			if (!ifa)
117962306a36Sopenharmony_ci				break;
118062306a36Sopenharmony_ci			INIT_HLIST_NODE(&ifa->hash);
118162306a36Sopenharmony_ci			if (colon)
118262306a36Sopenharmony_ci				memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ);
118362306a36Sopenharmony_ci			else
118462306a36Sopenharmony_ci				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
118562306a36Sopenharmony_ci		} else {
118662306a36Sopenharmony_ci			ret = 0;
118762306a36Sopenharmony_ci			if (ifa->ifa_local == sin->sin_addr.s_addr)
118862306a36Sopenharmony_ci				break;
118962306a36Sopenharmony_ci			inet_del_ifa(in_dev, ifap, 0);
119062306a36Sopenharmony_ci			ifa->ifa_broadcast = 0;
119162306a36Sopenharmony_ci			ifa->ifa_scope = 0;
119262306a36Sopenharmony_ci		}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		if (!(dev->flags & IFF_POINTOPOINT)) {
119762306a36Sopenharmony_ci			ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
119862306a36Sopenharmony_ci			ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
119962306a36Sopenharmony_ci			if ((dev->flags & IFF_BROADCAST) &&
120062306a36Sopenharmony_ci			    ifa->ifa_prefixlen < 31)
120162306a36Sopenharmony_ci				ifa->ifa_broadcast = ifa->ifa_address |
120262306a36Sopenharmony_ci						     ~ifa->ifa_mask;
120362306a36Sopenharmony_ci		} else {
120462306a36Sopenharmony_ci			ifa->ifa_prefixlen = 32;
120562306a36Sopenharmony_ci			ifa->ifa_mask = inet_make_mask(32);
120662306a36Sopenharmony_ci		}
120762306a36Sopenharmony_ci		set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
120862306a36Sopenharmony_ci		ret = inet_set_ifa(dev, ifa);
120962306a36Sopenharmony_ci		break;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	case SIOCSIFBRDADDR:	/* Set the broadcast address */
121262306a36Sopenharmony_ci		ret = 0;
121362306a36Sopenharmony_ci		if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
121462306a36Sopenharmony_ci			inet_del_ifa(in_dev, ifap, 0);
121562306a36Sopenharmony_ci			ifa->ifa_broadcast = sin->sin_addr.s_addr;
121662306a36Sopenharmony_ci			inet_insert_ifa(ifa);
121762306a36Sopenharmony_ci		}
121862306a36Sopenharmony_ci		break;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	case SIOCSIFDSTADDR:	/* Set the destination address */
122162306a36Sopenharmony_ci		ret = 0;
122262306a36Sopenharmony_ci		if (ifa->ifa_address == sin->sin_addr.s_addr)
122362306a36Sopenharmony_ci			break;
122462306a36Sopenharmony_ci		ret = -EINVAL;
122562306a36Sopenharmony_ci		if (inet_abc_len(sin->sin_addr.s_addr) < 0)
122662306a36Sopenharmony_ci			break;
122762306a36Sopenharmony_ci		ret = 0;
122862306a36Sopenharmony_ci		inet_del_ifa(in_dev, ifap, 0);
122962306a36Sopenharmony_ci		ifa->ifa_address = sin->sin_addr.s_addr;
123062306a36Sopenharmony_ci		inet_insert_ifa(ifa);
123162306a36Sopenharmony_ci		break;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci		/*
123662306a36Sopenharmony_ci		 *	The mask we set must be legal.
123762306a36Sopenharmony_ci		 */
123862306a36Sopenharmony_ci		ret = -EINVAL;
123962306a36Sopenharmony_ci		if (bad_mask(sin->sin_addr.s_addr, 0))
124062306a36Sopenharmony_ci			break;
124162306a36Sopenharmony_ci		ret = 0;
124262306a36Sopenharmony_ci		if (ifa->ifa_mask != sin->sin_addr.s_addr) {
124362306a36Sopenharmony_ci			__be32 old_mask = ifa->ifa_mask;
124462306a36Sopenharmony_ci			inet_del_ifa(in_dev, ifap, 0);
124562306a36Sopenharmony_ci			ifa->ifa_mask = sin->sin_addr.s_addr;
124662306a36Sopenharmony_ci			ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci			/* See if current broadcast address matches
124962306a36Sopenharmony_ci			 * with current netmask, then recalculate
125062306a36Sopenharmony_ci			 * the broadcast address. Otherwise it's a
125162306a36Sopenharmony_ci			 * funny address, so don't touch it since
125262306a36Sopenharmony_ci			 * the user seems to know what (s)he's doing...
125362306a36Sopenharmony_ci			 */
125462306a36Sopenharmony_ci			if ((dev->flags & IFF_BROADCAST) &&
125562306a36Sopenharmony_ci			    (ifa->ifa_prefixlen < 31) &&
125662306a36Sopenharmony_ci			    (ifa->ifa_broadcast ==
125762306a36Sopenharmony_ci			     (ifa->ifa_local|~old_mask))) {
125862306a36Sopenharmony_ci				ifa->ifa_broadcast = (ifa->ifa_local |
125962306a36Sopenharmony_ci						      ~sin->sin_addr.s_addr);
126062306a36Sopenharmony_ci			}
126162306a36Sopenharmony_ci			inet_insert_ifa(ifa);
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci		break;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_cidone:
126662306a36Sopenharmony_ci	rtnl_unlock();
126762306a36Sopenharmony_ciout:
126862306a36Sopenharmony_ci	return ret;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ciint inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	struct in_device *in_dev = __in_dev_get_rtnl(dev);
127462306a36Sopenharmony_ci	const struct in_ifaddr *ifa;
127562306a36Sopenharmony_ci	struct ifreq ifr;
127662306a36Sopenharmony_ci	int done = 0;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (WARN_ON(size > sizeof(struct ifreq)))
127962306a36Sopenharmony_ci		goto out;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (!in_dev)
128262306a36Sopenharmony_ci		goto out;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
128562306a36Sopenharmony_ci		if (!buf) {
128662306a36Sopenharmony_ci			done += size;
128762306a36Sopenharmony_ci			continue;
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci		if (len < size)
129062306a36Sopenharmony_ci			break;
129162306a36Sopenharmony_ci		memset(&ifr, 0, sizeof(struct ifreq));
129262306a36Sopenharmony_ci		strcpy(ifr.ifr_name, ifa->ifa_label);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
129562306a36Sopenharmony_ci		(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
129662306a36Sopenharmony_ci								ifa->ifa_local;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci		if (copy_to_user(buf + done, &ifr, size)) {
129962306a36Sopenharmony_ci			done = -EFAULT;
130062306a36Sopenharmony_ci			break;
130162306a36Sopenharmony_ci		}
130262306a36Sopenharmony_ci		len  -= size;
130362306a36Sopenharmony_ci		done += size;
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ciout:
130662306a36Sopenharmony_ci	return done;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic __be32 in_dev_select_addr(const struct in_device *in_dev,
131062306a36Sopenharmony_ci				 int scope)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	const struct in_ifaddr *ifa;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	in_dev_for_each_ifa_rcu(ifa, in_dev) {
131562306a36Sopenharmony_ci		if (ifa->ifa_flags & IFA_F_SECONDARY)
131662306a36Sopenharmony_ci			continue;
131762306a36Sopenharmony_ci		if (ifa->ifa_scope != RT_SCOPE_LINK &&
131862306a36Sopenharmony_ci		    ifa->ifa_scope <= scope)
131962306a36Sopenharmony_ci			return ifa->ifa_local;
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	return 0;
132362306a36Sopenharmony_ci}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
132662306a36Sopenharmony_ci{
132762306a36Sopenharmony_ci	const struct in_ifaddr *ifa;
132862306a36Sopenharmony_ci	__be32 addr = 0;
132962306a36Sopenharmony_ci	unsigned char localnet_scope = RT_SCOPE_HOST;
133062306a36Sopenharmony_ci	struct in_device *in_dev;
133162306a36Sopenharmony_ci	struct net *net = dev_net(dev);
133262306a36Sopenharmony_ci	int master_idx;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	rcu_read_lock();
133562306a36Sopenharmony_ci	in_dev = __in_dev_get_rcu(dev);
133662306a36Sopenharmony_ci	if (!in_dev)
133762306a36Sopenharmony_ci		goto no_in_dev;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
134062306a36Sopenharmony_ci		localnet_scope = RT_SCOPE_LINK;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	in_dev_for_each_ifa_rcu(ifa, in_dev) {
134362306a36Sopenharmony_ci		if (ifa->ifa_flags & IFA_F_SECONDARY)
134462306a36Sopenharmony_ci			continue;
134562306a36Sopenharmony_ci		if (min(ifa->ifa_scope, localnet_scope) > scope)
134662306a36Sopenharmony_ci			continue;
134762306a36Sopenharmony_ci		if (!dst || inet_ifa_match(dst, ifa)) {
134862306a36Sopenharmony_ci			addr = ifa->ifa_local;
134962306a36Sopenharmony_ci			break;
135062306a36Sopenharmony_ci		}
135162306a36Sopenharmony_ci		if (!addr)
135262306a36Sopenharmony_ci			addr = ifa->ifa_local;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (addr)
135662306a36Sopenharmony_ci		goto out_unlock;
135762306a36Sopenharmony_cino_in_dev:
135862306a36Sopenharmony_ci	master_idx = l3mdev_master_ifindex_rcu(dev);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	/* For VRFs, the VRF device takes the place of the loopback device,
136162306a36Sopenharmony_ci	 * with addresses on it being preferred.  Note in such cases the
136262306a36Sopenharmony_ci	 * loopback device will be among the devices that fail the master_idx
136362306a36Sopenharmony_ci	 * equality check in the loop below.
136462306a36Sopenharmony_ci	 */
136562306a36Sopenharmony_ci	if (master_idx &&
136662306a36Sopenharmony_ci	    (dev = dev_get_by_index_rcu(net, master_idx)) &&
136762306a36Sopenharmony_ci	    (in_dev = __in_dev_get_rcu(dev))) {
136862306a36Sopenharmony_ci		addr = in_dev_select_addr(in_dev, scope);
136962306a36Sopenharmony_ci		if (addr)
137062306a36Sopenharmony_ci			goto out_unlock;
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	/* Not loopback addresses on loopback should be preferred
137462306a36Sopenharmony_ci	   in this case. It is important that lo is the first interface
137562306a36Sopenharmony_ci	   in dev_base list.
137662306a36Sopenharmony_ci	 */
137762306a36Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
137862306a36Sopenharmony_ci		if (l3mdev_master_ifindex_rcu(dev) != master_idx)
137962306a36Sopenharmony_ci			continue;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		in_dev = __in_dev_get_rcu(dev);
138262306a36Sopenharmony_ci		if (!in_dev)
138362306a36Sopenharmony_ci			continue;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci		addr = in_dev_select_addr(in_dev, scope);
138662306a36Sopenharmony_ci		if (addr)
138762306a36Sopenharmony_ci			goto out_unlock;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ciout_unlock:
139062306a36Sopenharmony_ci	rcu_read_unlock();
139162306a36Sopenharmony_ci	return addr;
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ciEXPORT_SYMBOL(inet_select_addr);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_cistatic __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
139662306a36Sopenharmony_ci			      __be32 local, int scope)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	unsigned char localnet_scope = RT_SCOPE_HOST;
139962306a36Sopenharmony_ci	const struct in_ifaddr *ifa;
140062306a36Sopenharmony_ci	__be32 addr = 0;
140162306a36Sopenharmony_ci	int same = 0;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev)))
140462306a36Sopenharmony_ci		localnet_scope = RT_SCOPE_LINK;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	in_dev_for_each_ifa_rcu(ifa, in_dev) {
140762306a36Sopenharmony_ci		unsigned char min_scope = min(ifa->ifa_scope, localnet_scope);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		if (!addr &&
141062306a36Sopenharmony_ci		    (local == ifa->ifa_local || !local) &&
141162306a36Sopenharmony_ci		    min_scope <= scope) {
141262306a36Sopenharmony_ci			addr = ifa->ifa_local;
141362306a36Sopenharmony_ci			if (same)
141462306a36Sopenharmony_ci				break;
141562306a36Sopenharmony_ci		}
141662306a36Sopenharmony_ci		if (!same) {
141762306a36Sopenharmony_ci			same = (!local || inet_ifa_match(local, ifa)) &&
141862306a36Sopenharmony_ci				(!dst || inet_ifa_match(dst, ifa));
141962306a36Sopenharmony_ci			if (same && addr) {
142062306a36Sopenharmony_ci				if (local || !dst)
142162306a36Sopenharmony_ci					break;
142262306a36Sopenharmony_ci				/* Is the selected addr into dst subnet? */
142362306a36Sopenharmony_ci				if (inet_ifa_match(addr, ifa))
142462306a36Sopenharmony_ci					break;
142562306a36Sopenharmony_ci				/* No, then can we use new local src? */
142662306a36Sopenharmony_ci				if (min_scope <= scope) {
142762306a36Sopenharmony_ci					addr = ifa->ifa_local;
142862306a36Sopenharmony_ci					break;
142962306a36Sopenharmony_ci				}
143062306a36Sopenharmony_ci				/* search for large dst subnet for addr */
143162306a36Sopenharmony_ci				same = 0;
143262306a36Sopenharmony_ci			}
143362306a36Sopenharmony_ci		}
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	return same ? addr : 0;
143762306a36Sopenharmony_ci}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci/*
144062306a36Sopenharmony_ci * Confirm that local IP address exists using wildcards:
144162306a36Sopenharmony_ci * - net: netns to check, cannot be NULL
144262306a36Sopenharmony_ci * - in_dev: only on this interface, NULL=any interface
144362306a36Sopenharmony_ci * - dst: only in the same subnet as dst, 0=any dst
144462306a36Sopenharmony_ci * - local: address, 0=autoselect the local address
144562306a36Sopenharmony_ci * - scope: maximum allowed scope value for the local address
144662306a36Sopenharmony_ci */
144762306a36Sopenharmony_ci__be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
144862306a36Sopenharmony_ci			 __be32 dst, __be32 local, int scope)
144962306a36Sopenharmony_ci{
145062306a36Sopenharmony_ci	__be32 addr = 0;
145162306a36Sopenharmony_ci	struct net_device *dev;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	if (in_dev)
145462306a36Sopenharmony_ci		return confirm_addr_indev(in_dev, dst, local, scope);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	rcu_read_lock();
145762306a36Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
145862306a36Sopenharmony_ci		in_dev = __in_dev_get_rcu(dev);
145962306a36Sopenharmony_ci		if (in_dev) {
146062306a36Sopenharmony_ci			addr = confirm_addr_indev(in_dev, dst, local, scope);
146162306a36Sopenharmony_ci			if (addr)
146262306a36Sopenharmony_ci				break;
146362306a36Sopenharmony_ci		}
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci	rcu_read_unlock();
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return addr;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ciEXPORT_SYMBOL(inet_confirm_addr);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci/*
147262306a36Sopenharmony_ci *	Device notifier
147362306a36Sopenharmony_ci */
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ciint register_inetaddr_notifier(struct notifier_block *nb)
147662306a36Sopenharmony_ci{
147762306a36Sopenharmony_ci	return blocking_notifier_chain_register(&inetaddr_chain, nb);
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ciEXPORT_SYMBOL(register_inetaddr_notifier);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ciint unregister_inetaddr_notifier(struct notifier_block *nb)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_inetaddr_notifier);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ciint register_inetaddr_validator_notifier(struct notifier_block *nb)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ciEXPORT_SYMBOL(register_inetaddr_validator_notifier);
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ciint unregister_inetaddr_validator_notifier(struct notifier_block *nb)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
149662306a36Sopenharmony_ci	    nb);
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci/* Rename ifa_labels for a device name change. Make some effort to preserve
150162306a36Sopenharmony_ci * existing alias numbering and to create unique labels if possible.
150262306a36Sopenharmony_ci*/
150362306a36Sopenharmony_cistatic void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct in_ifaddr *ifa;
150662306a36Sopenharmony_ci	int named = 0;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
150962306a36Sopenharmony_ci		char old[IFNAMSIZ], *dot;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci		memcpy(old, ifa->ifa_label, IFNAMSIZ);
151262306a36Sopenharmony_ci		memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
151362306a36Sopenharmony_ci		if (named++ == 0)
151462306a36Sopenharmony_ci			goto skip;
151562306a36Sopenharmony_ci		dot = strchr(old, ':');
151662306a36Sopenharmony_ci		if (!dot) {
151762306a36Sopenharmony_ci			sprintf(old, ":%d", named);
151862306a36Sopenharmony_ci			dot = old;
151962306a36Sopenharmony_ci		}
152062306a36Sopenharmony_ci		if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
152162306a36Sopenharmony_ci			strcat(ifa->ifa_label, dot);
152262306a36Sopenharmony_ci		else
152362306a36Sopenharmony_ci			strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
152462306a36Sopenharmony_ciskip:
152562306a36Sopenharmony_ci		rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_cistatic void inetdev_send_gratuitous_arp(struct net_device *dev,
153062306a36Sopenharmony_ci					struct in_device *in_dev)
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	const struct in_ifaddr *ifa;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
153662306a36Sopenharmony_ci		arp_send(ARPOP_REQUEST, ETH_P_ARP,
153762306a36Sopenharmony_ci			 ifa->ifa_local, dev,
153862306a36Sopenharmony_ci			 ifa->ifa_local, NULL,
153962306a36Sopenharmony_ci			 dev->dev_addr, NULL);
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci/* Called only under RTNL semaphore */
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic int inetdev_event(struct notifier_block *this, unsigned long event,
154662306a36Sopenharmony_ci			 void *ptr)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
154962306a36Sopenharmony_ci	struct in_device *in_dev = __in_dev_get_rtnl(dev);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	ASSERT_RTNL();
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	if (!in_dev) {
155462306a36Sopenharmony_ci		if (event == NETDEV_REGISTER) {
155562306a36Sopenharmony_ci			in_dev = inetdev_init(dev);
155662306a36Sopenharmony_ci			if (IS_ERR(in_dev))
155762306a36Sopenharmony_ci				return notifier_from_errno(PTR_ERR(in_dev));
155862306a36Sopenharmony_ci			if (dev->flags & IFF_LOOPBACK) {
155962306a36Sopenharmony_ci				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
156062306a36Sopenharmony_ci				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
156162306a36Sopenharmony_ci			}
156262306a36Sopenharmony_ci		} else if (event == NETDEV_CHANGEMTU) {
156362306a36Sopenharmony_ci			/* Re-enabling IP */
156462306a36Sopenharmony_ci			if (inetdev_valid_mtu(dev->mtu))
156562306a36Sopenharmony_ci				in_dev = inetdev_init(dev);
156662306a36Sopenharmony_ci		}
156762306a36Sopenharmony_ci		goto out;
156862306a36Sopenharmony_ci	}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	switch (event) {
157162306a36Sopenharmony_ci	case NETDEV_REGISTER:
157262306a36Sopenharmony_ci		pr_debug("%s: bug\n", __func__);
157362306a36Sopenharmony_ci		RCU_INIT_POINTER(dev->ip_ptr, NULL);
157462306a36Sopenharmony_ci		break;
157562306a36Sopenharmony_ci	case NETDEV_UP:
157662306a36Sopenharmony_ci		if (!inetdev_valid_mtu(dev->mtu))
157762306a36Sopenharmony_ci			break;
157862306a36Sopenharmony_ci		if (dev->flags & IFF_LOOPBACK) {
157962306a36Sopenharmony_ci			struct in_ifaddr *ifa = inet_alloc_ifa();
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci			if (ifa) {
158262306a36Sopenharmony_ci				INIT_HLIST_NODE(&ifa->hash);
158362306a36Sopenharmony_ci				ifa->ifa_local =
158462306a36Sopenharmony_ci				  ifa->ifa_address = htonl(INADDR_LOOPBACK);
158562306a36Sopenharmony_ci				ifa->ifa_prefixlen = 8;
158662306a36Sopenharmony_ci				ifa->ifa_mask = inet_make_mask(8);
158762306a36Sopenharmony_ci				in_dev_hold(in_dev);
158862306a36Sopenharmony_ci				ifa->ifa_dev = in_dev;
158962306a36Sopenharmony_ci				ifa->ifa_scope = RT_SCOPE_HOST;
159062306a36Sopenharmony_ci				memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
159162306a36Sopenharmony_ci				set_ifa_lifetime(ifa, INFINITY_LIFE_TIME,
159262306a36Sopenharmony_ci						 INFINITY_LIFE_TIME);
159362306a36Sopenharmony_ci				ipv4_devconf_setall(in_dev);
159462306a36Sopenharmony_ci				neigh_parms_data_state_setall(in_dev->arp_parms);
159562306a36Sopenharmony_ci				inet_insert_ifa(ifa);
159662306a36Sopenharmony_ci			}
159762306a36Sopenharmony_ci		}
159862306a36Sopenharmony_ci		ip_mc_up(in_dev);
159962306a36Sopenharmony_ci		fallthrough;
160062306a36Sopenharmony_ci	case NETDEV_CHANGEADDR:
160162306a36Sopenharmony_ci		if (!IN_DEV_ARP_NOTIFY(in_dev))
160262306a36Sopenharmony_ci			break;
160362306a36Sopenharmony_ci		fallthrough;
160462306a36Sopenharmony_ci	case NETDEV_NOTIFY_PEERS:
160562306a36Sopenharmony_ci		/* Send gratuitous ARP to notify of link change */
160662306a36Sopenharmony_ci		inetdev_send_gratuitous_arp(dev, in_dev);
160762306a36Sopenharmony_ci		break;
160862306a36Sopenharmony_ci	case NETDEV_DOWN:
160962306a36Sopenharmony_ci		ip_mc_down(in_dev);
161062306a36Sopenharmony_ci		break;
161162306a36Sopenharmony_ci	case NETDEV_PRE_TYPE_CHANGE:
161262306a36Sopenharmony_ci		ip_mc_unmap(in_dev);
161362306a36Sopenharmony_ci		break;
161462306a36Sopenharmony_ci	case NETDEV_POST_TYPE_CHANGE:
161562306a36Sopenharmony_ci		ip_mc_remap(in_dev);
161662306a36Sopenharmony_ci		break;
161762306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
161862306a36Sopenharmony_ci		if (inetdev_valid_mtu(dev->mtu))
161962306a36Sopenharmony_ci			break;
162062306a36Sopenharmony_ci		/* disable IP when MTU is not enough */
162162306a36Sopenharmony_ci		fallthrough;
162262306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
162362306a36Sopenharmony_ci		inetdev_destroy(in_dev);
162462306a36Sopenharmony_ci		break;
162562306a36Sopenharmony_ci	case NETDEV_CHANGENAME:
162662306a36Sopenharmony_ci		/* Do not notify about label change, this event is
162762306a36Sopenharmony_ci		 * not interesting to applications using netlink.
162862306a36Sopenharmony_ci		 */
162962306a36Sopenharmony_ci		inetdev_changename(dev, in_dev);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci		devinet_sysctl_unregister(in_dev);
163262306a36Sopenharmony_ci		devinet_sysctl_register(in_dev);
163362306a36Sopenharmony_ci		break;
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ciout:
163662306a36Sopenharmony_ci	return NOTIFY_DONE;
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_cistatic struct notifier_block ip_netdev_notifier = {
164062306a36Sopenharmony_ci	.notifier_call = inetdev_event,
164162306a36Sopenharmony_ci};
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic size_t inet_nlmsg_size(void)
164462306a36Sopenharmony_ci{
164562306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
164662306a36Sopenharmony_ci	       + nla_total_size(4) /* IFA_ADDRESS */
164762306a36Sopenharmony_ci	       + nla_total_size(4) /* IFA_LOCAL */
164862306a36Sopenharmony_ci	       + nla_total_size(4) /* IFA_BROADCAST */
164962306a36Sopenharmony_ci	       + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
165062306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFA_FLAGS */
165162306a36Sopenharmony_ci	       + nla_total_size(1)  /* IFA_PROTO */
165262306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFA_RT_PRIORITY */
165362306a36Sopenharmony_ci	       + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic inline u32 cstamp_delta(unsigned long cstamp)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
165962306a36Sopenharmony_ci}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_cistatic int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
166262306a36Sopenharmony_ci			 unsigned long tstamp, u32 preferred, u32 valid)
166362306a36Sopenharmony_ci{
166462306a36Sopenharmony_ci	struct ifa_cacheinfo ci;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	ci.cstamp = cstamp_delta(cstamp);
166762306a36Sopenharmony_ci	ci.tstamp = cstamp_delta(tstamp);
166862306a36Sopenharmony_ci	ci.ifa_prefered = preferred;
166962306a36Sopenharmony_ci	ci.ifa_valid = valid;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
167262306a36Sopenharmony_ci}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_cistatic int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
167562306a36Sopenharmony_ci			    struct inet_fill_args *args)
167662306a36Sopenharmony_ci{
167762306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
167862306a36Sopenharmony_ci	struct nlmsghdr  *nlh;
167962306a36Sopenharmony_ci	u32 preferred, valid;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm),
168262306a36Sopenharmony_ci			args->flags);
168362306a36Sopenharmony_ci	if (!nlh)
168462306a36Sopenharmony_ci		return -EMSGSIZE;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
168762306a36Sopenharmony_ci	ifm->ifa_family = AF_INET;
168862306a36Sopenharmony_ci	ifm->ifa_prefixlen = ifa->ifa_prefixlen;
168962306a36Sopenharmony_ci	ifm->ifa_flags = ifa->ifa_flags;
169062306a36Sopenharmony_ci	ifm->ifa_scope = ifa->ifa_scope;
169162306a36Sopenharmony_ci	ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (args->netnsid >= 0 &&
169462306a36Sopenharmony_ci	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
169562306a36Sopenharmony_ci		goto nla_put_failure;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	if (!(ifm->ifa_flags & IFA_F_PERMANENT)) {
169862306a36Sopenharmony_ci		preferred = ifa->ifa_preferred_lft;
169962306a36Sopenharmony_ci		valid = ifa->ifa_valid_lft;
170062306a36Sopenharmony_ci		if (preferred != INFINITY_LIFE_TIME) {
170162306a36Sopenharmony_ci			long tval = (jiffies - ifa->ifa_tstamp) / HZ;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci			if (preferred > tval)
170462306a36Sopenharmony_ci				preferred -= tval;
170562306a36Sopenharmony_ci			else
170662306a36Sopenharmony_ci				preferred = 0;
170762306a36Sopenharmony_ci			if (valid != INFINITY_LIFE_TIME) {
170862306a36Sopenharmony_ci				if (valid > tval)
170962306a36Sopenharmony_ci					valid -= tval;
171062306a36Sopenharmony_ci				else
171162306a36Sopenharmony_ci					valid = 0;
171262306a36Sopenharmony_ci			}
171362306a36Sopenharmony_ci		}
171462306a36Sopenharmony_ci	} else {
171562306a36Sopenharmony_ci		preferred = INFINITY_LIFE_TIME;
171662306a36Sopenharmony_ci		valid = INFINITY_LIFE_TIME;
171762306a36Sopenharmony_ci	}
171862306a36Sopenharmony_ci	if ((ifa->ifa_address &&
171962306a36Sopenharmony_ci	     nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) ||
172062306a36Sopenharmony_ci	    (ifa->ifa_local &&
172162306a36Sopenharmony_ci	     nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) ||
172262306a36Sopenharmony_ci	    (ifa->ifa_broadcast &&
172362306a36Sopenharmony_ci	     nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
172462306a36Sopenharmony_ci	    (ifa->ifa_label[0] &&
172562306a36Sopenharmony_ci	     nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
172662306a36Sopenharmony_ci	    (ifa->ifa_proto &&
172762306a36Sopenharmony_ci	     nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
172862306a36Sopenharmony_ci	    nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
172962306a36Sopenharmony_ci	    (ifa->ifa_rt_priority &&
173062306a36Sopenharmony_ci	     nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
173162306a36Sopenharmony_ci	    put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
173262306a36Sopenharmony_ci			  preferred, valid))
173362306a36Sopenharmony_ci		goto nla_put_failure;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
173662306a36Sopenharmony_ci	return 0;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cinla_put_failure:
173962306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
174062306a36Sopenharmony_ci	return -EMSGSIZE;
174162306a36Sopenharmony_ci}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_cistatic int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
174462306a36Sopenharmony_ci				      struct inet_fill_args *fillargs,
174562306a36Sopenharmony_ci				      struct net **tgt_net, struct sock *sk,
174662306a36Sopenharmony_ci				      struct netlink_callback *cb)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	struct netlink_ext_ack *extack = cb->extack;
174962306a36Sopenharmony_ci	struct nlattr *tb[IFA_MAX+1];
175062306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
175162306a36Sopenharmony_ci	int err, i;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
175462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request");
175562306a36Sopenharmony_ci		return -EINVAL;
175662306a36Sopenharmony_ci	}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
175962306a36Sopenharmony_ci	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
176062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request");
176162306a36Sopenharmony_ci		return -EINVAL;
176262306a36Sopenharmony_ci	}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	fillargs->ifindex = ifm->ifa_index;
176562306a36Sopenharmony_ci	if (fillargs->ifindex) {
176662306a36Sopenharmony_ci		cb->answer_flags |= NLM_F_DUMP_FILTERED;
176762306a36Sopenharmony_ci		fillargs->flags |= NLM_F_DUMP_FILTERED;
176862306a36Sopenharmony_ci	}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
177162306a36Sopenharmony_ci					    ifa_ipv4_policy, extack);
177262306a36Sopenharmony_ci	if (err < 0)
177362306a36Sopenharmony_ci		return err;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	for (i = 0; i <= IFA_MAX; ++i) {
177662306a36Sopenharmony_ci		if (!tb[i])
177762306a36Sopenharmony_ci			continue;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci		if (i == IFA_TARGET_NETNSID) {
178062306a36Sopenharmony_ci			struct net *net;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci			fillargs->netnsid = nla_get_s32(tb[i]);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
178562306a36Sopenharmony_ci			if (IS_ERR(net)) {
178662306a36Sopenharmony_ci				fillargs->netnsid = -1;
178762306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id");
178862306a36Sopenharmony_ci				return PTR_ERR(net);
178962306a36Sopenharmony_ci			}
179062306a36Sopenharmony_ci			*tgt_net = net;
179162306a36Sopenharmony_ci		} else {
179262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request");
179362306a36Sopenharmony_ci			return -EINVAL;
179462306a36Sopenharmony_ci		}
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	return 0;
179862306a36Sopenharmony_ci}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cistatic int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb,
180162306a36Sopenharmony_ci			    struct netlink_callback *cb, int s_ip_idx,
180262306a36Sopenharmony_ci			    struct inet_fill_args *fillargs)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	struct in_ifaddr *ifa;
180562306a36Sopenharmony_ci	int ip_idx = 0;
180662306a36Sopenharmony_ci	int err;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	in_dev_for_each_ifa_rtnl(ifa, in_dev) {
180962306a36Sopenharmony_ci		if (ip_idx < s_ip_idx) {
181062306a36Sopenharmony_ci			ip_idx++;
181162306a36Sopenharmony_ci			continue;
181262306a36Sopenharmony_ci		}
181362306a36Sopenharmony_ci		err = inet_fill_ifaddr(skb, ifa, fillargs);
181462306a36Sopenharmony_ci		if (err < 0)
181562306a36Sopenharmony_ci			goto done;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci		nl_dump_check_consistent(cb, nlmsg_hdr(skb));
181862306a36Sopenharmony_ci		ip_idx++;
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci	err = 0;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_cidone:
182362306a36Sopenharmony_ci	cb->args[2] = ip_idx;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	return err;
182662306a36Sopenharmony_ci}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci/* Combine dev_addr_genid and dev_base_seq to detect changes.
182962306a36Sopenharmony_ci */
183062306a36Sopenharmony_cistatic u32 inet_base_seq(const struct net *net)
183162306a36Sopenharmony_ci{
183262306a36Sopenharmony_ci	u32 res = atomic_read(&net->ipv4.dev_addr_genid) +
183362306a36Sopenharmony_ci		  net->dev_base_seq;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	/* Must not return 0 (see nl_dump_check_consistent()).
183662306a36Sopenharmony_ci	 * Chose a value far away from 0.
183762306a36Sopenharmony_ci	 */
183862306a36Sopenharmony_ci	if (!res)
183962306a36Sopenharmony_ci		res = 0x80000000;
184062306a36Sopenharmony_ci	return res;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_cistatic int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
184662306a36Sopenharmony_ci	struct inet_fill_args fillargs = {
184762306a36Sopenharmony_ci		.portid = NETLINK_CB(cb->skb).portid,
184862306a36Sopenharmony_ci		.seq = nlh->nlmsg_seq,
184962306a36Sopenharmony_ci		.event = RTM_NEWADDR,
185062306a36Sopenharmony_ci		.flags = NLM_F_MULTI,
185162306a36Sopenharmony_ci		.netnsid = -1,
185262306a36Sopenharmony_ci	};
185362306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
185462306a36Sopenharmony_ci	struct net *tgt_net = net;
185562306a36Sopenharmony_ci	int h, s_h;
185662306a36Sopenharmony_ci	int idx, s_idx;
185762306a36Sopenharmony_ci	int s_ip_idx;
185862306a36Sopenharmony_ci	struct net_device *dev;
185962306a36Sopenharmony_ci	struct in_device *in_dev;
186062306a36Sopenharmony_ci	struct hlist_head *head;
186162306a36Sopenharmony_ci	int err = 0;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	s_h = cb->args[0];
186462306a36Sopenharmony_ci	s_idx = idx = cb->args[1];
186562306a36Sopenharmony_ci	s_ip_idx = cb->args[2];
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	if (cb->strict_check) {
186862306a36Sopenharmony_ci		err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
186962306a36Sopenharmony_ci						 skb->sk, cb);
187062306a36Sopenharmony_ci		if (err < 0)
187162306a36Sopenharmony_ci			goto put_tgt_net;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci		err = 0;
187462306a36Sopenharmony_ci		if (fillargs.ifindex) {
187562306a36Sopenharmony_ci			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
187662306a36Sopenharmony_ci			if (!dev) {
187762306a36Sopenharmony_ci				err = -ENODEV;
187862306a36Sopenharmony_ci				goto put_tgt_net;
187962306a36Sopenharmony_ci			}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci			in_dev = __in_dev_get_rtnl(dev);
188262306a36Sopenharmony_ci			if (in_dev) {
188362306a36Sopenharmony_ci				err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
188462306a36Sopenharmony_ci						       &fillargs);
188562306a36Sopenharmony_ci			}
188662306a36Sopenharmony_ci			goto put_tgt_net;
188762306a36Sopenharmony_ci		}
188862306a36Sopenharmony_ci	}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
189162306a36Sopenharmony_ci		idx = 0;
189262306a36Sopenharmony_ci		head = &tgt_net->dev_index_head[h];
189362306a36Sopenharmony_ci		rcu_read_lock();
189462306a36Sopenharmony_ci		cb->seq = inet_base_seq(tgt_net);
189562306a36Sopenharmony_ci		hlist_for_each_entry_rcu(dev, head, index_hlist) {
189662306a36Sopenharmony_ci			if (idx < s_idx)
189762306a36Sopenharmony_ci				goto cont;
189862306a36Sopenharmony_ci			if (h > s_h || idx > s_idx)
189962306a36Sopenharmony_ci				s_ip_idx = 0;
190062306a36Sopenharmony_ci			in_dev = __in_dev_get_rcu(dev);
190162306a36Sopenharmony_ci			if (!in_dev)
190262306a36Sopenharmony_ci				goto cont;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci			err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx,
190562306a36Sopenharmony_ci					       &fillargs);
190662306a36Sopenharmony_ci			if (err < 0) {
190762306a36Sopenharmony_ci				rcu_read_unlock();
190862306a36Sopenharmony_ci				goto done;
190962306a36Sopenharmony_ci			}
191062306a36Sopenharmony_cicont:
191162306a36Sopenharmony_ci			idx++;
191262306a36Sopenharmony_ci		}
191362306a36Sopenharmony_ci		rcu_read_unlock();
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_cidone:
191762306a36Sopenharmony_ci	cb->args[0] = h;
191862306a36Sopenharmony_ci	cb->args[1] = idx;
191962306a36Sopenharmony_ciput_tgt_net:
192062306a36Sopenharmony_ci	if (fillargs.netnsid >= 0)
192162306a36Sopenharmony_ci		put_net(tgt_net);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	return skb->len ? : err;
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_cistatic void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
192762306a36Sopenharmony_ci		      u32 portid)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci	struct inet_fill_args fillargs = {
193062306a36Sopenharmony_ci		.portid = portid,
193162306a36Sopenharmony_ci		.seq = nlh ? nlh->nlmsg_seq : 0,
193262306a36Sopenharmony_ci		.event = event,
193362306a36Sopenharmony_ci		.flags = 0,
193462306a36Sopenharmony_ci		.netnsid = -1,
193562306a36Sopenharmony_ci	};
193662306a36Sopenharmony_ci	struct sk_buff *skb;
193762306a36Sopenharmony_ci	int err = -ENOBUFS;
193862306a36Sopenharmony_ci	struct net *net;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	net = dev_net(ifa->ifa_dev->dev);
194162306a36Sopenharmony_ci	skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
194262306a36Sopenharmony_ci	if (!skb)
194362306a36Sopenharmony_ci		goto errout;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	err = inet_fill_ifaddr(skb, ifa, &fillargs);
194662306a36Sopenharmony_ci	if (err < 0) {
194762306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet_nlmsg_size() */
194862306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
194962306a36Sopenharmony_ci		kfree_skb(skb);
195062306a36Sopenharmony_ci		goto errout;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci	rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
195362306a36Sopenharmony_ci	return;
195462306a36Sopenharmony_cierrout:
195562306a36Sopenharmony_ci	if (err < 0)
195662306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_cistatic size_t inet_get_link_af_size(const struct net_device *dev,
196062306a36Sopenharmony_ci				    u32 ext_filter_mask)
196162306a36Sopenharmony_ci{
196262306a36Sopenharmony_ci	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	if (!in_dev)
196562306a36Sopenharmony_ci		return 0;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
196862306a36Sopenharmony_ci}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_cistatic int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
197162306a36Sopenharmony_ci			     u32 ext_filter_mask)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
197462306a36Sopenharmony_ci	struct nlattr *nla;
197562306a36Sopenharmony_ci	int i;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	if (!in_dev)
197862306a36Sopenharmony_ci		return -ENODATA;
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
198162306a36Sopenharmony_ci	if (!nla)
198262306a36Sopenharmony_ci		return -EMSGSIZE;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	for (i = 0; i < IPV4_DEVCONF_MAX; i++)
198562306a36Sopenharmony_ci		((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	return 0;
198862306a36Sopenharmony_ci}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_cistatic const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
199162306a36Sopenharmony_ci	[IFLA_INET_CONF]	= { .type = NLA_NESTED },
199262306a36Sopenharmony_ci};
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_cistatic int inet_validate_link_af(const struct net_device *dev,
199562306a36Sopenharmony_ci				 const struct nlattr *nla,
199662306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
199762306a36Sopenharmony_ci{
199862306a36Sopenharmony_ci	struct nlattr *a, *tb[IFLA_INET_MAX+1];
199962306a36Sopenharmony_ci	int err, rem;
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	if (dev && !__in_dev_get_rtnl(dev))
200262306a36Sopenharmony_ci		return -EAFNOSUPPORT;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla,
200562306a36Sopenharmony_ci					  inet_af_policy, extack);
200662306a36Sopenharmony_ci	if (err < 0)
200762306a36Sopenharmony_ci		return err;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	if (tb[IFLA_INET_CONF]) {
201062306a36Sopenharmony_ci		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
201162306a36Sopenharmony_ci			int cfgid = nla_type(a);
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci			if (nla_len(a) < 4)
201462306a36Sopenharmony_ci				return -EINVAL;
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci			if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
201762306a36Sopenharmony_ci				return -EINVAL;
201862306a36Sopenharmony_ci		}
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	return 0;
202262306a36Sopenharmony_ci}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_cistatic int inet_set_link_af(struct net_device *dev, const struct nlattr *nla,
202562306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
202662306a36Sopenharmony_ci{
202762306a36Sopenharmony_ci	struct in_device *in_dev = __in_dev_get_rtnl(dev);
202862306a36Sopenharmony_ci	struct nlattr *a, *tb[IFLA_INET_MAX+1];
202962306a36Sopenharmony_ci	int rem;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	if (!in_dev)
203262306a36Sopenharmony_ci		return -EAFNOSUPPORT;
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0)
203562306a36Sopenharmony_ci		return -EINVAL;
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	if (tb[IFLA_INET_CONF]) {
203862306a36Sopenharmony_ci		nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
203962306a36Sopenharmony_ci			ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
204062306a36Sopenharmony_ci	}
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	return 0;
204362306a36Sopenharmony_ci}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_cistatic int inet_netconf_msgsize_devconf(int type)
204662306a36Sopenharmony_ci{
204762306a36Sopenharmony_ci	int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
204862306a36Sopenharmony_ci		   + nla_total_size(4);	/* NETCONFA_IFINDEX */
204962306a36Sopenharmony_ci	bool all = false;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (type == NETCONFA_ALL)
205262306a36Sopenharmony_ci		all = true;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	if (all || type == NETCONFA_FORWARDING)
205562306a36Sopenharmony_ci		size += nla_total_size(4);
205662306a36Sopenharmony_ci	if (all || type == NETCONFA_RP_FILTER)
205762306a36Sopenharmony_ci		size += nla_total_size(4);
205862306a36Sopenharmony_ci	if (all || type == NETCONFA_MC_FORWARDING)
205962306a36Sopenharmony_ci		size += nla_total_size(4);
206062306a36Sopenharmony_ci	if (all || type == NETCONFA_BC_FORWARDING)
206162306a36Sopenharmony_ci		size += nla_total_size(4);
206262306a36Sopenharmony_ci	if (all || type == NETCONFA_PROXY_NEIGH)
206362306a36Sopenharmony_ci		size += nla_total_size(4);
206462306a36Sopenharmony_ci	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
206562306a36Sopenharmony_ci		size += nla_total_size(4);
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	return size;
206862306a36Sopenharmony_ci}
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_cistatic int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
207162306a36Sopenharmony_ci				     struct ipv4_devconf *devconf, u32 portid,
207262306a36Sopenharmony_ci				     u32 seq, int event, unsigned int flags,
207362306a36Sopenharmony_ci				     int type)
207462306a36Sopenharmony_ci{
207562306a36Sopenharmony_ci	struct nlmsghdr  *nlh;
207662306a36Sopenharmony_ci	struct netconfmsg *ncm;
207762306a36Sopenharmony_ci	bool all = false;
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
208062306a36Sopenharmony_ci			flags);
208162306a36Sopenharmony_ci	if (!nlh)
208262306a36Sopenharmony_ci		return -EMSGSIZE;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	if (type == NETCONFA_ALL)
208562306a36Sopenharmony_ci		all = true;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	ncm = nlmsg_data(nlh);
208862306a36Sopenharmony_ci	ncm->ncm_family = AF_INET;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
209162306a36Sopenharmony_ci		goto nla_put_failure;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	if (!devconf)
209462306a36Sopenharmony_ci		goto out;
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	if ((all || type == NETCONFA_FORWARDING) &&
209762306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_FORWARDING,
209862306a36Sopenharmony_ci			IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
209962306a36Sopenharmony_ci		goto nla_put_failure;
210062306a36Sopenharmony_ci	if ((all || type == NETCONFA_RP_FILTER) &&
210162306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_RP_FILTER,
210262306a36Sopenharmony_ci			IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
210362306a36Sopenharmony_ci		goto nla_put_failure;
210462306a36Sopenharmony_ci	if ((all || type == NETCONFA_MC_FORWARDING) &&
210562306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
210662306a36Sopenharmony_ci			IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
210762306a36Sopenharmony_ci		goto nla_put_failure;
210862306a36Sopenharmony_ci	if ((all || type == NETCONFA_BC_FORWARDING) &&
210962306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_BC_FORWARDING,
211062306a36Sopenharmony_ci			IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0)
211162306a36Sopenharmony_ci		goto nla_put_failure;
211262306a36Sopenharmony_ci	if ((all || type == NETCONFA_PROXY_NEIGH) &&
211362306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
211462306a36Sopenharmony_ci			IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
211562306a36Sopenharmony_ci		goto nla_put_failure;
211662306a36Sopenharmony_ci	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
211762306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
211862306a36Sopenharmony_ci			IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
211962306a36Sopenharmony_ci		goto nla_put_failure;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ciout:
212262306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
212362306a36Sopenharmony_ci	return 0;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_cinla_put_failure:
212662306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
212762306a36Sopenharmony_ci	return -EMSGSIZE;
212862306a36Sopenharmony_ci}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_civoid inet_netconf_notify_devconf(struct net *net, int event, int type,
213162306a36Sopenharmony_ci				 int ifindex, struct ipv4_devconf *devconf)
213262306a36Sopenharmony_ci{
213362306a36Sopenharmony_ci	struct sk_buff *skb;
213462306a36Sopenharmony_ci	int err = -ENOBUFS;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL);
213762306a36Sopenharmony_ci	if (!skb)
213862306a36Sopenharmony_ci		goto errout;
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci	err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
214162306a36Sopenharmony_ci					event, 0, type);
214262306a36Sopenharmony_ci	if (err < 0) {
214362306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
214462306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
214562306a36Sopenharmony_ci		kfree_skb(skb);
214662306a36Sopenharmony_ci		goto errout;
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL);
214962306a36Sopenharmony_ci	return;
215062306a36Sopenharmony_cierrout:
215162306a36Sopenharmony_ci	if (err < 0)
215262306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
215362306a36Sopenharmony_ci}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_cistatic const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
215662306a36Sopenharmony_ci	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
215762306a36Sopenharmony_ci	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
215862306a36Sopenharmony_ci	[NETCONFA_RP_FILTER]	= { .len = sizeof(int) },
215962306a36Sopenharmony_ci	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
216062306a36Sopenharmony_ci	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
216162306a36Sopenharmony_ci};
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_cistatic int inet_netconf_valid_get_req(struct sk_buff *skb,
216462306a36Sopenharmony_ci				      const struct nlmsghdr *nlh,
216562306a36Sopenharmony_ci				      struct nlattr **tb,
216662306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
216762306a36Sopenharmony_ci{
216862306a36Sopenharmony_ci	int i, err;
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
217162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request");
217262306a36Sopenharmony_ci		return -EINVAL;
217362306a36Sopenharmony_ci	}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	if (!netlink_strict_get_check(skb))
217662306a36Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
217762306a36Sopenharmony_ci					      tb, NETCONFA_MAX,
217862306a36Sopenharmony_ci					      devconf_ipv4_policy, extack);
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
218162306a36Sopenharmony_ci					    tb, NETCONFA_MAX,
218262306a36Sopenharmony_ci					    devconf_ipv4_policy, extack);
218362306a36Sopenharmony_ci	if (err)
218462306a36Sopenharmony_ci		return err;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	for (i = 0; i <= NETCONFA_MAX; i++) {
218762306a36Sopenharmony_ci		if (!tb[i])
218862306a36Sopenharmony_ci			continue;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci		switch (i) {
219162306a36Sopenharmony_ci		case NETCONFA_IFINDEX:
219262306a36Sopenharmony_ci			break;
219362306a36Sopenharmony_ci		default:
219462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request");
219562306a36Sopenharmony_ci			return -EINVAL;
219662306a36Sopenharmony_ci		}
219762306a36Sopenharmony_ci	}
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	return 0;
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_cistatic int inet_netconf_get_devconf(struct sk_buff *in_skb,
220362306a36Sopenharmony_ci				    struct nlmsghdr *nlh,
220462306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
220562306a36Sopenharmony_ci{
220662306a36Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
220762306a36Sopenharmony_ci	struct nlattr *tb[NETCONFA_MAX+1];
220862306a36Sopenharmony_ci	struct sk_buff *skb;
220962306a36Sopenharmony_ci	struct ipv4_devconf *devconf;
221062306a36Sopenharmony_ci	struct in_device *in_dev;
221162306a36Sopenharmony_ci	struct net_device *dev;
221262306a36Sopenharmony_ci	int ifindex;
221362306a36Sopenharmony_ci	int err;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack);
221662306a36Sopenharmony_ci	if (err)
221762306a36Sopenharmony_ci		goto errout;
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	err = -EINVAL;
222062306a36Sopenharmony_ci	if (!tb[NETCONFA_IFINDEX])
222162306a36Sopenharmony_ci		goto errout;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
222462306a36Sopenharmony_ci	switch (ifindex) {
222562306a36Sopenharmony_ci	case NETCONFA_IFINDEX_ALL:
222662306a36Sopenharmony_ci		devconf = net->ipv4.devconf_all;
222762306a36Sopenharmony_ci		break;
222862306a36Sopenharmony_ci	case NETCONFA_IFINDEX_DEFAULT:
222962306a36Sopenharmony_ci		devconf = net->ipv4.devconf_dflt;
223062306a36Sopenharmony_ci		break;
223162306a36Sopenharmony_ci	default:
223262306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ifindex);
223362306a36Sopenharmony_ci		if (!dev)
223462306a36Sopenharmony_ci			goto errout;
223562306a36Sopenharmony_ci		in_dev = __in_dev_get_rtnl(dev);
223662306a36Sopenharmony_ci		if (!in_dev)
223762306a36Sopenharmony_ci			goto errout;
223862306a36Sopenharmony_ci		devconf = &in_dev->cnf;
223962306a36Sopenharmony_ci		break;
224062306a36Sopenharmony_ci	}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	err = -ENOBUFS;
224362306a36Sopenharmony_ci	skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
224462306a36Sopenharmony_ci	if (!skb)
224562306a36Sopenharmony_ci		goto errout;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	err = inet_netconf_fill_devconf(skb, ifindex, devconf,
224862306a36Sopenharmony_ci					NETLINK_CB(in_skb).portid,
224962306a36Sopenharmony_ci					nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
225062306a36Sopenharmony_ci					NETCONFA_ALL);
225162306a36Sopenharmony_ci	if (err < 0) {
225262306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
225362306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
225462306a36Sopenharmony_ci		kfree_skb(skb);
225562306a36Sopenharmony_ci		goto errout;
225662306a36Sopenharmony_ci	}
225762306a36Sopenharmony_ci	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
225862306a36Sopenharmony_cierrout:
225962306a36Sopenharmony_ci	return err;
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_cistatic int inet_netconf_dump_devconf(struct sk_buff *skb,
226362306a36Sopenharmony_ci				     struct netlink_callback *cb)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
226662306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
226762306a36Sopenharmony_ci	int h, s_h;
226862306a36Sopenharmony_ci	int idx, s_idx;
226962306a36Sopenharmony_ci	struct net_device *dev;
227062306a36Sopenharmony_ci	struct in_device *in_dev;
227162306a36Sopenharmony_ci	struct hlist_head *head;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	if (cb->strict_check) {
227462306a36Sopenharmony_ci		struct netlink_ext_ack *extack = cb->extack;
227562306a36Sopenharmony_ci		struct netconfmsg *ncm;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
227862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request");
227962306a36Sopenharmony_ci			return -EINVAL;
228062306a36Sopenharmony_ci		}
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
228362306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request");
228462306a36Sopenharmony_ci			return -EINVAL;
228562306a36Sopenharmony_ci		}
228662306a36Sopenharmony_ci	}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	s_h = cb->args[0];
228962306a36Sopenharmony_ci	s_idx = idx = cb->args[1];
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
229262306a36Sopenharmony_ci		idx = 0;
229362306a36Sopenharmony_ci		head = &net->dev_index_head[h];
229462306a36Sopenharmony_ci		rcu_read_lock();
229562306a36Sopenharmony_ci		cb->seq = inet_base_seq(net);
229662306a36Sopenharmony_ci		hlist_for_each_entry_rcu(dev, head, index_hlist) {
229762306a36Sopenharmony_ci			if (idx < s_idx)
229862306a36Sopenharmony_ci				goto cont;
229962306a36Sopenharmony_ci			in_dev = __in_dev_get_rcu(dev);
230062306a36Sopenharmony_ci			if (!in_dev)
230162306a36Sopenharmony_ci				goto cont;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci			if (inet_netconf_fill_devconf(skb, dev->ifindex,
230462306a36Sopenharmony_ci						      &in_dev->cnf,
230562306a36Sopenharmony_ci						      NETLINK_CB(cb->skb).portid,
230662306a36Sopenharmony_ci						      nlh->nlmsg_seq,
230762306a36Sopenharmony_ci						      RTM_NEWNETCONF,
230862306a36Sopenharmony_ci						      NLM_F_MULTI,
230962306a36Sopenharmony_ci						      NETCONFA_ALL) < 0) {
231062306a36Sopenharmony_ci				rcu_read_unlock();
231162306a36Sopenharmony_ci				goto done;
231262306a36Sopenharmony_ci			}
231362306a36Sopenharmony_ci			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
231462306a36Sopenharmony_cicont:
231562306a36Sopenharmony_ci			idx++;
231662306a36Sopenharmony_ci		}
231762306a36Sopenharmony_ci		rcu_read_unlock();
231862306a36Sopenharmony_ci	}
231962306a36Sopenharmony_ci	if (h == NETDEV_HASHENTRIES) {
232062306a36Sopenharmony_ci		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
232162306a36Sopenharmony_ci					      net->ipv4.devconf_all,
232262306a36Sopenharmony_ci					      NETLINK_CB(cb->skb).portid,
232362306a36Sopenharmony_ci					      nlh->nlmsg_seq,
232462306a36Sopenharmony_ci					      RTM_NEWNETCONF, NLM_F_MULTI,
232562306a36Sopenharmony_ci					      NETCONFA_ALL) < 0)
232662306a36Sopenharmony_ci			goto done;
232762306a36Sopenharmony_ci		else
232862306a36Sopenharmony_ci			h++;
232962306a36Sopenharmony_ci	}
233062306a36Sopenharmony_ci	if (h == NETDEV_HASHENTRIES + 1) {
233162306a36Sopenharmony_ci		if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
233262306a36Sopenharmony_ci					      net->ipv4.devconf_dflt,
233362306a36Sopenharmony_ci					      NETLINK_CB(cb->skb).portid,
233462306a36Sopenharmony_ci					      nlh->nlmsg_seq,
233562306a36Sopenharmony_ci					      RTM_NEWNETCONF, NLM_F_MULTI,
233662306a36Sopenharmony_ci					      NETCONFA_ALL) < 0)
233762306a36Sopenharmony_ci			goto done;
233862306a36Sopenharmony_ci		else
233962306a36Sopenharmony_ci			h++;
234062306a36Sopenharmony_ci	}
234162306a36Sopenharmony_cidone:
234262306a36Sopenharmony_ci	cb->args[0] = h;
234362306a36Sopenharmony_ci	cb->args[1] = idx;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	return skb->len;
234662306a36Sopenharmony_ci}
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_cistatic void devinet_copy_dflt_conf(struct net *net, int i)
235162306a36Sopenharmony_ci{
235262306a36Sopenharmony_ci	struct net_device *dev;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	rcu_read_lock();
235562306a36Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
235662306a36Sopenharmony_ci		struct in_device *in_dev;
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci		in_dev = __in_dev_get_rcu(dev);
235962306a36Sopenharmony_ci		if (in_dev && !test_bit(i, in_dev->cnf.state))
236062306a36Sopenharmony_ci			in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci	rcu_read_unlock();
236362306a36Sopenharmony_ci}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci/* called with RTNL locked */
236662306a36Sopenharmony_cistatic void inet_forward_change(struct net *net)
236762306a36Sopenharmony_ci{
236862306a36Sopenharmony_ci	struct net_device *dev;
236962306a36Sopenharmony_ci	int on = IPV4_DEVCONF_ALL(net, FORWARDING);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
237262306a36Sopenharmony_ci	IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
237362306a36Sopenharmony_ci	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
237462306a36Sopenharmony_ci				    NETCONFA_FORWARDING,
237562306a36Sopenharmony_ci				    NETCONFA_IFINDEX_ALL,
237662306a36Sopenharmony_ci				    net->ipv4.devconf_all);
237762306a36Sopenharmony_ci	inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
237862306a36Sopenharmony_ci				    NETCONFA_FORWARDING,
237962306a36Sopenharmony_ci				    NETCONFA_IFINDEX_DEFAULT,
238062306a36Sopenharmony_ci				    net->ipv4.devconf_dflt);
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	for_each_netdev(net, dev) {
238362306a36Sopenharmony_ci		struct in_device *in_dev;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci		if (on)
238662306a36Sopenharmony_ci			dev_disable_lro(dev);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci		in_dev = __in_dev_get_rtnl(dev);
238962306a36Sopenharmony_ci		if (in_dev) {
239062306a36Sopenharmony_ci			IN_DEV_CONF_SET(in_dev, FORWARDING, on);
239162306a36Sopenharmony_ci			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
239262306a36Sopenharmony_ci						    NETCONFA_FORWARDING,
239362306a36Sopenharmony_ci						    dev->ifindex, &in_dev->cnf);
239462306a36Sopenharmony_ci		}
239562306a36Sopenharmony_ci	}
239662306a36Sopenharmony_ci}
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_cistatic int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
239962306a36Sopenharmony_ci{
240062306a36Sopenharmony_ci	if (cnf == net->ipv4.devconf_dflt)
240162306a36Sopenharmony_ci		return NETCONFA_IFINDEX_DEFAULT;
240262306a36Sopenharmony_ci	else if (cnf == net->ipv4.devconf_all)
240362306a36Sopenharmony_ci		return NETCONFA_IFINDEX_ALL;
240462306a36Sopenharmony_ci	else {
240562306a36Sopenharmony_ci		struct in_device *idev
240662306a36Sopenharmony_ci			= container_of(cnf, struct in_device, cnf);
240762306a36Sopenharmony_ci		return idev->dev->ifindex;
240862306a36Sopenharmony_ci	}
240962306a36Sopenharmony_ci}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_cistatic int devinet_conf_proc(struct ctl_table *ctl, int write,
241262306a36Sopenharmony_ci			     void *buffer, size_t *lenp, loff_t *ppos)
241362306a36Sopenharmony_ci{
241462306a36Sopenharmony_ci	int old_value = *(int *)ctl->data;
241562306a36Sopenharmony_ci	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
241662306a36Sopenharmony_ci	int new_value = *(int *)ctl->data;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	if (write) {
241962306a36Sopenharmony_ci		struct ipv4_devconf *cnf = ctl->extra1;
242062306a36Sopenharmony_ci		struct net *net = ctl->extra2;
242162306a36Sopenharmony_ci		int i = (int *)ctl->data - cnf->data;
242262306a36Sopenharmony_ci		int ifindex;
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci		set_bit(i, cnf->state);
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci		if (cnf == net->ipv4.devconf_dflt)
242762306a36Sopenharmony_ci			devinet_copy_dflt_conf(net, i);
242862306a36Sopenharmony_ci		if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
242962306a36Sopenharmony_ci		    i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
243062306a36Sopenharmony_ci			if ((new_value == 0) && (old_value != 0))
243162306a36Sopenharmony_ci				rt_cache_flush(net);
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci		if (i == IPV4_DEVCONF_BC_FORWARDING - 1 &&
243462306a36Sopenharmony_ci		    new_value != old_value)
243562306a36Sopenharmony_ci			rt_cache_flush(net);
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci		if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
243862306a36Sopenharmony_ci		    new_value != old_value) {
243962306a36Sopenharmony_ci			ifindex = devinet_conf_ifindex(net, cnf);
244062306a36Sopenharmony_ci			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
244162306a36Sopenharmony_ci						    NETCONFA_RP_FILTER,
244262306a36Sopenharmony_ci						    ifindex, cnf);
244362306a36Sopenharmony_ci		}
244462306a36Sopenharmony_ci		if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
244562306a36Sopenharmony_ci		    new_value != old_value) {
244662306a36Sopenharmony_ci			ifindex = devinet_conf_ifindex(net, cnf);
244762306a36Sopenharmony_ci			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
244862306a36Sopenharmony_ci						    NETCONFA_PROXY_NEIGH,
244962306a36Sopenharmony_ci						    ifindex, cnf);
245062306a36Sopenharmony_ci		}
245162306a36Sopenharmony_ci		if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 &&
245262306a36Sopenharmony_ci		    new_value != old_value) {
245362306a36Sopenharmony_ci			ifindex = devinet_conf_ifindex(net, cnf);
245462306a36Sopenharmony_ci			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
245562306a36Sopenharmony_ci						    NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
245662306a36Sopenharmony_ci						    ifindex, cnf);
245762306a36Sopenharmony_ci		}
245862306a36Sopenharmony_ci	}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	return ret;
246162306a36Sopenharmony_ci}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_cistatic int devinet_sysctl_forward(struct ctl_table *ctl, int write,
246462306a36Sopenharmony_ci				  void *buffer, size_t *lenp, loff_t *ppos)
246562306a36Sopenharmony_ci{
246662306a36Sopenharmony_ci	int *valp = ctl->data;
246762306a36Sopenharmony_ci	int val = *valp;
246862306a36Sopenharmony_ci	loff_t pos = *ppos;
246962306a36Sopenharmony_ci	struct net *net = ctl->extra2;
247062306a36Sopenharmony_ci	int ret;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN))
247362306a36Sopenharmony_ci		return -EPERM;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	if (write && *valp != val) {
247862306a36Sopenharmony_ci		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
247962306a36Sopenharmony_ci			if (!rtnl_trylock()) {
248062306a36Sopenharmony_ci				/* Restore the original values before restarting */
248162306a36Sopenharmony_ci				*valp = val;
248262306a36Sopenharmony_ci				*ppos = pos;
248362306a36Sopenharmony_ci				return restart_syscall();
248462306a36Sopenharmony_ci			}
248562306a36Sopenharmony_ci			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
248662306a36Sopenharmony_ci				inet_forward_change(net);
248762306a36Sopenharmony_ci			} else {
248862306a36Sopenharmony_ci				struct ipv4_devconf *cnf = ctl->extra1;
248962306a36Sopenharmony_ci				struct in_device *idev =
249062306a36Sopenharmony_ci					container_of(cnf, struct in_device, cnf);
249162306a36Sopenharmony_ci				if (*valp)
249262306a36Sopenharmony_ci					dev_disable_lro(idev->dev);
249362306a36Sopenharmony_ci				inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
249462306a36Sopenharmony_ci							    NETCONFA_FORWARDING,
249562306a36Sopenharmony_ci							    idev->dev->ifindex,
249662306a36Sopenharmony_ci							    cnf);
249762306a36Sopenharmony_ci			}
249862306a36Sopenharmony_ci			rtnl_unlock();
249962306a36Sopenharmony_ci			rt_cache_flush(net);
250062306a36Sopenharmony_ci		} else
250162306a36Sopenharmony_ci			inet_netconf_notify_devconf(net, RTM_NEWNETCONF,
250262306a36Sopenharmony_ci						    NETCONFA_FORWARDING,
250362306a36Sopenharmony_ci						    NETCONFA_IFINDEX_DEFAULT,
250462306a36Sopenharmony_ci						    net->ipv4.devconf_dflt);
250562306a36Sopenharmony_ci	}
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	return ret;
250862306a36Sopenharmony_ci}
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_cistatic int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
251162306a36Sopenharmony_ci				void *buffer, size_t *lenp, loff_t *ppos)
251262306a36Sopenharmony_ci{
251362306a36Sopenharmony_ci	int *valp = ctl->data;
251462306a36Sopenharmony_ci	int val = *valp;
251562306a36Sopenharmony_ci	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
251662306a36Sopenharmony_ci	struct net *net = ctl->extra2;
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	if (write && *valp != val)
251962306a36Sopenharmony_ci		rt_cache_flush(net);
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	return ret;
252262306a36Sopenharmony_ci}
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
252562306a36Sopenharmony_ci	{ \
252662306a36Sopenharmony_ci		.procname	= name, \
252762306a36Sopenharmony_ci		.data		= ipv4_devconf.data + \
252862306a36Sopenharmony_ci				  IPV4_DEVCONF_ ## attr - 1, \
252962306a36Sopenharmony_ci		.maxlen		= sizeof(int), \
253062306a36Sopenharmony_ci		.mode		= mval, \
253162306a36Sopenharmony_ci		.proc_handler	= proc, \
253262306a36Sopenharmony_ci		.extra1		= &ipv4_devconf, \
253362306a36Sopenharmony_ci	}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
253662306a36Sopenharmony_ci	DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
253962306a36Sopenharmony_ci	DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
254262306a36Sopenharmony_ci	DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
254562306a36Sopenharmony_ci	DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_cistatic struct devinet_sysctl_table {
254862306a36Sopenharmony_ci	struct ctl_table_header *sysctl_header;
254962306a36Sopenharmony_ci	struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
255062306a36Sopenharmony_ci} devinet_sysctl = {
255162306a36Sopenharmony_ci	.devinet_vars = {
255262306a36Sopenharmony_ci		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
255362306a36Sopenharmony_ci					     devinet_sysctl_forward),
255462306a36Sopenharmony_ci		DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
255562306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"),
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
255862306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
255962306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
256062306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
256162306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
256262306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
256362306a36Sopenharmony_ci					"accept_source_route"),
256462306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
256562306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
256662306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
256762306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
256862306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
256962306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
257062306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
257162306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
257262306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
257362306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
257462306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
257562306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
257662306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER,
257762306a36Sopenharmony_ci					"arp_evict_nocarrier"),
257862306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
257962306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION,
258062306a36Sopenharmony_ci					"force_igmp_version"),
258162306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL,
258262306a36Sopenharmony_ci					"igmpv2_unsolicited_report_interval"),
258362306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL,
258462306a36Sopenharmony_ci					"igmpv3_unsolicited_report_interval"),
258562306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
258662306a36Sopenharmony_ci					"ignore_routes_with_linkdown"),
258762306a36Sopenharmony_ci		DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
258862306a36Sopenharmony_ci					"drop_gratuitous_arp"),
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci		DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
259162306a36Sopenharmony_ci		DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
259262306a36Sopenharmony_ci		DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
259362306a36Sopenharmony_ci					      "promote_secondaries"),
259462306a36Sopenharmony_ci		DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
259562306a36Sopenharmony_ci					      "route_localnet"),
259662306a36Sopenharmony_ci		DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
259762306a36Sopenharmony_ci					      "drop_unicast_in_l2_multicast"),
259862306a36Sopenharmony_ci	},
259962306a36Sopenharmony_ci};
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_cistatic int __devinet_sysctl_register(struct net *net, char *dev_name,
260262306a36Sopenharmony_ci				     int ifindex, struct ipv4_devconf *p)
260362306a36Sopenharmony_ci{
260462306a36Sopenharmony_ci	int i;
260562306a36Sopenharmony_ci	struct devinet_sysctl_table *t;
260662306a36Sopenharmony_ci	char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT);
260962306a36Sopenharmony_ci	if (!t)
261062306a36Sopenharmony_ci		goto out;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
261362306a36Sopenharmony_ci		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
261462306a36Sopenharmony_ci		t->devinet_vars[i].extra1 = p;
261562306a36Sopenharmony_ci		t->devinet_vars[i].extra2 = net;
261662306a36Sopenharmony_ci	}
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
262162306a36Sopenharmony_ci	if (!t->sysctl_header)
262262306a36Sopenharmony_ci		goto free;
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	p->sysctl = t;
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
262762306a36Sopenharmony_ci				    ifindex, p);
262862306a36Sopenharmony_ci	return 0;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_cifree:
263162306a36Sopenharmony_ci	kfree(t);
263262306a36Sopenharmony_ciout:
263362306a36Sopenharmony_ci	return -ENOMEM;
263462306a36Sopenharmony_ci}
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_cistatic void __devinet_sysctl_unregister(struct net *net,
263762306a36Sopenharmony_ci					struct ipv4_devconf *cnf, int ifindex)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	struct devinet_sysctl_table *t = cnf->sysctl;
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	if (t) {
264262306a36Sopenharmony_ci		cnf->sysctl = NULL;
264362306a36Sopenharmony_ci		unregister_net_sysctl_table(t->sysctl_header);
264462306a36Sopenharmony_ci		kfree(t);
264562306a36Sopenharmony_ci	}
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
264862306a36Sopenharmony_ci}
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_cistatic int devinet_sysctl_register(struct in_device *idev)
265162306a36Sopenharmony_ci{
265262306a36Sopenharmony_ci	int err;
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	if (!sysctl_dev_name_is_allowed(idev->dev->name))
265562306a36Sopenharmony_ci		return -EINVAL;
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
265862306a36Sopenharmony_ci	if (err)
265962306a36Sopenharmony_ci		return err;
266062306a36Sopenharmony_ci	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
266162306a36Sopenharmony_ci					idev->dev->ifindex, &idev->cnf);
266262306a36Sopenharmony_ci	if (err)
266362306a36Sopenharmony_ci		neigh_sysctl_unregister(idev->arp_parms);
266462306a36Sopenharmony_ci	return err;
266562306a36Sopenharmony_ci}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_cistatic void devinet_sysctl_unregister(struct in_device *idev)
266862306a36Sopenharmony_ci{
266962306a36Sopenharmony_ci	struct net *net = dev_net(idev->dev);
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	__devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex);
267262306a36Sopenharmony_ci	neigh_sysctl_unregister(idev->arp_parms);
267362306a36Sopenharmony_ci}
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_cistatic struct ctl_table ctl_forward_entry[] = {
267662306a36Sopenharmony_ci	{
267762306a36Sopenharmony_ci		.procname	= "ip_forward",
267862306a36Sopenharmony_ci		.data		= &ipv4_devconf.data[
267962306a36Sopenharmony_ci					IPV4_DEVCONF_FORWARDING - 1],
268062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
268162306a36Sopenharmony_ci		.mode		= 0644,
268262306a36Sopenharmony_ci		.proc_handler	= devinet_sysctl_forward,
268362306a36Sopenharmony_ci		.extra1		= &ipv4_devconf,
268462306a36Sopenharmony_ci		.extra2		= &init_net,
268562306a36Sopenharmony_ci	},
268662306a36Sopenharmony_ci	{ },
268762306a36Sopenharmony_ci};
268862306a36Sopenharmony_ci#endif
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_cistatic __net_init int devinet_init_net(struct net *net)
269162306a36Sopenharmony_ci{
269262306a36Sopenharmony_ci	int err;
269362306a36Sopenharmony_ci	struct ipv4_devconf *all, *dflt;
269462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
269562306a36Sopenharmony_ci	struct ctl_table *tbl;
269662306a36Sopenharmony_ci	struct ctl_table_header *forw_hdr;
269762306a36Sopenharmony_ci#endif
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	err = -ENOMEM;
270062306a36Sopenharmony_ci	all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL);
270162306a36Sopenharmony_ci	if (!all)
270262306a36Sopenharmony_ci		goto err_alloc_all;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
270562306a36Sopenharmony_ci	if (!dflt)
270662306a36Sopenharmony_ci		goto err_alloc_dflt;
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
270962306a36Sopenharmony_ci	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
271062306a36Sopenharmony_ci	if (!tbl)
271162306a36Sopenharmony_ci		goto err_alloc_ctl;
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
271462306a36Sopenharmony_ci	tbl[0].extra1 = all;
271562306a36Sopenharmony_ci	tbl[0].extra2 = net;
271662306a36Sopenharmony_ci#endif
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	if (!net_eq(net, &init_net)) {
271962306a36Sopenharmony_ci		switch (net_inherit_devconf()) {
272062306a36Sopenharmony_ci		case 3:
272162306a36Sopenharmony_ci			/* copy from the current netns */
272262306a36Sopenharmony_ci			memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all,
272362306a36Sopenharmony_ci			       sizeof(ipv4_devconf));
272462306a36Sopenharmony_ci			memcpy(dflt,
272562306a36Sopenharmony_ci			       current->nsproxy->net_ns->ipv4.devconf_dflt,
272662306a36Sopenharmony_ci			       sizeof(ipv4_devconf_dflt));
272762306a36Sopenharmony_ci			break;
272862306a36Sopenharmony_ci		case 0:
272962306a36Sopenharmony_ci		case 1:
273062306a36Sopenharmony_ci			/* copy from init_net */
273162306a36Sopenharmony_ci			memcpy(all, init_net.ipv4.devconf_all,
273262306a36Sopenharmony_ci			       sizeof(ipv4_devconf));
273362306a36Sopenharmony_ci			memcpy(dflt, init_net.ipv4.devconf_dflt,
273462306a36Sopenharmony_ci			       sizeof(ipv4_devconf_dflt));
273562306a36Sopenharmony_ci			break;
273662306a36Sopenharmony_ci		case 2:
273762306a36Sopenharmony_ci			/* use compiled values */
273862306a36Sopenharmony_ci			break;
273962306a36Sopenharmony_ci		}
274062306a36Sopenharmony_ci	}
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
274362306a36Sopenharmony_ci	err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all);
274462306a36Sopenharmony_ci	if (err < 0)
274562306a36Sopenharmony_ci		goto err_reg_all;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci	err = __devinet_sysctl_register(net, "default",
274862306a36Sopenharmony_ci					NETCONFA_IFINDEX_DEFAULT, dflt);
274962306a36Sopenharmony_ci	if (err < 0)
275062306a36Sopenharmony_ci		goto err_reg_dflt;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	err = -ENOMEM;
275362306a36Sopenharmony_ci	forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl,
275462306a36Sopenharmony_ci					  ARRAY_SIZE(ctl_forward_entry));
275562306a36Sopenharmony_ci	if (!forw_hdr)
275662306a36Sopenharmony_ci		goto err_reg_ctl;
275762306a36Sopenharmony_ci	net->ipv4.forw_hdr = forw_hdr;
275862306a36Sopenharmony_ci#endif
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	net->ipv4.devconf_all = all;
276162306a36Sopenharmony_ci	net->ipv4.devconf_dflt = dflt;
276262306a36Sopenharmony_ci	return 0;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
276562306a36Sopenharmony_cierr_reg_ctl:
276662306a36Sopenharmony_ci	__devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT);
276762306a36Sopenharmony_cierr_reg_dflt:
276862306a36Sopenharmony_ci	__devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
276962306a36Sopenharmony_cierr_reg_all:
277062306a36Sopenharmony_ci	kfree(tbl);
277162306a36Sopenharmony_cierr_alloc_ctl:
277262306a36Sopenharmony_ci#endif
277362306a36Sopenharmony_ci	kfree(dflt);
277462306a36Sopenharmony_cierr_alloc_dflt:
277562306a36Sopenharmony_ci	kfree(all);
277662306a36Sopenharmony_cierr_alloc_all:
277762306a36Sopenharmony_ci	return err;
277862306a36Sopenharmony_ci}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_cistatic __net_exit void devinet_exit_net(struct net *net)
278162306a36Sopenharmony_ci{
278262306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
278362306a36Sopenharmony_ci	struct ctl_table *tbl;
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	tbl = net->ipv4.forw_hdr->ctl_table_arg;
278662306a36Sopenharmony_ci	unregister_net_sysctl_table(net->ipv4.forw_hdr);
278762306a36Sopenharmony_ci	__devinet_sysctl_unregister(net, net->ipv4.devconf_dflt,
278862306a36Sopenharmony_ci				    NETCONFA_IFINDEX_DEFAULT);
278962306a36Sopenharmony_ci	__devinet_sysctl_unregister(net, net->ipv4.devconf_all,
279062306a36Sopenharmony_ci				    NETCONFA_IFINDEX_ALL);
279162306a36Sopenharmony_ci	kfree(tbl);
279262306a36Sopenharmony_ci#endif
279362306a36Sopenharmony_ci	kfree(net->ipv4.devconf_dflt);
279462306a36Sopenharmony_ci	kfree(net->ipv4.devconf_all);
279562306a36Sopenharmony_ci}
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_cistatic __net_initdata struct pernet_operations devinet_ops = {
279862306a36Sopenharmony_ci	.init = devinet_init_net,
279962306a36Sopenharmony_ci	.exit = devinet_exit_net,
280062306a36Sopenharmony_ci};
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_cistatic struct rtnl_af_ops inet_af_ops __read_mostly = {
280362306a36Sopenharmony_ci	.family		  = AF_INET,
280462306a36Sopenharmony_ci	.fill_link_af	  = inet_fill_link_af,
280562306a36Sopenharmony_ci	.get_link_af_size = inet_get_link_af_size,
280662306a36Sopenharmony_ci	.validate_link_af = inet_validate_link_af,
280762306a36Sopenharmony_ci	.set_link_af	  = inet_set_link_af,
280862306a36Sopenharmony_ci};
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_civoid __init devinet_init(void)
281162306a36Sopenharmony_ci{
281262306a36Sopenharmony_ci	int i;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	for (i = 0; i < IN4_ADDR_HSIZE; i++)
281562306a36Sopenharmony_ci		INIT_HLIST_HEAD(&inet_addr_lst[i]);
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	register_pernet_subsys(&devinet_ops);
281862306a36Sopenharmony_ci	register_netdevice_notifier(&ip_netdev_notifier);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0);
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	rtnl_af_register(&inet_af_ops);
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0);
282562306a36Sopenharmony_ci	rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0);
282662306a36Sopenharmony_ci	rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0);
282762306a36Sopenharmony_ci	rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
282862306a36Sopenharmony_ci		      inet_netconf_dump_devconf, 0);
282962306a36Sopenharmony_ci}
2830