162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * INET		An implementation of the TCP/IP protocol suite for the LINUX
462306a36Sopenharmony_ci *		operating system.  INET is implemented using the  BSD Socket
562306a36Sopenharmony_ci *		interface as the means of communication with the user level.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *		Routing netlink socket interface: protocol independent part.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	Fixes:
1262306a36Sopenharmony_ci *	Vitaly E. Lavrov		RTA_OK arithmetic was wrong.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/bitops.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/socket.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/timer.h>
2262306a36Sopenharmony_ci#include <linux/string.h>
2362306a36Sopenharmony_ci#include <linux/sockios.h>
2462306a36Sopenharmony_ci#include <linux/net.h>
2562306a36Sopenharmony_ci#include <linux/fcntl.h>
2662306a36Sopenharmony_ci#include <linux/mm.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/interrupt.h>
2962306a36Sopenharmony_ci#include <linux/capability.h>
3062306a36Sopenharmony_ci#include <linux/skbuff.h>
3162306a36Sopenharmony_ci#include <linux/init.h>
3262306a36Sopenharmony_ci#include <linux/security.h>
3362306a36Sopenharmony_ci#include <linux/mutex.h>
3462306a36Sopenharmony_ci#include <linux/if_addr.h>
3562306a36Sopenharmony_ci#include <linux/if_bridge.h>
3662306a36Sopenharmony_ci#include <linux/if_vlan.h>
3762306a36Sopenharmony_ci#include <linux/pci.h>
3862306a36Sopenharmony_ci#include <linux/etherdevice.h>
3962306a36Sopenharmony_ci#include <linux/bpf.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include <linux/uaccess.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include <linux/inet.h>
4462306a36Sopenharmony_ci#include <linux/netdevice.h>
4562306a36Sopenharmony_ci#include <net/ip.h>
4662306a36Sopenharmony_ci#include <net/protocol.h>
4762306a36Sopenharmony_ci#include <net/arp.h>
4862306a36Sopenharmony_ci#include <net/route.h>
4962306a36Sopenharmony_ci#include <net/udp.h>
5062306a36Sopenharmony_ci#include <net/tcp.h>
5162306a36Sopenharmony_ci#include <net/sock.h>
5262306a36Sopenharmony_ci#include <net/pkt_sched.h>
5362306a36Sopenharmony_ci#include <net/fib_rules.h>
5462306a36Sopenharmony_ci#include <net/rtnetlink.h>
5562306a36Sopenharmony_ci#include <net/net_namespace.h>
5662306a36Sopenharmony_ci#include <net/devlink.h>
5762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
5862306a36Sopenharmony_ci#include <net/addrconf.h>
5962306a36Sopenharmony_ci#endif
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#include "dev.h"
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define RTNL_MAX_TYPE		50
6462306a36Sopenharmony_ci#define RTNL_SLAVE_MAX_TYPE	44
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct rtnl_link {
6762306a36Sopenharmony_ci	rtnl_doit_func		doit;
6862306a36Sopenharmony_ci	rtnl_dumpit_func	dumpit;
6962306a36Sopenharmony_ci	struct module		*owner;
7062306a36Sopenharmony_ci	unsigned int		flags;
7162306a36Sopenharmony_ci	struct rcu_head		rcu;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic DEFINE_MUTEX(rtnl_mutex);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid rtnl_lock(void)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	mutex_lock(&rtnl_mutex);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_lock);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint rtnl_lock_killable(void)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	return mutex_lock_killable(&rtnl_mutex);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_lock_killable);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic struct sk_buff *defer_kfree_skb_list;
8962306a36Sopenharmony_civoid rtnl_kfree_skbs(struct sk_buff *head, struct sk_buff *tail)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	if (head && tail) {
9262306a36Sopenharmony_ci		tail->next = defer_kfree_skb_list;
9362306a36Sopenharmony_ci		defer_kfree_skb_list = head;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_kfree_skbs);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_civoid __rtnl_unlock(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct sk_buff *head = defer_kfree_skb_list;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	defer_kfree_skb_list = NULL;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Ensure that we didn't actually add any TODO item when __rtnl_unlock()
10562306a36Sopenharmony_ci	 * is used. In some places, e.g. in cfg80211, we have code that will do
10662306a36Sopenharmony_ci	 * something like
10762306a36Sopenharmony_ci	 *   rtnl_lock()
10862306a36Sopenharmony_ci	 *   wiphy_lock()
10962306a36Sopenharmony_ci	 *   ...
11062306a36Sopenharmony_ci	 *   rtnl_unlock()
11162306a36Sopenharmony_ci	 *
11262306a36Sopenharmony_ci	 * and because netdev_run_todo() acquires the RTNL for items on the list
11362306a36Sopenharmony_ci	 * we could cause a situation such as this:
11462306a36Sopenharmony_ci	 * Thread 1			Thread 2
11562306a36Sopenharmony_ci	 *				  rtnl_lock()
11662306a36Sopenharmony_ci	 *				  unregister_netdevice()
11762306a36Sopenharmony_ci	 *				  __rtnl_unlock()
11862306a36Sopenharmony_ci	 * rtnl_lock()
11962306a36Sopenharmony_ci	 * wiphy_lock()
12062306a36Sopenharmony_ci	 * rtnl_unlock()
12162306a36Sopenharmony_ci	 *   netdev_run_todo()
12262306a36Sopenharmony_ci	 *     __rtnl_unlock()
12362306a36Sopenharmony_ci	 *
12462306a36Sopenharmony_ci	 *     // list not empty now
12562306a36Sopenharmony_ci	 *     // because of thread 2
12662306a36Sopenharmony_ci	 *				  rtnl_lock()
12762306a36Sopenharmony_ci	 *     while (!list_empty(...))
12862306a36Sopenharmony_ci	 *       rtnl_lock()
12962306a36Sopenharmony_ci	 *				  wiphy_lock()
13062306a36Sopenharmony_ci	 * **** DEADLOCK ****
13162306a36Sopenharmony_ci	 *
13262306a36Sopenharmony_ci	 * However, usage of __rtnl_unlock() is rare, and so we can ensure that
13362306a36Sopenharmony_ci	 * it's not used in cases where something is added to do the list.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	WARN_ON(!list_empty(&net_todo_list));
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	mutex_unlock(&rtnl_mutex);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	while (head) {
14062306a36Sopenharmony_ci		struct sk_buff *next = head->next;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		kfree_skb(head);
14362306a36Sopenharmony_ci		cond_resched();
14462306a36Sopenharmony_ci		head = next;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_civoid rtnl_unlock(void)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	/* This fellow will unlock it for us. */
15162306a36Sopenharmony_ci	netdev_run_todo();
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_unlock);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciint rtnl_trylock(void)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	return mutex_trylock(&rtnl_mutex);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_trylock);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ciint rtnl_is_locked(void)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	return mutex_is_locked(&rtnl_mutex);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_is_locked);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cibool refcount_dec_and_rtnl_lock(refcount_t *r)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return refcount_dec_and_mutex_lock(r, &rtnl_mutex);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ciEXPORT_SYMBOL(refcount_dec_and_rtnl_lock);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci#ifdef CONFIG_PROVE_LOCKING
17462306a36Sopenharmony_cibool lockdep_rtnl_is_held(void)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	return lockdep_is_held(&rtnl_mutex);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ciEXPORT_SYMBOL(lockdep_rtnl_is_held);
17962306a36Sopenharmony_ci#endif /* #ifdef CONFIG_PROVE_LOCKING */
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic struct rtnl_link __rcu *__rcu *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic inline int rtm_msgindex(int msgtype)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	int msgindex = msgtype - RTM_BASE;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/*
18862306a36Sopenharmony_ci	 * msgindex < 0 implies someone tried to register a netlink
18962306a36Sopenharmony_ci	 * control code. msgindex >= RTM_NR_MSGTYPES may indicate that
19062306a36Sopenharmony_ci	 * the message type has not been added to linux/rtnetlink.h
19162306a36Sopenharmony_ci	 */
19262306a36Sopenharmony_ci	BUG_ON(msgindex < 0 || msgindex >= RTM_NR_MSGTYPES);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return msgindex;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic struct rtnl_link *rtnl_get_link(int protocol, int msgtype)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct rtnl_link __rcu **tab;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (protocol >= ARRAY_SIZE(rtnl_msg_handlers))
20262306a36Sopenharmony_ci		protocol = PF_UNSPEC;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	tab = rcu_dereference_rtnl(rtnl_msg_handlers[protocol]);
20562306a36Sopenharmony_ci	if (!tab)
20662306a36Sopenharmony_ci		tab = rcu_dereference_rtnl(rtnl_msg_handlers[PF_UNSPEC]);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return rcu_dereference_rtnl(tab[msgtype]);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int rtnl_register_internal(struct module *owner,
21262306a36Sopenharmony_ci				  int protocol, int msgtype,
21362306a36Sopenharmony_ci				  rtnl_doit_func doit, rtnl_dumpit_func dumpit,
21462306a36Sopenharmony_ci				  unsigned int flags)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct rtnl_link *link, *old;
21762306a36Sopenharmony_ci	struct rtnl_link __rcu **tab;
21862306a36Sopenharmony_ci	int msgindex;
21962306a36Sopenharmony_ci	int ret = -ENOBUFS;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
22262306a36Sopenharmony_ci	msgindex = rtm_msgindex(msgtype);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	rtnl_lock();
22562306a36Sopenharmony_ci	tab = rtnl_dereference(rtnl_msg_handlers[protocol]);
22662306a36Sopenharmony_ci	if (tab == NULL) {
22762306a36Sopenharmony_ci		tab = kcalloc(RTM_NR_MSGTYPES, sizeof(void *), GFP_KERNEL);
22862306a36Sopenharmony_ci		if (!tab)
22962306a36Sopenharmony_ci			goto unlock;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/* ensures we see the 0 stores */
23262306a36Sopenharmony_ci		rcu_assign_pointer(rtnl_msg_handlers[protocol], tab);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	old = rtnl_dereference(tab[msgindex]);
23662306a36Sopenharmony_ci	if (old) {
23762306a36Sopenharmony_ci		link = kmemdup(old, sizeof(*old), GFP_KERNEL);
23862306a36Sopenharmony_ci		if (!link)
23962306a36Sopenharmony_ci			goto unlock;
24062306a36Sopenharmony_ci	} else {
24162306a36Sopenharmony_ci		link = kzalloc(sizeof(*link), GFP_KERNEL);
24262306a36Sopenharmony_ci		if (!link)
24362306a36Sopenharmony_ci			goto unlock;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	WARN_ON(link->owner && link->owner != owner);
24762306a36Sopenharmony_ci	link->owner = owner;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	WARN_ON(doit && link->doit && link->doit != doit);
25062306a36Sopenharmony_ci	if (doit)
25162306a36Sopenharmony_ci		link->doit = doit;
25262306a36Sopenharmony_ci	WARN_ON(dumpit && link->dumpit && link->dumpit != dumpit);
25362306a36Sopenharmony_ci	if (dumpit)
25462306a36Sopenharmony_ci		link->dumpit = dumpit;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	WARN_ON(rtnl_msgtype_kind(msgtype) != RTNL_KIND_DEL &&
25762306a36Sopenharmony_ci		(flags & RTNL_FLAG_BULK_DEL_SUPPORTED));
25862306a36Sopenharmony_ci	link->flags |= flags;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* publish protocol:msgtype */
26162306a36Sopenharmony_ci	rcu_assign_pointer(tab[msgindex], link);
26262306a36Sopenharmony_ci	ret = 0;
26362306a36Sopenharmony_ci	if (old)
26462306a36Sopenharmony_ci		kfree_rcu(old, rcu);
26562306a36Sopenharmony_ciunlock:
26662306a36Sopenharmony_ci	rtnl_unlock();
26762306a36Sopenharmony_ci	return ret;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci/**
27162306a36Sopenharmony_ci * rtnl_register_module - Register a rtnetlink message type
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * @owner: module registering the hook (THIS_MODULE)
27462306a36Sopenharmony_ci * @protocol: Protocol family or PF_UNSPEC
27562306a36Sopenharmony_ci * @msgtype: rtnetlink message type
27662306a36Sopenharmony_ci * @doit: Function pointer called for each request message
27762306a36Sopenharmony_ci * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
27862306a36Sopenharmony_ci * @flags: rtnl_link_flags to modify behaviour of doit/dumpit functions
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Like rtnl_register, but for use by removable modules.
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_ciint rtnl_register_module(struct module *owner,
28362306a36Sopenharmony_ci			 int protocol, int msgtype,
28462306a36Sopenharmony_ci			 rtnl_doit_func doit, rtnl_dumpit_func dumpit,
28562306a36Sopenharmony_ci			 unsigned int flags)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	return rtnl_register_internal(owner, protocol, msgtype,
28862306a36Sopenharmony_ci				      doit, dumpit, flags);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_register_module);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/**
29362306a36Sopenharmony_ci * rtnl_register - Register a rtnetlink message type
29462306a36Sopenharmony_ci * @protocol: Protocol family or PF_UNSPEC
29562306a36Sopenharmony_ci * @msgtype: rtnetlink message type
29662306a36Sopenharmony_ci * @doit: Function pointer called for each request message
29762306a36Sopenharmony_ci * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message
29862306a36Sopenharmony_ci * @flags: rtnl_link_flags to modify behaviour of doit/dumpit functions
29962306a36Sopenharmony_ci *
30062306a36Sopenharmony_ci * Registers the specified function pointers (at least one of them has
30162306a36Sopenharmony_ci * to be non-NULL) to be called whenever a request message for the
30262306a36Sopenharmony_ci * specified protocol family and message type is received.
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * The special protocol family PF_UNSPEC may be used to define fallback
30562306a36Sopenharmony_ci * function pointers for the case when no entry for the specific protocol
30662306a36Sopenharmony_ci * family exists.
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_civoid rtnl_register(int protocol, int msgtype,
30962306a36Sopenharmony_ci		   rtnl_doit_func doit, rtnl_dumpit_func dumpit,
31062306a36Sopenharmony_ci		   unsigned int flags)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	int err;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	err = rtnl_register_internal(NULL, protocol, msgtype, doit, dumpit,
31562306a36Sopenharmony_ci				     flags);
31662306a36Sopenharmony_ci	if (err)
31762306a36Sopenharmony_ci		pr_err("Unable to register rtnetlink message handler, "
31862306a36Sopenharmony_ci		       "protocol = %d, message type = %d\n", protocol, msgtype);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/**
32262306a36Sopenharmony_ci * rtnl_unregister - Unregister a rtnetlink message type
32362306a36Sopenharmony_ci * @protocol: Protocol family or PF_UNSPEC
32462306a36Sopenharmony_ci * @msgtype: rtnetlink message type
32562306a36Sopenharmony_ci *
32662306a36Sopenharmony_ci * Returns 0 on success or a negative error code.
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_ciint rtnl_unregister(int protocol, int msgtype)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct rtnl_link __rcu **tab;
33162306a36Sopenharmony_ci	struct rtnl_link *link;
33262306a36Sopenharmony_ci	int msgindex;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
33562306a36Sopenharmony_ci	msgindex = rtm_msgindex(msgtype);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	rtnl_lock();
33862306a36Sopenharmony_ci	tab = rtnl_dereference(rtnl_msg_handlers[protocol]);
33962306a36Sopenharmony_ci	if (!tab) {
34062306a36Sopenharmony_ci		rtnl_unlock();
34162306a36Sopenharmony_ci		return -ENOENT;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	link = rtnl_dereference(tab[msgindex]);
34562306a36Sopenharmony_ci	RCU_INIT_POINTER(tab[msgindex], NULL);
34662306a36Sopenharmony_ci	rtnl_unlock();
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	kfree_rcu(link, rcu);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return 0;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_unregister);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/**
35562306a36Sopenharmony_ci * rtnl_unregister_all - Unregister all rtnetlink message type of a protocol
35662306a36Sopenharmony_ci * @protocol : Protocol family or PF_UNSPEC
35762306a36Sopenharmony_ci *
35862306a36Sopenharmony_ci * Identical to calling rtnl_unregster() for all registered message types
35962306a36Sopenharmony_ci * of a certain protocol family.
36062306a36Sopenharmony_ci */
36162306a36Sopenharmony_civoid rtnl_unregister_all(int protocol)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct rtnl_link __rcu **tab;
36462306a36Sopenharmony_ci	struct rtnl_link *link;
36562306a36Sopenharmony_ci	int msgindex;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	rtnl_lock();
37062306a36Sopenharmony_ci	tab = rtnl_dereference(rtnl_msg_handlers[protocol]);
37162306a36Sopenharmony_ci	if (!tab) {
37262306a36Sopenharmony_ci		rtnl_unlock();
37362306a36Sopenharmony_ci		return;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	RCU_INIT_POINTER(rtnl_msg_handlers[protocol], NULL);
37662306a36Sopenharmony_ci	for (msgindex = 0; msgindex < RTM_NR_MSGTYPES; msgindex++) {
37762306a36Sopenharmony_ci		link = rtnl_dereference(tab[msgindex]);
37862306a36Sopenharmony_ci		if (!link)
37962306a36Sopenharmony_ci			continue;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		RCU_INIT_POINTER(tab[msgindex], NULL);
38262306a36Sopenharmony_ci		kfree_rcu(link, rcu);
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci	rtnl_unlock();
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	synchronize_net();
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	kfree(tab);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_unregister_all);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic LIST_HEAD(link_ops);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic const struct rtnl_link_ops *rtnl_link_ops_get(const char *kind)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	const struct rtnl_link_ops *ops;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	list_for_each_entry(ops, &link_ops, list) {
39962306a36Sopenharmony_ci		if (!strcmp(ops->kind, kind))
40062306a36Sopenharmony_ci			return ops;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci	return NULL;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci/**
40662306a36Sopenharmony_ci * __rtnl_link_register - Register rtnl_link_ops with rtnetlink.
40762306a36Sopenharmony_ci * @ops: struct rtnl_link_ops * to register
40862306a36Sopenharmony_ci *
40962306a36Sopenharmony_ci * The caller must hold the rtnl_mutex. This function should be used
41062306a36Sopenharmony_ci * by drivers that create devices during module initialization. It
41162306a36Sopenharmony_ci * must be called before registering the devices.
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * Returns 0 on success or a negative error code.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_ciint __rtnl_link_register(struct rtnl_link_ops *ops)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	if (rtnl_link_ops_get(ops->kind))
41862306a36Sopenharmony_ci		return -EEXIST;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* The check for alloc/setup is here because if ops
42162306a36Sopenharmony_ci	 * does not have that filled up, it is not possible
42262306a36Sopenharmony_ci	 * to use the ops for creating device. So do not
42362306a36Sopenharmony_ci	 * fill up dellink as well. That disables rtnl_dellink.
42462306a36Sopenharmony_ci	 */
42562306a36Sopenharmony_ci	if ((ops->alloc || ops->setup) && !ops->dellink)
42662306a36Sopenharmony_ci		ops->dellink = unregister_netdevice_queue;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	list_add_tail(&ops->list, &link_ops);
42962306a36Sopenharmony_ci	return 0;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__rtnl_link_register);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/**
43462306a36Sopenharmony_ci * rtnl_link_register - Register rtnl_link_ops with rtnetlink.
43562306a36Sopenharmony_ci * @ops: struct rtnl_link_ops * to register
43662306a36Sopenharmony_ci *
43762306a36Sopenharmony_ci * Returns 0 on success or a negative error code.
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_ciint rtnl_link_register(struct rtnl_link_ops *ops)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	int err;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	/* Sanity-check max sizes to avoid stack buffer overflow. */
44462306a36Sopenharmony_ci	if (WARN_ON(ops->maxtype > RTNL_MAX_TYPE ||
44562306a36Sopenharmony_ci		    ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE))
44662306a36Sopenharmony_ci		return -EINVAL;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	rtnl_lock();
44962306a36Sopenharmony_ci	err = __rtnl_link_register(ops);
45062306a36Sopenharmony_ci	rtnl_unlock();
45162306a36Sopenharmony_ci	return err;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_link_register);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct net_device *dev;
45862306a36Sopenharmony_ci	LIST_HEAD(list_kill);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	for_each_netdev(net, dev) {
46162306a36Sopenharmony_ci		if (dev->rtnl_link_ops == ops)
46262306a36Sopenharmony_ci			ops->dellink(dev, &list_kill);
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci	unregister_netdevice_many(&list_kill);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci/**
46862306a36Sopenharmony_ci * __rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink.
46962306a36Sopenharmony_ci * @ops: struct rtnl_link_ops * to unregister
47062306a36Sopenharmony_ci *
47162306a36Sopenharmony_ci * The caller must hold the rtnl_mutex and guarantee net_namespace_list
47262306a36Sopenharmony_ci * integrity (hold pernet_ops_rwsem for writing to close the race
47362306a36Sopenharmony_ci * with setup_net() and cleanup_net()).
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_civoid __rtnl_link_unregister(struct rtnl_link_ops *ops)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct net *net;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	for_each_net(net) {
48062306a36Sopenharmony_ci		__rtnl_kill_links(net, ops);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	list_del(&ops->list);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__rtnl_link_unregister);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* Return with the rtnl_lock held when there are no network
48762306a36Sopenharmony_ci * devices unregistering in any network namespace.
48862306a36Sopenharmony_ci */
48962306a36Sopenharmony_cistatic void rtnl_lock_unregistering_all(void)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct net *net;
49262306a36Sopenharmony_ci	bool unregistering;
49362306a36Sopenharmony_ci	DEFINE_WAIT_FUNC(wait, woken_wake_function);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	add_wait_queue(&netdev_unregistering_wq, &wait);
49662306a36Sopenharmony_ci	for (;;) {
49762306a36Sopenharmony_ci		unregistering = false;
49862306a36Sopenharmony_ci		rtnl_lock();
49962306a36Sopenharmony_ci		/* We held write locked pernet_ops_rwsem, and parallel
50062306a36Sopenharmony_ci		 * setup_net() and cleanup_net() are not possible.
50162306a36Sopenharmony_ci		 */
50262306a36Sopenharmony_ci		for_each_net(net) {
50362306a36Sopenharmony_ci			if (atomic_read(&net->dev_unreg_count) > 0) {
50462306a36Sopenharmony_ci				unregistering = true;
50562306a36Sopenharmony_ci				break;
50662306a36Sopenharmony_ci			}
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		if (!unregistering)
50962306a36Sopenharmony_ci			break;
51062306a36Sopenharmony_ci		__rtnl_unlock();
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	remove_wait_queue(&netdev_unregistering_wq, &wait);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci/**
51862306a36Sopenharmony_ci * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink.
51962306a36Sopenharmony_ci * @ops: struct rtnl_link_ops * to unregister
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_civoid rtnl_link_unregister(struct rtnl_link_ops *ops)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	/* Close the race with setup_net() and cleanup_net() */
52462306a36Sopenharmony_ci	down_write(&pernet_ops_rwsem);
52562306a36Sopenharmony_ci	rtnl_lock_unregistering_all();
52662306a36Sopenharmony_ci	__rtnl_link_unregister(ops);
52762306a36Sopenharmony_ci	rtnl_unlock();
52862306a36Sopenharmony_ci	up_write(&pernet_ops_rwsem);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_link_unregister);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct net_device *master_dev;
53562306a36Sopenharmony_ci	const struct rtnl_link_ops *ops;
53662306a36Sopenharmony_ci	size_t size = 0;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	rcu_read_lock();
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
54162306a36Sopenharmony_ci	if (!master_dev)
54262306a36Sopenharmony_ci		goto out;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ops = master_dev->rtnl_link_ops;
54562306a36Sopenharmony_ci	if (!ops || !ops->get_slave_size)
54662306a36Sopenharmony_ci		goto out;
54762306a36Sopenharmony_ci	/* IFLA_INFO_SLAVE_DATA + nested data */
54862306a36Sopenharmony_ci	size = nla_total_size(sizeof(struct nlattr)) +
54962306a36Sopenharmony_ci	       ops->get_slave_size(master_dev, dev);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ciout:
55262306a36Sopenharmony_ci	rcu_read_unlock();
55362306a36Sopenharmony_ci	return size;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic size_t rtnl_link_get_size(const struct net_device *dev)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
55962306a36Sopenharmony_ci	size_t size;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (!ops)
56262306a36Sopenharmony_ci		return 0;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	size = nla_total_size(sizeof(struct nlattr)) + /* IFLA_LINKINFO */
56562306a36Sopenharmony_ci	       nla_total_size(strlen(ops->kind) + 1);  /* IFLA_INFO_KIND */
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (ops->get_size)
56862306a36Sopenharmony_ci		/* IFLA_INFO_DATA + nested data */
56962306a36Sopenharmony_ci		size += nla_total_size(sizeof(struct nlattr)) +
57062306a36Sopenharmony_ci			ops->get_size(dev);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (ops->get_xstats_size)
57362306a36Sopenharmony_ci		/* IFLA_INFO_XSTATS */
57462306a36Sopenharmony_ci		size += nla_total_size(ops->get_xstats_size(dev));
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	size += rtnl_link_get_slave_info_data_size(dev);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return size;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic LIST_HEAD(rtnl_af_ops);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic const struct rtnl_af_ops *rtnl_af_lookup(const int family)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	const struct rtnl_af_ops *ops;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	ASSERT_RTNL();
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	list_for_each_entry(ops, &rtnl_af_ops, list) {
59062306a36Sopenharmony_ci		if (ops->family == family)
59162306a36Sopenharmony_ci			return ops;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return NULL;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/**
59862306a36Sopenharmony_ci * rtnl_af_register - Register rtnl_af_ops with rtnetlink.
59962306a36Sopenharmony_ci * @ops: struct rtnl_af_ops * to register
60062306a36Sopenharmony_ci *
60162306a36Sopenharmony_ci * Returns 0 on success or a negative error code.
60262306a36Sopenharmony_ci */
60362306a36Sopenharmony_civoid rtnl_af_register(struct rtnl_af_ops *ops)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	rtnl_lock();
60662306a36Sopenharmony_ci	list_add_tail_rcu(&ops->list, &rtnl_af_ops);
60762306a36Sopenharmony_ci	rtnl_unlock();
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_af_register);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/**
61262306a36Sopenharmony_ci * rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink.
61362306a36Sopenharmony_ci * @ops: struct rtnl_af_ops * to unregister
61462306a36Sopenharmony_ci */
61562306a36Sopenharmony_civoid rtnl_af_unregister(struct rtnl_af_ops *ops)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	rtnl_lock();
61862306a36Sopenharmony_ci	list_del_rcu(&ops->list);
61962306a36Sopenharmony_ci	rtnl_unlock();
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	synchronize_rcu();
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_af_unregister);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic size_t rtnl_link_get_af_size(const struct net_device *dev,
62662306a36Sopenharmony_ci				    u32 ext_filter_mask)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct rtnl_af_ops *af_ops;
62962306a36Sopenharmony_ci	size_t size;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* IFLA_AF_SPEC */
63262306a36Sopenharmony_ci	size = nla_total_size(sizeof(struct nlattr));
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	rcu_read_lock();
63562306a36Sopenharmony_ci	list_for_each_entry_rcu(af_ops, &rtnl_af_ops, list) {
63662306a36Sopenharmony_ci		if (af_ops->get_link_af_size) {
63762306a36Sopenharmony_ci			/* AF_* + nested data */
63862306a36Sopenharmony_ci			size += nla_total_size(sizeof(struct nlattr)) +
63962306a36Sopenharmony_ci				af_ops->get_link_af_size(dev, ext_filter_mask);
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	rcu_read_unlock();
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return size;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic bool rtnl_have_link_slave_info(const struct net_device *dev)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	struct net_device *master_dev;
65062306a36Sopenharmony_ci	bool ret = false;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	rcu_read_lock();
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
65562306a36Sopenharmony_ci	if (master_dev && master_dev->rtnl_link_ops)
65662306a36Sopenharmony_ci		ret = true;
65762306a36Sopenharmony_ci	rcu_read_unlock();
65862306a36Sopenharmony_ci	return ret;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic int rtnl_link_slave_info_fill(struct sk_buff *skb,
66262306a36Sopenharmony_ci				     const struct net_device *dev)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct net_device *master_dev;
66562306a36Sopenharmony_ci	const struct rtnl_link_ops *ops;
66662306a36Sopenharmony_ci	struct nlattr *slave_data;
66762306a36Sopenharmony_ci	int err;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	master_dev = netdev_master_upper_dev_get((struct net_device *) dev);
67062306a36Sopenharmony_ci	if (!master_dev)
67162306a36Sopenharmony_ci		return 0;
67262306a36Sopenharmony_ci	ops = master_dev->rtnl_link_ops;
67362306a36Sopenharmony_ci	if (!ops)
67462306a36Sopenharmony_ci		return 0;
67562306a36Sopenharmony_ci	if (nla_put_string(skb, IFLA_INFO_SLAVE_KIND, ops->kind) < 0)
67662306a36Sopenharmony_ci		return -EMSGSIZE;
67762306a36Sopenharmony_ci	if (ops->fill_slave_info) {
67862306a36Sopenharmony_ci		slave_data = nla_nest_start_noflag(skb, IFLA_INFO_SLAVE_DATA);
67962306a36Sopenharmony_ci		if (!slave_data)
68062306a36Sopenharmony_ci			return -EMSGSIZE;
68162306a36Sopenharmony_ci		err = ops->fill_slave_info(skb, master_dev, dev);
68262306a36Sopenharmony_ci		if (err < 0)
68362306a36Sopenharmony_ci			goto err_cancel_slave_data;
68462306a36Sopenharmony_ci		nla_nest_end(skb, slave_data);
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cierr_cancel_slave_data:
68962306a36Sopenharmony_ci	nla_nest_cancel(skb, slave_data);
69062306a36Sopenharmony_ci	return err;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int rtnl_link_info_fill(struct sk_buff *skb,
69462306a36Sopenharmony_ci			       const struct net_device *dev)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
69762306a36Sopenharmony_ci	struct nlattr *data;
69862306a36Sopenharmony_ci	int err;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (!ops)
70162306a36Sopenharmony_ci		return 0;
70262306a36Sopenharmony_ci	if (nla_put_string(skb, IFLA_INFO_KIND, ops->kind) < 0)
70362306a36Sopenharmony_ci		return -EMSGSIZE;
70462306a36Sopenharmony_ci	if (ops->fill_xstats) {
70562306a36Sopenharmony_ci		err = ops->fill_xstats(skb, dev);
70662306a36Sopenharmony_ci		if (err < 0)
70762306a36Sopenharmony_ci			return err;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci	if (ops->fill_info) {
71062306a36Sopenharmony_ci		data = nla_nest_start_noflag(skb, IFLA_INFO_DATA);
71162306a36Sopenharmony_ci		if (data == NULL)
71262306a36Sopenharmony_ci			return -EMSGSIZE;
71362306a36Sopenharmony_ci		err = ops->fill_info(skb, dev);
71462306a36Sopenharmony_ci		if (err < 0)
71562306a36Sopenharmony_ci			goto err_cancel_data;
71662306a36Sopenharmony_ci		nla_nest_end(skb, data);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci	return 0;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cierr_cancel_data:
72162306a36Sopenharmony_ci	nla_nest_cancel(skb, data);
72262306a36Sopenharmony_ci	return err;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct nlattr *linkinfo;
72862306a36Sopenharmony_ci	int err = -EMSGSIZE;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	linkinfo = nla_nest_start_noflag(skb, IFLA_LINKINFO);
73162306a36Sopenharmony_ci	if (linkinfo == NULL)
73262306a36Sopenharmony_ci		goto out;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	err = rtnl_link_info_fill(skb, dev);
73562306a36Sopenharmony_ci	if (err < 0)
73662306a36Sopenharmony_ci		goto err_cancel_link;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	err = rtnl_link_slave_info_fill(skb, dev);
73962306a36Sopenharmony_ci	if (err < 0)
74062306a36Sopenharmony_ci		goto err_cancel_link;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	nla_nest_end(skb, linkinfo);
74362306a36Sopenharmony_ci	return 0;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cierr_cancel_link:
74662306a36Sopenharmony_ci	nla_nest_cancel(skb, linkinfo);
74762306a36Sopenharmony_ciout:
74862306a36Sopenharmony_ci	return err;
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ciint rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	struct sock *rtnl = net->rtnl;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	return nlmsg_notify(rtnl, skb, pid, group, echo, GFP_KERNEL);
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ciint rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct sock *rtnl = net->rtnl;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return nlmsg_unicast(rtnl, skb, pid);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_unicast);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_civoid rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
76762306a36Sopenharmony_ci		 const struct nlmsghdr *nlh, gfp_t flags)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct sock *rtnl = net->rtnl;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	nlmsg_notify(rtnl, skb, pid, group, nlmsg_report(nlh), flags);
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_notify);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_civoid rtnl_set_sk_err(struct net *net, u32 group, int error)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	struct sock *rtnl = net->rtnl;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	netlink_set_err(rtnl, 0, group, error);
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_set_sk_err);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ciint rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct nlattr *mx;
78662306a36Sopenharmony_ci	int i, valid = 0;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	/* nothing is dumped for dst_default_metrics, so just skip the loop */
78962306a36Sopenharmony_ci	if (metrics == dst_default_metrics.metrics)
79062306a36Sopenharmony_ci		return 0;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	mx = nla_nest_start_noflag(skb, RTA_METRICS);
79362306a36Sopenharmony_ci	if (mx == NULL)
79462306a36Sopenharmony_ci		return -ENOBUFS;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	for (i = 0; i < RTAX_MAX; i++) {
79762306a36Sopenharmony_ci		if (metrics[i]) {
79862306a36Sopenharmony_ci			if (i == RTAX_CC_ALGO - 1) {
79962306a36Sopenharmony_ci				char tmp[TCP_CA_NAME_MAX], *name;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci				name = tcp_ca_get_name_by_key(metrics[i], tmp);
80262306a36Sopenharmony_ci				if (!name)
80362306a36Sopenharmony_ci					continue;
80462306a36Sopenharmony_ci				if (nla_put_string(skb, i + 1, name))
80562306a36Sopenharmony_ci					goto nla_put_failure;
80662306a36Sopenharmony_ci			} else if (i == RTAX_FEATURES - 1) {
80762306a36Sopenharmony_ci				u32 user_features = metrics[i] & RTAX_FEATURE_MASK;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci				if (!user_features)
81062306a36Sopenharmony_ci					continue;
81162306a36Sopenharmony_ci				BUILD_BUG_ON(RTAX_FEATURE_MASK & DST_FEATURE_MASK);
81262306a36Sopenharmony_ci				if (nla_put_u32(skb, i + 1, user_features))
81362306a36Sopenharmony_ci					goto nla_put_failure;
81462306a36Sopenharmony_ci			} else {
81562306a36Sopenharmony_ci				if (nla_put_u32(skb, i + 1, metrics[i]))
81662306a36Sopenharmony_ci					goto nla_put_failure;
81762306a36Sopenharmony_ci			}
81862306a36Sopenharmony_ci			valid++;
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (!valid) {
82362306a36Sopenharmony_ci		nla_nest_cancel(skb, mx);
82462306a36Sopenharmony_ci		return 0;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	return nla_nest_end(skb, mx);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cinla_put_failure:
83062306a36Sopenharmony_ci	nla_nest_cancel(skb, mx);
83162306a36Sopenharmony_ci	return -EMSGSIZE;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ciEXPORT_SYMBOL(rtnetlink_put_metrics);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ciint rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
83662306a36Sopenharmony_ci		       long expires, u32 error)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct rta_cacheinfo ci = {
83962306a36Sopenharmony_ci		.rta_error = error,
84062306a36Sopenharmony_ci		.rta_id =  id,
84162306a36Sopenharmony_ci	};
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (dst) {
84462306a36Sopenharmony_ci		ci.rta_lastuse = jiffies_delta_to_clock_t(jiffies - dst->lastuse);
84562306a36Sopenharmony_ci		ci.rta_used = dst->__use;
84662306a36Sopenharmony_ci		ci.rta_clntref = rcuref_read(&dst->__rcuref);
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci	if (expires) {
84962306a36Sopenharmony_ci		unsigned long clock;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		clock = jiffies_to_clock_t(abs(expires));
85262306a36Sopenharmony_ci		clock = min_t(unsigned long, clock, INT_MAX);
85362306a36Sopenharmony_ci		ci.rta_expires = (expires > 0) ? clock : -clock;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci	return nla_put(skb, RTA_CACHEINFO, sizeof(ci), &ci);
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_put_cacheinfo);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic void set_operstate(struct net_device *dev, unsigned char transition)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	unsigned char operstate = dev->operstate;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	switch (transition) {
86462306a36Sopenharmony_ci	case IF_OPER_UP:
86562306a36Sopenharmony_ci		if ((operstate == IF_OPER_DORMANT ||
86662306a36Sopenharmony_ci		     operstate == IF_OPER_TESTING ||
86762306a36Sopenharmony_ci		     operstate == IF_OPER_UNKNOWN) &&
86862306a36Sopenharmony_ci		    !netif_dormant(dev) && !netif_testing(dev))
86962306a36Sopenharmony_ci			operstate = IF_OPER_UP;
87062306a36Sopenharmony_ci		break;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	case IF_OPER_TESTING:
87362306a36Sopenharmony_ci		if (netif_oper_up(dev))
87462306a36Sopenharmony_ci			operstate = IF_OPER_TESTING;
87562306a36Sopenharmony_ci		break;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	case IF_OPER_DORMANT:
87862306a36Sopenharmony_ci		if (netif_oper_up(dev))
87962306a36Sopenharmony_ci			operstate = IF_OPER_DORMANT;
88062306a36Sopenharmony_ci		break;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (dev->operstate != operstate) {
88462306a36Sopenharmony_ci		write_lock(&dev_base_lock);
88562306a36Sopenharmony_ci		dev->operstate = operstate;
88662306a36Sopenharmony_ci		write_unlock(&dev_base_lock);
88762306a36Sopenharmony_ci		netdev_state_change(dev);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic unsigned int rtnl_dev_get_flags(const struct net_device *dev)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	return (dev->flags & ~(IFF_PROMISC | IFF_ALLMULTI)) |
89462306a36Sopenharmony_ci	       (dev->gflags & (IFF_PROMISC | IFF_ALLMULTI));
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic unsigned int rtnl_dev_combine_flags(const struct net_device *dev,
89862306a36Sopenharmony_ci					   const struct ifinfomsg *ifm)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	unsigned int flags = ifm->ifi_flags;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
90362306a36Sopenharmony_ci	if (ifm->ifi_change)
90462306a36Sopenharmony_ci		flags = (flags & ifm->ifi_change) |
90562306a36Sopenharmony_ci			(rtnl_dev_get_flags(dev) & ~ifm->ifi_change);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	return flags;
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_cistatic void copy_rtnl_link_stats(struct rtnl_link_stats *a,
91162306a36Sopenharmony_ci				 const struct rtnl_link_stats64 *b)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	a->rx_packets = b->rx_packets;
91462306a36Sopenharmony_ci	a->tx_packets = b->tx_packets;
91562306a36Sopenharmony_ci	a->rx_bytes = b->rx_bytes;
91662306a36Sopenharmony_ci	a->tx_bytes = b->tx_bytes;
91762306a36Sopenharmony_ci	a->rx_errors = b->rx_errors;
91862306a36Sopenharmony_ci	a->tx_errors = b->tx_errors;
91962306a36Sopenharmony_ci	a->rx_dropped = b->rx_dropped;
92062306a36Sopenharmony_ci	a->tx_dropped = b->tx_dropped;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	a->multicast = b->multicast;
92362306a36Sopenharmony_ci	a->collisions = b->collisions;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	a->rx_length_errors = b->rx_length_errors;
92662306a36Sopenharmony_ci	a->rx_over_errors = b->rx_over_errors;
92762306a36Sopenharmony_ci	a->rx_crc_errors = b->rx_crc_errors;
92862306a36Sopenharmony_ci	a->rx_frame_errors = b->rx_frame_errors;
92962306a36Sopenharmony_ci	a->rx_fifo_errors = b->rx_fifo_errors;
93062306a36Sopenharmony_ci	a->rx_missed_errors = b->rx_missed_errors;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	a->tx_aborted_errors = b->tx_aborted_errors;
93362306a36Sopenharmony_ci	a->tx_carrier_errors = b->tx_carrier_errors;
93462306a36Sopenharmony_ci	a->tx_fifo_errors = b->tx_fifo_errors;
93562306a36Sopenharmony_ci	a->tx_heartbeat_errors = b->tx_heartbeat_errors;
93662306a36Sopenharmony_ci	a->tx_window_errors = b->tx_window_errors;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	a->rx_compressed = b->rx_compressed;
93962306a36Sopenharmony_ci	a->tx_compressed = b->tx_compressed;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	a->rx_nohandler = b->rx_nohandler;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/* All VF info */
94562306a36Sopenharmony_cistatic inline int rtnl_vfinfo_size(const struct net_device *dev,
94662306a36Sopenharmony_ci				   u32 ext_filter_mask)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF)) {
94962306a36Sopenharmony_ci		int num_vfs = dev_num_vf(dev->dev.parent);
95062306a36Sopenharmony_ci		size_t size = nla_total_size(0);
95162306a36Sopenharmony_ci		size += num_vfs *
95262306a36Sopenharmony_ci			(nla_total_size(0) +
95362306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_mac)) +
95462306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_broadcast)) +
95562306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_vlan)) +
95662306a36Sopenharmony_ci			 nla_total_size(0) + /* nest IFLA_VF_VLAN_LIST */
95762306a36Sopenharmony_ci			 nla_total_size(MAX_VLAN_LIST_LEN *
95862306a36Sopenharmony_ci					sizeof(struct ifla_vf_vlan_info)) +
95962306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_spoofchk)) +
96062306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_tx_rate)) +
96162306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_rate)) +
96262306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_link_state)) +
96362306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_rss_query_en)) +
96462306a36Sopenharmony_ci			 nla_total_size(sizeof(struct ifla_vf_trust)));
96562306a36Sopenharmony_ci		if (~ext_filter_mask & RTEXT_FILTER_SKIP_STATS) {
96662306a36Sopenharmony_ci			size += num_vfs *
96762306a36Sopenharmony_ci				(nla_total_size(0) + /* nest IFLA_VF_STATS */
96862306a36Sopenharmony_ci				 /* IFLA_VF_STATS_RX_PACKETS */
96962306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)) +
97062306a36Sopenharmony_ci				 /* IFLA_VF_STATS_TX_PACKETS */
97162306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)) +
97262306a36Sopenharmony_ci				 /* IFLA_VF_STATS_RX_BYTES */
97362306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)) +
97462306a36Sopenharmony_ci				 /* IFLA_VF_STATS_TX_BYTES */
97562306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)) +
97662306a36Sopenharmony_ci				 /* IFLA_VF_STATS_BROADCAST */
97762306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)) +
97862306a36Sopenharmony_ci				 /* IFLA_VF_STATS_MULTICAST */
97962306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)) +
98062306a36Sopenharmony_ci				 /* IFLA_VF_STATS_RX_DROPPED */
98162306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)) +
98262306a36Sopenharmony_ci				 /* IFLA_VF_STATS_TX_DROPPED */
98362306a36Sopenharmony_ci				 nla_total_size_64bit(sizeof(__u64)));
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci		return size;
98662306a36Sopenharmony_ci	} else
98762306a36Sopenharmony_ci		return 0;
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic size_t rtnl_port_size(const struct net_device *dev,
99162306a36Sopenharmony_ci			     u32 ext_filter_mask)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	size_t port_size = nla_total_size(4)		/* PORT_VF */
99462306a36Sopenharmony_ci		+ nla_total_size(PORT_PROFILE_MAX)	/* PORT_PROFILE */
99562306a36Sopenharmony_ci		+ nla_total_size(PORT_UUID_MAX)		/* PORT_INSTANCE_UUID */
99662306a36Sopenharmony_ci		+ nla_total_size(PORT_UUID_MAX)		/* PORT_HOST_UUID */
99762306a36Sopenharmony_ci		+ nla_total_size(1)			/* PROT_VDP_REQUEST */
99862306a36Sopenharmony_ci		+ nla_total_size(2);			/* PORT_VDP_RESPONSE */
99962306a36Sopenharmony_ci	size_t vf_ports_size = nla_total_size(sizeof(struct nlattr));
100062306a36Sopenharmony_ci	size_t vf_port_size = nla_total_size(sizeof(struct nlattr))
100162306a36Sopenharmony_ci		+ port_size;
100262306a36Sopenharmony_ci	size_t port_self_size = nla_total_size(sizeof(struct nlattr))
100362306a36Sopenharmony_ci		+ port_size;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
100662306a36Sopenharmony_ci	    !(ext_filter_mask & RTEXT_FILTER_VF))
100762306a36Sopenharmony_ci		return 0;
100862306a36Sopenharmony_ci	if (dev_num_vf(dev->dev.parent))
100962306a36Sopenharmony_ci		return port_self_size + vf_ports_size +
101062306a36Sopenharmony_ci			vf_port_size * dev_num_vf(dev->dev.parent);
101162306a36Sopenharmony_ci	else
101262306a36Sopenharmony_ci		return port_self_size;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic size_t rtnl_xdp_size(void)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	size_t xdp_size = nla_total_size(0) +	/* nest IFLA_XDP */
101862306a36Sopenharmony_ci			  nla_total_size(1) +	/* XDP_ATTACHED */
101962306a36Sopenharmony_ci			  nla_total_size(4) +	/* XDP_PROG_ID (or 1st mode) */
102062306a36Sopenharmony_ci			  nla_total_size(4);	/* XDP_<mode>_PROG_ID */
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	return xdp_size;
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic size_t rtnl_prop_list_size(const struct net_device *dev)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct netdev_name_node *name_node;
102862306a36Sopenharmony_ci	size_t size;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (list_empty(&dev->name_node->list))
103162306a36Sopenharmony_ci		return 0;
103262306a36Sopenharmony_ci	size = nla_total_size(0);
103362306a36Sopenharmony_ci	list_for_each_entry(name_node, &dev->name_node->list, list)
103462306a36Sopenharmony_ci		size += nla_total_size(ALTIFNAMSIZ);
103562306a36Sopenharmony_ci	return size;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic size_t rtnl_proto_down_size(const struct net_device *dev)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	size_t size = nla_total_size(1);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (dev->proto_down_reason)
104362306a36Sopenharmony_ci		size += nla_total_size(0) + nla_total_size(4);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	return size;
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cistatic size_t rtnl_devlink_port_size(const struct net_device *dev)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	size_t size = nla_total_size(0); /* nest IFLA_DEVLINK_PORT */
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (dev->devlink_port)
105362306a36Sopenharmony_ci		size += devlink_nl_port_handle_size(dev->devlink_port);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	return size;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_cistatic noinline size_t if_nlmsg_size(const struct net_device *dev,
105962306a36Sopenharmony_ci				     u32 ext_filter_mask)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifinfomsg))
106262306a36Sopenharmony_ci	       + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
106362306a36Sopenharmony_ci	       + nla_total_size(IFALIASZ) /* IFLA_IFALIAS */
106462306a36Sopenharmony_ci	       + nla_total_size(IFNAMSIZ) /* IFLA_QDISC */
106562306a36Sopenharmony_ci	       + nla_total_size_64bit(sizeof(struct rtnl_link_ifmap))
106662306a36Sopenharmony_ci	       + nla_total_size(sizeof(struct rtnl_link_stats))
106762306a36Sopenharmony_ci	       + nla_total_size_64bit(sizeof(struct rtnl_link_stats64))
106862306a36Sopenharmony_ci	       + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
106962306a36Sopenharmony_ci	       + nla_total_size(MAX_ADDR_LEN) /* IFLA_BROADCAST */
107062306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_TXQLEN */
107162306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_WEIGHT */
107262306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_MTU */
107362306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_LINK */
107462306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_MASTER */
107562306a36Sopenharmony_ci	       + nla_total_size(1) /* IFLA_CARRIER */
107662306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_PROMISCUITY */
107762306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_ALLMULTI */
107862306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_NUM_TX_QUEUES */
107962306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_NUM_RX_QUEUES */
108062306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_GSO_MAX_SEGS */
108162306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_GSO_MAX_SIZE */
108262306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_GRO_MAX_SIZE */
108362306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_GSO_IPV4_MAX_SIZE */
108462306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_GRO_IPV4_MAX_SIZE */
108562306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_TSO_MAX_SIZE */
108662306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_TSO_MAX_SEGS */
108762306a36Sopenharmony_ci	       + nla_total_size(1) /* IFLA_OPERSTATE */
108862306a36Sopenharmony_ci	       + nla_total_size(1) /* IFLA_LINKMODE */
108962306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_CARRIER_CHANGES */
109062306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_LINK_NETNSID */
109162306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_GROUP */
109262306a36Sopenharmony_ci	       + nla_total_size(ext_filter_mask
109362306a36Sopenharmony_ci			        & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
109462306a36Sopenharmony_ci	       + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
109562306a36Sopenharmony_ci	       + rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
109662306a36Sopenharmony_ci	       + rtnl_link_get_size(dev) /* IFLA_LINKINFO */
109762306a36Sopenharmony_ci	       + rtnl_link_get_af_size(dev, ext_filter_mask) /* IFLA_AF_SPEC */
109862306a36Sopenharmony_ci	       + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */
109962306a36Sopenharmony_ci	       + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */
110062306a36Sopenharmony_ci	       + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */
110162306a36Sopenharmony_ci	       + rtnl_xdp_size() /* IFLA_XDP */
110262306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_EVENT */
110362306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_NEW_NETNSID */
110462306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_NEW_IFINDEX */
110562306a36Sopenharmony_ci	       + rtnl_proto_down_size(dev)  /* proto down */
110662306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_TARGET_NETNSID */
110762306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_CARRIER_UP_COUNT */
110862306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_CARRIER_DOWN_COUNT */
110962306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_MIN_MTU */
111062306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFLA_MAX_MTU */
111162306a36Sopenharmony_ci	       + rtnl_prop_list_size(dev)
111262306a36Sopenharmony_ci	       + nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */
111362306a36Sopenharmony_ci	       + rtnl_devlink_port_size(dev)
111462306a36Sopenharmony_ci	       + 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct nlattr *vf_ports;
112062306a36Sopenharmony_ci	struct nlattr *vf_port;
112162306a36Sopenharmony_ci	int vf;
112262306a36Sopenharmony_ci	int err;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	vf_ports = nla_nest_start_noflag(skb, IFLA_VF_PORTS);
112562306a36Sopenharmony_ci	if (!vf_ports)
112662306a36Sopenharmony_ci		return -EMSGSIZE;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	for (vf = 0; vf < dev_num_vf(dev->dev.parent); vf++) {
112962306a36Sopenharmony_ci		vf_port = nla_nest_start_noflag(skb, IFLA_VF_PORT);
113062306a36Sopenharmony_ci		if (!vf_port)
113162306a36Sopenharmony_ci			goto nla_put_failure;
113262306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_PORT_VF, vf))
113362306a36Sopenharmony_ci			goto nla_put_failure;
113462306a36Sopenharmony_ci		err = dev->netdev_ops->ndo_get_vf_port(dev, vf, skb);
113562306a36Sopenharmony_ci		if (err == -EMSGSIZE)
113662306a36Sopenharmony_ci			goto nla_put_failure;
113762306a36Sopenharmony_ci		if (err) {
113862306a36Sopenharmony_ci			nla_nest_cancel(skb, vf_port);
113962306a36Sopenharmony_ci			continue;
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci		nla_nest_end(skb, vf_port);
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	nla_nest_end(skb, vf_ports);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	return 0;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cinla_put_failure:
114962306a36Sopenharmony_ci	nla_nest_cancel(skb, vf_ports);
115062306a36Sopenharmony_ci	return -EMSGSIZE;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic int rtnl_port_self_fill(struct sk_buff *skb, struct net_device *dev)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct nlattr *port_self;
115662306a36Sopenharmony_ci	int err;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	port_self = nla_nest_start_noflag(skb, IFLA_PORT_SELF);
115962306a36Sopenharmony_ci	if (!port_self)
116062306a36Sopenharmony_ci		return -EMSGSIZE;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	err = dev->netdev_ops->ndo_get_vf_port(dev, PORT_SELF_VF, skb);
116362306a36Sopenharmony_ci	if (err) {
116462306a36Sopenharmony_ci		nla_nest_cancel(skb, port_self);
116562306a36Sopenharmony_ci		return (err == -EMSGSIZE) ? err : 0;
116662306a36Sopenharmony_ci	}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	nla_nest_end(skb, port_self);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	return 0;
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev,
117462306a36Sopenharmony_ci			  u32 ext_filter_mask)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	int err;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
117962306a36Sopenharmony_ci	    !(ext_filter_mask & RTEXT_FILTER_VF))
118062306a36Sopenharmony_ci		return 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	err = rtnl_port_self_fill(skb, dev);
118362306a36Sopenharmony_ci	if (err)
118462306a36Sopenharmony_ci		return err;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (dev_num_vf(dev->dev.parent)) {
118762306a36Sopenharmony_ci		err = rtnl_vf_ports_fill(skb, dev);
118862306a36Sopenharmony_ci		if (err)
118962306a36Sopenharmony_ci			return err;
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	return 0;
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	int err;
119862306a36Sopenharmony_ci	struct netdev_phys_item_id ppid;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	err = dev_get_phys_port_id(dev, &ppid);
120162306a36Sopenharmony_ci	if (err) {
120262306a36Sopenharmony_ci		if (err == -EOPNOTSUPP)
120362306a36Sopenharmony_ci			return 0;
120462306a36Sopenharmony_ci		return err;
120562306a36Sopenharmony_ci	}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (nla_put(skb, IFLA_PHYS_PORT_ID, ppid.id_len, ppid.id))
120862306a36Sopenharmony_ci		return -EMSGSIZE;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	return 0;
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cistatic int rtnl_phys_port_name_fill(struct sk_buff *skb, struct net_device *dev)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	char name[IFNAMSIZ];
121662306a36Sopenharmony_ci	int err;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	err = dev_get_phys_port_name(dev, name, sizeof(name));
121962306a36Sopenharmony_ci	if (err) {
122062306a36Sopenharmony_ci		if (err == -EOPNOTSUPP)
122162306a36Sopenharmony_ci			return 0;
122262306a36Sopenharmony_ci		return err;
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (nla_put_string(skb, IFLA_PHYS_PORT_NAME, name))
122662306a36Sopenharmony_ci		return -EMSGSIZE;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	return 0;
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cistatic int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
123262306a36Sopenharmony_ci{
123362306a36Sopenharmony_ci	struct netdev_phys_item_id ppid = { };
123462306a36Sopenharmony_ci	int err;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	err = dev_get_port_parent_id(dev, &ppid, false);
123762306a36Sopenharmony_ci	if (err) {
123862306a36Sopenharmony_ci		if (err == -EOPNOTSUPP)
123962306a36Sopenharmony_ci			return 0;
124062306a36Sopenharmony_ci		return err;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	if (nla_put(skb, IFLA_PHYS_SWITCH_ID, ppid.id_len, ppid.id))
124462306a36Sopenharmony_ci		return -EMSGSIZE;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	return 0;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_cistatic noinline_for_stack int rtnl_fill_stats(struct sk_buff *skb,
125062306a36Sopenharmony_ci					      struct net_device *dev)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	struct rtnl_link_stats64 *sp;
125362306a36Sopenharmony_ci	struct nlattr *attr;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	attr = nla_reserve_64bit(skb, IFLA_STATS64,
125662306a36Sopenharmony_ci				 sizeof(struct rtnl_link_stats64), IFLA_PAD);
125762306a36Sopenharmony_ci	if (!attr)
125862306a36Sopenharmony_ci		return -EMSGSIZE;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	sp = nla_data(attr);
126162306a36Sopenharmony_ci	dev_get_stats(dev, sp);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	attr = nla_reserve(skb, IFLA_STATS,
126462306a36Sopenharmony_ci			   sizeof(struct rtnl_link_stats));
126562306a36Sopenharmony_ci	if (!attr)
126662306a36Sopenharmony_ci		return -EMSGSIZE;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	copy_rtnl_link_stats(nla_data(attr), sp);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return 0;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
127462306a36Sopenharmony_ci					       struct net_device *dev,
127562306a36Sopenharmony_ci					       int vfs_num,
127662306a36Sopenharmony_ci					       u32 ext_filter_mask)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	struct ifla_vf_rss_query_en vf_rss_query_en;
127962306a36Sopenharmony_ci	struct nlattr *vf, *vfstats, *vfvlanlist;
128062306a36Sopenharmony_ci	struct ifla_vf_link_state vf_linkstate;
128162306a36Sopenharmony_ci	struct ifla_vf_vlan_info vf_vlan_info;
128262306a36Sopenharmony_ci	struct ifla_vf_spoofchk vf_spoofchk;
128362306a36Sopenharmony_ci	struct ifla_vf_tx_rate vf_tx_rate;
128462306a36Sopenharmony_ci	struct ifla_vf_stats vf_stats;
128562306a36Sopenharmony_ci	struct ifla_vf_trust vf_trust;
128662306a36Sopenharmony_ci	struct ifla_vf_vlan vf_vlan;
128762306a36Sopenharmony_ci	struct ifla_vf_rate vf_rate;
128862306a36Sopenharmony_ci	struct ifla_vf_mac vf_mac;
128962306a36Sopenharmony_ci	struct ifla_vf_broadcast vf_broadcast;
129062306a36Sopenharmony_ci	struct ifla_vf_info ivi;
129162306a36Sopenharmony_ci	struct ifla_vf_guid node_guid;
129262306a36Sopenharmony_ci	struct ifla_vf_guid port_guid;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	memset(&ivi, 0, sizeof(ivi));
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	/* Not all SR-IOV capable drivers support the
129762306a36Sopenharmony_ci	 * spoofcheck and "RSS query enable" query.  Preset to
129862306a36Sopenharmony_ci	 * -1 so the user space tool can detect that the driver
129962306a36Sopenharmony_ci	 * didn't report anything.
130062306a36Sopenharmony_ci	 */
130162306a36Sopenharmony_ci	ivi.spoofchk = -1;
130262306a36Sopenharmony_ci	ivi.rss_query_en = -1;
130362306a36Sopenharmony_ci	ivi.trusted = -1;
130462306a36Sopenharmony_ci	/* The default value for VF link state is "auto"
130562306a36Sopenharmony_ci	 * IFLA_VF_LINK_STATE_AUTO which equals zero
130662306a36Sopenharmony_ci	 */
130762306a36Sopenharmony_ci	ivi.linkstate = 0;
130862306a36Sopenharmony_ci	/* VLAN Protocol by default is 802.1Q */
130962306a36Sopenharmony_ci	ivi.vlan_proto = htons(ETH_P_8021Q);
131062306a36Sopenharmony_ci	if (dev->netdev_ops->ndo_get_vf_config(dev, vfs_num, &ivi))
131162306a36Sopenharmony_ci		return 0;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	memset(&vf_vlan_info, 0, sizeof(vf_vlan_info));
131462306a36Sopenharmony_ci	memset(&node_guid, 0, sizeof(node_guid));
131562306a36Sopenharmony_ci	memset(&port_guid, 0, sizeof(port_guid));
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	vf_mac.vf =
131862306a36Sopenharmony_ci		vf_vlan.vf =
131962306a36Sopenharmony_ci		vf_vlan_info.vf =
132062306a36Sopenharmony_ci		vf_rate.vf =
132162306a36Sopenharmony_ci		vf_tx_rate.vf =
132262306a36Sopenharmony_ci		vf_spoofchk.vf =
132362306a36Sopenharmony_ci		vf_linkstate.vf =
132462306a36Sopenharmony_ci		vf_rss_query_en.vf =
132562306a36Sopenharmony_ci		vf_trust.vf =
132662306a36Sopenharmony_ci		node_guid.vf =
132762306a36Sopenharmony_ci		port_guid.vf = ivi.vf;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	memcpy(vf_mac.mac, ivi.mac, sizeof(ivi.mac));
133062306a36Sopenharmony_ci	memcpy(vf_broadcast.broadcast, dev->broadcast, dev->addr_len);
133162306a36Sopenharmony_ci	vf_vlan.vlan = ivi.vlan;
133262306a36Sopenharmony_ci	vf_vlan.qos = ivi.qos;
133362306a36Sopenharmony_ci	vf_vlan_info.vlan = ivi.vlan;
133462306a36Sopenharmony_ci	vf_vlan_info.qos = ivi.qos;
133562306a36Sopenharmony_ci	vf_vlan_info.vlan_proto = ivi.vlan_proto;
133662306a36Sopenharmony_ci	vf_tx_rate.rate = ivi.max_tx_rate;
133762306a36Sopenharmony_ci	vf_rate.min_tx_rate = ivi.min_tx_rate;
133862306a36Sopenharmony_ci	vf_rate.max_tx_rate = ivi.max_tx_rate;
133962306a36Sopenharmony_ci	vf_spoofchk.setting = ivi.spoofchk;
134062306a36Sopenharmony_ci	vf_linkstate.link_state = ivi.linkstate;
134162306a36Sopenharmony_ci	vf_rss_query_en.setting = ivi.rss_query_en;
134262306a36Sopenharmony_ci	vf_trust.setting = ivi.trusted;
134362306a36Sopenharmony_ci	vf = nla_nest_start_noflag(skb, IFLA_VF_INFO);
134462306a36Sopenharmony_ci	if (!vf)
134562306a36Sopenharmony_ci		return -EMSGSIZE;
134662306a36Sopenharmony_ci	if (nla_put(skb, IFLA_VF_MAC, sizeof(vf_mac), &vf_mac) ||
134762306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_BROADCAST, sizeof(vf_broadcast), &vf_broadcast) ||
134862306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_VLAN, sizeof(vf_vlan), &vf_vlan) ||
134962306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_RATE, sizeof(vf_rate),
135062306a36Sopenharmony_ci		    &vf_rate) ||
135162306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_TX_RATE, sizeof(vf_tx_rate),
135262306a36Sopenharmony_ci		    &vf_tx_rate) ||
135362306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_SPOOFCHK, sizeof(vf_spoofchk),
135462306a36Sopenharmony_ci		    &vf_spoofchk) ||
135562306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_LINK_STATE, sizeof(vf_linkstate),
135662306a36Sopenharmony_ci		    &vf_linkstate) ||
135762306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_RSS_QUERY_EN,
135862306a36Sopenharmony_ci		    sizeof(vf_rss_query_en),
135962306a36Sopenharmony_ci		    &vf_rss_query_en) ||
136062306a36Sopenharmony_ci	    nla_put(skb, IFLA_VF_TRUST,
136162306a36Sopenharmony_ci		    sizeof(vf_trust), &vf_trust))
136262306a36Sopenharmony_ci		goto nla_put_vf_failure;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (dev->netdev_ops->ndo_get_vf_guid &&
136562306a36Sopenharmony_ci	    !dev->netdev_ops->ndo_get_vf_guid(dev, vfs_num, &node_guid,
136662306a36Sopenharmony_ci					      &port_guid)) {
136762306a36Sopenharmony_ci		if (nla_put(skb, IFLA_VF_IB_NODE_GUID, sizeof(node_guid),
136862306a36Sopenharmony_ci			    &node_guid) ||
136962306a36Sopenharmony_ci		    nla_put(skb, IFLA_VF_IB_PORT_GUID, sizeof(port_guid),
137062306a36Sopenharmony_ci			    &port_guid))
137162306a36Sopenharmony_ci			goto nla_put_vf_failure;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci	vfvlanlist = nla_nest_start_noflag(skb, IFLA_VF_VLAN_LIST);
137462306a36Sopenharmony_ci	if (!vfvlanlist)
137562306a36Sopenharmony_ci		goto nla_put_vf_failure;
137662306a36Sopenharmony_ci	if (nla_put(skb, IFLA_VF_VLAN_INFO, sizeof(vf_vlan_info),
137762306a36Sopenharmony_ci		    &vf_vlan_info)) {
137862306a36Sopenharmony_ci		nla_nest_cancel(skb, vfvlanlist);
137962306a36Sopenharmony_ci		goto nla_put_vf_failure;
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci	nla_nest_end(skb, vfvlanlist);
138262306a36Sopenharmony_ci	if (~ext_filter_mask & RTEXT_FILTER_SKIP_STATS) {
138362306a36Sopenharmony_ci		memset(&vf_stats, 0, sizeof(vf_stats));
138462306a36Sopenharmony_ci		if (dev->netdev_ops->ndo_get_vf_stats)
138562306a36Sopenharmony_ci			dev->netdev_ops->ndo_get_vf_stats(dev, vfs_num,
138662306a36Sopenharmony_ci							  &vf_stats);
138762306a36Sopenharmony_ci		vfstats = nla_nest_start_noflag(skb, IFLA_VF_STATS);
138862306a36Sopenharmony_ci		if (!vfstats)
138962306a36Sopenharmony_ci			goto nla_put_vf_failure;
139062306a36Sopenharmony_ci		if (nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_PACKETS,
139162306a36Sopenharmony_ci				      vf_stats.rx_packets, IFLA_VF_STATS_PAD) ||
139262306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_PACKETS,
139362306a36Sopenharmony_ci				      vf_stats.tx_packets, IFLA_VF_STATS_PAD) ||
139462306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_BYTES,
139562306a36Sopenharmony_ci				      vf_stats.rx_bytes, IFLA_VF_STATS_PAD) ||
139662306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_BYTES,
139762306a36Sopenharmony_ci				      vf_stats.tx_bytes, IFLA_VF_STATS_PAD) ||
139862306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, IFLA_VF_STATS_BROADCAST,
139962306a36Sopenharmony_ci				      vf_stats.broadcast, IFLA_VF_STATS_PAD) ||
140062306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, IFLA_VF_STATS_MULTICAST,
140162306a36Sopenharmony_ci				      vf_stats.multicast, IFLA_VF_STATS_PAD) ||
140262306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_DROPPED,
140362306a36Sopenharmony_ci				      vf_stats.rx_dropped, IFLA_VF_STATS_PAD) ||
140462306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_DROPPED,
140562306a36Sopenharmony_ci				      vf_stats.tx_dropped, IFLA_VF_STATS_PAD)) {
140662306a36Sopenharmony_ci			nla_nest_cancel(skb, vfstats);
140762306a36Sopenharmony_ci			goto nla_put_vf_failure;
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci		nla_nest_end(skb, vfstats);
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci	nla_nest_end(skb, vf);
141262306a36Sopenharmony_ci	return 0;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_cinla_put_vf_failure:
141562306a36Sopenharmony_ci	nla_nest_cancel(skb, vf);
141662306a36Sopenharmony_ci	return -EMSGSIZE;
141762306a36Sopenharmony_ci}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_cistatic noinline_for_stack int rtnl_fill_vf(struct sk_buff *skb,
142062306a36Sopenharmony_ci					   struct net_device *dev,
142162306a36Sopenharmony_ci					   u32 ext_filter_mask)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	struct nlattr *vfinfo;
142462306a36Sopenharmony_ci	int i, num_vfs;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (!dev->dev.parent || ((ext_filter_mask & RTEXT_FILTER_VF) == 0))
142762306a36Sopenharmony_ci		return 0;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	num_vfs = dev_num_vf(dev->dev.parent);
143062306a36Sopenharmony_ci	if (nla_put_u32(skb, IFLA_NUM_VF, num_vfs))
143162306a36Sopenharmony_ci		return -EMSGSIZE;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	if (!dev->netdev_ops->ndo_get_vf_config)
143462306a36Sopenharmony_ci		return 0;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	vfinfo = nla_nest_start_noflag(skb, IFLA_VFINFO_LIST);
143762306a36Sopenharmony_ci	if (!vfinfo)
143862306a36Sopenharmony_ci		return -EMSGSIZE;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	for (i = 0; i < num_vfs; i++) {
144162306a36Sopenharmony_ci		if (rtnl_fill_vfinfo(skb, dev, i, ext_filter_mask)) {
144262306a36Sopenharmony_ci			nla_nest_cancel(skb, vfinfo);
144362306a36Sopenharmony_ci			return -EMSGSIZE;
144462306a36Sopenharmony_ci		}
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	nla_nest_end(skb, vfinfo);
144862306a36Sopenharmony_ci	return 0;
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_cistatic int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev)
145262306a36Sopenharmony_ci{
145362306a36Sopenharmony_ci	struct rtnl_link_ifmap map;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	memset(&map, 0, sizeof(map));
145662306a36Sopenharmony_ci	map.mem_start   = dev->mem_start;
145762306a36Sopenharmony_ci	map.mem_end     = dev->mem_end;
145862306a36Sopenharmony_ci	map.base_addr   = dev->base_addr;
145962306a36Sopenharmony_ci	map.irq         = dev->irq;
146062306a36Sopenharmony_ci	map.dma         = dev->dma;
146162306a36Sopenharmony_ci	map.port        = dev->if_port;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (nla_put_64bit(skb, IFLA_MAP, sizeof(map), &map, IFLA_PAD))
146462306a36Sopenharmony_ci		return -EMSGSIZE;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	return 0;
146762306a36Sopenharmony_ci}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_cistatic u32 rtnl_xdp_prog_skb(struct net_device *dev)
147062306a36Sopenharmony_ci{
147162306a36Sopenharmony_ci	const struct bpf_prog *generic_xdp_prog;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	ASSERT_RTNL();
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	generic_xdp_prog = rtnl_dereference(dev->xdp_prog);
147662306a36Sopenharmony_ci	if (!generic_xdp_prog)
147762306a36Sopenharmony_ci		return 0;
147862306a36Sopenharmony_ci	return generic_xdp_prog->aux->id;
147962306a36Sopenharmony_ci}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic u32 rtnl_xdp_prog_drv(struct net_device *dev)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	return dev_xdp_prog_id(dev, XDP_MODE_DRV);
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic u32 rtnl_xdp_prog_hw(struct net_device *dev)
148762306a36Sopenharmony_ci{
148862306a36Sopenharmony_ci	return dev_xdp_prog_id(dev, XDP_MODE_HW);
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_cistatic int rtnl_xdp_report_one(struct sk_buff *skb, struct net_device *dev,
149262306a36Sopenharmony_ci			       u32 *prog_id, u8 *mode, u8 tgt_mode, u32 attr,
149362306a36Sopenharmony_ci			       u32 (*get_prog_id)(struct net_device *dev))
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	u32 curr_id;
149662306a36Sopenharmony_ci	int err;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	curr_id = get_prog_id(dev);
149962306a36Sopenharmony_ci	if (!curr_id)
150062306a36Sopenharmony_ci		return 0;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	*prog_id = curr_id;
150362306a36Sopenharmony_ci	err = nla_put_u32(skb, attr, curr_id);
150462306a36Sopenharmony_ci	if (err)
150562306a36Sopenharmony_ci		return err;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	if (*mode != XDP_ATTACHED_NONE)
150862306a36Sopenharmony_ci		*mode = XDP_ATTACHED_MULTI;
150962306a36Sopenharmony_ci	else
151062306a36Sopenharmony_ci		*mode = tgt_mode;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	return 0;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct nlattr *xdp;
151862306a36Sopenharmony_ci	u32 prog_id;
151962306a36Sopenharmony_ci	int err;
152062306a36Sopenharmony_ci	u8 mode;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	xdp = nla_nest_start_noflag(skb, IFLA_XDP);
152362306a36Sopenharmony_ci	if (!xdp)
152462306a36Sopenharmony_ci		return -EMSGSIZE;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	prog_id = 0;
152762306a36Sopenharmony_ci	mode = XDP_ATTACHED_NONE;
152862306a36Sopenharmony_ci	err = rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_SKB,
152962306a36Sopenharmony_ci				  IFLA_XDP_SKB_PROG_ID, rtnl_xdp_prog_skb);
153062306a36Sopenharmony_ci	if (err)
153162306a36Sopenharmony_ci		goto err_cancel;
153262306a36Sopenharmony_ci	err = rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_DRV,
153362306a36Sopenharmony_ci				  IFLA_XDP_DRV_PROG_ID, rtnl_xdp_prog_drv);
153462306a36Sopenharmony_ci	if (err)
153562306a36Sopenharmony_ci		goto err_cancel;
153662306a36Sopenharmony_ci	err = rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_HW,
153762306a36Sopenharmony_ci				  IFLA_XDP_HW_PROG_ID, rtnl_xdp_prog_hw);
153862306a36Sopenharmony_ci	if (err)
153962306a36Sopenharmony_ci		goto err_cancel;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	err = nla_put_u8(skb, IFLA_XDP_ATTACHED, mode);
154262306a36Sopenharmony_ci	if (err)
154362306a36Sopenharmony_ci		goto err_cancel;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	if (prog_id && mode != XDP_ATTACHED_MULTI) {
154662306a36Sopenharmony_ci		err = nla_put_u32(skb, IFLA_XDP_PROG_ID, prog_id);
154762306a36Sopenharmony_ci		if (err)
154862306a36Sopenharmony_ci			goto err_cancel;
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	nla_nest_end(skb, xdp);
155262306a36Sopenharmony_ci	return 0;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cierr_cancel:
155562306a36Sopenharmony_ci	nla_nest_cancel(skb, xdp);
155662306a36Sopenharmony_ci	return err;
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic u32 rtnl_get_event(unsigned long event)
156062306a36Sopenharmony_ci{
156162306a36Sopenharmony_ci	u32 rtnl_event_type = IFLA_EVENT_NONE;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	switch (event) {
156462306a36Sopenharmony_ci	case NETDEV_REBOOT:
156562306a36Sopenharmony_ci		rtnl_event_type = IFLA_EVENT_REBOOT;
156662306a36Sopenharmony_ci		break;
156762306a36Sopenharmony_ci	case NETDEV_FEAT_CHANGE:
156862306a36Sopenharmony_ci		rtnl_event_type = IFLA_EVENT_FEATURES;
156962306a36Sopenharmony_ci		break;
157062306a36Sopenharmony_ci	case NETDEV_BONDING_FAILOVER:
157162306a36Sopenharmony_ci		rtnl_event_type = IFLA_EVENT_BONDING_FAILOVER;
157262306a36Sopenharmony_ci		break;
157362306a36Sopenharmony_ci	case NETDEV_NOTIFY_PEERS:
157462306a36Sopenharmony_ci		rtnl_event_type = IFLA_EVENT_NOTIFY_PEERS;
157562306a36Sopenharmony_ci		break;
157662306a36Sopenharmony_ci	case NETDEV_RESEND_IGMP:
157762306a36Sopenharmony_ci		rtnl_event_type = IFLA_EVENT_IGMP_RESEND;
157862306a36Sopenharmony_ci		break;
157962306a36Sopenharmony_ci	case NETDEV_CHANGEINFODATA:
158062306a36Sopenharmony_ci		rtnl_event_type = IFLA_EVENT_BONDING_OPTIONS;
158162306a36Sopenharmony_ci		break;
158262306a36Sopenharmony_ci	default:
158362306a36Sopenharmony_ci		break;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	return rtnl_event_type;
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_cistatic int put_master_ifindex(struct sk_buff *skb, struct net_device *dev)
159062306a36Sopenharmony_ci{
159162306a36Sopenharmony_ci	const struct net_device *upper_dev;
159262306a36Sopenharmony_ci	int ret = 0;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	rcu_read_lock();
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	upper_dev = netdev_master_upper_dev_get_rcu(dev);
159762306a36Sopenharmony_ci	if (upper_dev)
159862306a36Sopenharmony_ci		ret = nla_put_u32(skb, IFLA_MASTER, upper_dev->ifindex);
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	rcu_read_unlock();
160162306a36Sopenharmony_ci	return ret;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_cistatic int nla_put_iflink(struct sk_buff *skb, const struct net_device *dev,
160562306a36Sopenharmony_ci			  bool force)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	int ifindex = dev_get_iflink(dev);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	if (force || dev->ifindex != ifindex)
161062306a36Sopenharmony_ci		return nla_put_u32(skb, IFLA_LINK, ifindex);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	return 0;
161362306a36Sopenharmony_ci}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_cistatic noinline_for_stack int nla_put_ifalias(struct sk_buff *skb,
161662306a36Sopenharmony_ci					      struct net_device *dev)
161762306a36Sopenharmony_ci{
161862306a36Sopenharmony_ci	char buf[IFALIASZ];
161962306a36Sopenharmony_ci	int ret;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	ret = dev_get_alias(dev, buf, sizeof(buf));
162262306a36Sopenharmony_ci	return ret > 0 ? nla_put_string(skb, IFLA_IFALIAS, buf) : 0;
162362306a36Sopenharmony_ci}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic int rtnl_fill_link_netnsid(struct sk_buff *skb,
162662306a36Sopenharmony_ci				  const struct net_device *dev,
162762306a36Sopenharmony_ci				  struct net *src_net, gfp_t gfp)
162862306a36Sopenharmony_ci{
162962306a36Sopenharmony_ci	bool put_iflink = false;
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	if (dev->rtnl_link_ops && dev->rtnl_link_ops->get_link_net) {
163262306a36Sopenharmony_ci		struct net *link_net = dev->rtnl_link_ops->get_link_net(dev);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci		if (!net_eq(dev_net(dev), link_net)) {
163562306a36Sopenharmony_ci			int id = peernet2id_alloc(src_net, link_net, gfp);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci			if (nla_put_s32(skb, IFLA_LINK_NETNSID, id))
163862306a36Sopenharmony_ci				return -EMSGSIZE;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci			put_iflink = true;
164162306a36Sopenharmony_ci		}
164262306a36Sopenharmony_ci	}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	return nla_put_iflink(skb, dev, put_iflink);
164562306a36Sopenharmony_ci}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_cistatic int rtnl_fill_link_af(struct sk_buff *skb,
164862306a36Sopenharmony_ci			     const struct net_device *dev,
164962306a36Sopenharmony_ci			     u32 ext_filter_mask)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	const struct rtnl_af_ops *af_ops;
165262306a36Sopenharmony_ci	struct nlattr *af_spec;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	af_spec = nla_nest_start_noflag(skb, IFLA_AF_SPEC);
165562306a36Sopenharmony_ci	if (!af_spec)
165662306a36Sopenharmony_ci		return -EMSGSIZE;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	list_for_each_entry_rcu(af_ops, &rtnl_af_ops, list) {
165962306a36Sopenharmony_ci		struct nlattr *af;
166062306a36Sopenharmony_ci		int err;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci		if (!af_ops->fill_link_af)
166362306a36Sopenharmony_ci			continue;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci		af = nla_nest_start_noflag(skb, af_ops->family);
166662306a36Sopenharmony_ci		if (!af)
166762306a36Sopenharmony_ci			return -EMSGSIZE;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci		err = af_ops->fill_link_af(skb, dev, ext_filter_mask);
167062306a36Sopenharmony_ci		/*
167162306a36Sopenharmony_ci		 * Caller may return ENODATA to indicate that there
167262306a36Sopenharmony_ci		 * was no data to be dumped. This is not an error, it
167362306a36Sopenharmony_ci		 * means we should trim the attribute header and
167462306a36Sopenharmony_ci		 * continue.
167562306a36Sopenharmony_ci		 */
167662306a36Sopenharmony_ci		if (err == -ENODATA)
167762306a36Sopenharmony_ci			nla_nest_cancel(skb, af);
167862306a36Sopenharmony_ci		else if (err < 0)
167962306a36Sopenharmony_ci			return -EMSGSIZE;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci		nla_nest_end(skb, af);
168262306a36Sopenharmony_ci	}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	nla_nest_end(skb, af_spec);
168562306a36Sopenharmony_ci	return 0;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_cistatic int rtnl_fill_alt_ifnames(struct sk_buff *skb,
168962306a36Sopenharmony_ci				 const struct net_device *dev)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	struct netdev_name_node *name_node;
169262306a36Sopenharmony_ci	int count = 0;
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	list_for_each_entry(name_node, &dev->name_node->list, list) {
169562306a36Sopenharmony_ci		if (nla_put_string(skb, IFLA_ALT_IFNAME, name_node->name))
169662306a36Sopenharmony_ci			return -EMSGSIZE;
169762306a36Sopenharmony_ci		count++;
169862306a36Sopenharmony_ci	}
169962306a36Sopenharmony_ci	return count;
170062306a36Sopenharmony_ci}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_cistatic int rtnl_fill_prop_list(struct sk_buff *skb,
170362306a36Sopenharmony_ci			       const struct net_device *dev)
170462306a36Sopenharmony_ci{
170562306a36Sopenharmony_ci	struct nlattr *prop_list;
170662306a36Sopenharmony_ci	int ret;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	prop_list = nla_nest_start(skb, IFLA_PROP_LIST);
170962306a36Sopenharmony_ci	if (!prop_list)
171062306a36Sopenharmony_ci		return -EMSGSIZE;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	ret = rtnl_fill_alt_ifnames(skb, dev);
171362306a36Sopenharmony_ci	if (ret <= 0)
171462306a36Sopenharmony_ci		goto nest_cancel;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	nla_nest_end(skb, prop_list);
171762306a36Sopenharmony_ci	return 0;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_cinest_cancel:
172062306a36Sopenharmony_ci	nla_nest_cancel(skb, prop_list);
172162306a36Sopenharmony_ci	return ret;
172262306a36Sopenharmony_ci}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_cistatic int rtnl_fill_proto_down(struct sk_buff *skb,
172562306a36Sopenharmony_ci				const struct net_device *dev)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	struct nlattr *pr;
172862306a36Sopenharmony_ci	u32 preason;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down))
173162306a36Sopenharmony_ci		goto nla_put_failure;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	preason = dev->proto_down_reason;
173462306a36Sopenharmony_ci	if (!preason)
173562306a36Sopenharmony_ci		return 0;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	pr = nla_nest_start(skb, IFLA_PROTO_DOWN_REASON);
173862306a36Sopenharmony_ci	if (!pr)
173962306a36Sopenharmony_ci		return -EMSGSIZE;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	if (nla_put_u32(skb, IFLA_PROTO_DOWN_REASON_VALUE, preason)) {
174262306a36Sopenharmony_ci		nla_nest_cancel(skb, pr);
174362306a36Sopenharmony_ci		goto nla_put_failure;
174462306a36Sopenharmony_ci	}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	nla_nest_end(skb, pr);
174762306a36Sopenharmony_ci	return 0;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_cinla_put_failure:
175062306a36Sopenharmony_ci	return -EMSGSIZE;
175162306a36Sopenharmony_ci}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_cistatic int rtnl_fill_devlink_port(struct sk_buff *skb,
175462306a36Sopenharmony_ci				  const struct net_device *dev)
175562306a36Sopenharmony_ci{
175662306a36Sopenharmony_ci	struct nlattr *devlink_port_nest;
175762306a36Sopenharmony_ci	int ret;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	devlink_port_nest = nla_nest_start(skb, IFLA_DEVLINK_PORT);
176062306a36Sopenharmony_ci	if (!devlink_port_nest)
176162306a36Sopenharmony_ci		return -EMSGSIZE;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	if (dev->devlink_port) {
176462306a36Sopenharmony_ci		ret = devlink_nl_port_handle_fill(skb, dev->devlink_port);
176562306a36Sopenharmony_ci		if (ret < 0)
176662306a36Sopenharmony_ci			goto nest_cancel;
176762306a36Sopenharmony_ci	}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	nla_nest_end(skb, devlink_port_nest);
177062306a36Sopenharmony_ci	return 0;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_cinest_cancel:
177362306a36Sopenharmony_ci	nla_nest_cancel(skb, devlink_port_nest);
177462306a36Sopenharmony_ci	return ret;
177562306a36Sopenharmony_ci}
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_cistatic int rtnl_fill_ifinfo(struct sk_buff *skb,
177862306a36Sopenharmony_ci			    struct net_device *dev, struct net *src_net,
177962306a36Sopenharmony_ci			    int type, u32 pid, u32 seq, u32 change,
178062306a36Sopenharmony_ci			    unsigned int flags, u32 ext_filter_mask,
178162306a36Sopenharmony_ci			    u32 event, int *new_nsid, int new_ifindex,
178262306a36Sopenharmony_ci			    int tgt_netnsid, gfp_t gfp)
178362306a36Sopenharmony_ci{
178462306a36Sopenharmony_ci	struct ifinfomsg *ifm;
178562306a36Sopenharmony_ci	struct nlmsghdr *nlh;
178662306a36Sopenharmony_ci	struct Qdisc *qdisc;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	ASSERT_RTNL();
178962306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
179062306a36Sopenharmony_ci	if (nlh == NULL)
179162306a36Sopenharmony_ci		return -EMSGSIZE;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
179462306a36Sopenharmony_ci	ifm->ifi_family = AF_UNSPEC;
179562306a36Sopenharmony_ci	ifm->__ifi_pad = 0;
179662306a36Sopenharmony_ci	ifm->ifi_type = dev->type;
179762306a36Sopenharmony_ci	ifm->ifi_index = dev->ifindex;
179862306a36Sopenharmony_ci	ifm->ifi_flags = dev_get_flags(dev);
179962306a36Sopenharmony_ci	ifm->ifi_change = change;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_TARGET_NETNSID, tgt_netnsid))
180262306a36Sopenharmony_ci		goto nla_put_failure;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	qdisc = rtnl_dereference(dev->qdisc);
180562306a36Sopenharmony_ci	if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
180662306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) ||
180762306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_OPERSTATE,
180862306a36Sopenharmony_ci		       netif_running(dev) ? dev->operstate : IF_OPER_DOWN) ||
180962306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_LINKMODE, dev->link_mode) ||
181062306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
181162306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_MIN_MTU, dev->min_mtu) ||
181262306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_MAX_MTU, dev->max_mtu) ||
181362306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GROUP, dev->group) ||
181462306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_PROMISCUITY, dev->promiscuity) ||
181562306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_ALLMULTI, dev->allmulti) ||
181662306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_NUM_TX_QUEUES, dev->num_tx_queues) ||
181762306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GSO_MAX_SEGS, dev->gso_max_segs) ||
181862306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GSO_MAX_SIZE, dev->gso_max_size) ||
181962306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GRO_MAX_SIZE, dev->gro_max_size) ||
182062306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GSO_IPV4_MAX_SIZE, dev->gso_ipv4_max_size) ||
182162306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_GRO_IPV4_MAX_SIZE, dev->gro_ipv4_max_size) ||
182262306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_TSO_MAX_SIZE, dev->tso_max_size) ||
182362306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_TSO_MAX_SEGS, dev->tso_max_segs) ||
182462306a36Sopenharmony_ci#ifdef CONFIG_RPS
182562306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_NUM_RX_QUEUES, dev->num_rx_queues) ||
182662306a36Sopenharmony_ci#endif
182762306a36Sopenharmony_ci	    put_master_ifindex(skb, dev) ||
182862306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
182962306a36Sopenharmony_ci	    (qdisc &&
183062306a36Sopenharmony_ci	     nla_put_string(skb, IFLA_QDISC, qdisc->ops->id)) ||
183162306a36Sopenharmony_ci	    nla_put_ifalias(skb, dev) ||
183262306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_CARRIER_CHANGES,
183362306a36Sopenharmony_ci			atomic_read(&dev->carrier_up_count) +
183462306a36Sopenharmony_ci			atomic_read(&dev->carrier_down_count)) ||
183562306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_CARRIER_UP_COUNT,
183662306a36Sopenharmony_ci			atomic_read(&dev->carrier_up_count)) ||
183762306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_CARRIER_DOWN_COUNT,
183862306a36Sopenharmony_ci			atomic_read(&dev->carrier_down_count)))
183962306a36Sopenharmony_ci		goto nla_put_failure;
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	if (rtnl_fill_proto_down(skb, dev))
184262306a36Sopenharmony_ci		goto nla_put_failure;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	if (event != IFLA_EVENT_NONE) {
184562306a36Sopenharmony_ci		if (nla_put_u32(skb, IFLA_EVENT, event))
184662306a36Sopenharmony_ci			goto nla_put_failure;
184762306a36Sopenharmony_ci	}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	if (rtnl_fill_link_ifmap(skb, dev))
185062306a36Sopenharmony_ci		goto nla_put_failure;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	if (dev->addr_len) {
185362306a36Sopenharmony_ci		if (nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr) ||
185462306a36Sopenharmony_ci		    nla_put(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast))
185562306a36Sopenharmony_ci			goto nla_put_failure;
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	if (rtnl_phys_port_id_fill(skb, dev))
185962306a36Sopenharmony_ci		goto nla_put_failure;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	if (rtnl_phys_port_name_fill(skb, dev))
186262306a36Sopenharmony_ci		goto nla_put_failure;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	if (rtnl_phys_switch_id_fill(skb, dev))
186562306a36Sopenharmony_ci		goto nla_put_failure;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	if (rtnl_fill_stats(skb, dev))
186862306a36Sopenharmony_ci		goto nla_put_failure;
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	if (rtnl_fill_vf(skb, dev, ext_filter_mask))
187162306a36Sopenharmony_ci		goto nla_put_failure;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (rtnl_port_fill(skb, dev, ext_filter_mask))
187462306a36Sopenharmony_ci		goto nla_put_failure;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (rtnl_xdp_fill(skb, dev))
187762306a36Sopenharmony_ci		goto nla_put_failure;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) {
188062306a36Sopenharmony_ci		if (rtnl_link_fill(skb, dev) < 0)
188162306a36Sopenharmony_ci			goto nla_put_failure;
188262306a36Sopenharmony_ci	}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	if (rtnl_fill_link_netnsid(skb, dev, src_net, gfp))
188562306a36Sopenharmony_ci		goto nla_put_failure;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	if (new_nsid &&
188862306a36Sopenharmony_ci	    nla_put_s32(skb, IFLA_NEW_NETNSID, *new_nsid) < 0)
188962306a36Sopenharmony_ci		goto nla_put_failure;
189062306a36Sopenharmony_ci	if (new_ifindex &&
189162306a36Sopenharmony_ci	    nla_put_s32(skb, IFLA_NEW_IFINDEX, new_ifindex) < 0)
189262306a36Sopenharmony_ci		goto nla_put_failure;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	if (memchr_inv(dev->perm_addr, '\0', dev->addr_len) &&
189562306a36Sopenharmony_ci	    nla_put(skb, IFLA_PERM_ADDRESS, dev->addr_len, dev->perm_addr))
189662306a36Sopenharmony_ci		goto nla_put_failure;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	rcu_read_lock();
189962306a36Sopenharmony_ci	if (rtnl_fill_link_af(skb, dev, ext_filter_mask))
190062306a36Sopenharmony_ci		goto nla_put_failure_rcu;
190162306a36Sopenharmony_ci	rcu_read_unlock();
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	if (rtnl_fill_prop_list(skb, dev))
190462306a36Sopenharmony_ci		goto nla_put_failure;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	if (dev->dev.parent &&
190762306a36Sopenharmony_ci	    nla_put_string(skb, IFLA_PARENT_DEV_NAME,
190862306a36Sopenharmony_ci			   dev_name(dev->dev.parent)))
190962306a36Sopenharmony_ci		goto nla_put_failure;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	if (dev->dev.parent && dev->dev.parent->bus &&
191262306a36Sopenharmony_ci	    nla_put_string(skb, IFLA_PARENT_DEV_BUS_NAME,
191362306a36Sopenharmony_ci			   dev->dev.parent->bus->name))
191462306a36Sopenharmony_ci		goto nla_put_failure;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if (rtnl_fill_devlink_port(skb, dev))
191762306a36Sopenharmony_ci		goto nla_put_failure;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
192062306a36Sopenharmony_ci	return 0;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cinla_put_failure_rcu:
192362306a36Sopenharmony_ci	rcu_read_unlock();
192462306a36Sopenharmony_cinla_put_failure:
192562306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
192662306a36Sopenharmony_ci	return -EMSGSIZE;
192762306a36Sopenharmony_ci}
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_cistatic const struct nla_policy ifla_policy[IFLA_MAX+1] = {
193062306a36Sopenharmony_ci	[IFLA_IFNAME]		= { .type = NLA_STRING, .len = IFNAMSIZ-1 },
193162306a36Sopenharmony_ci	[IFLA_ADDRESS]		= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
193262306a36Sopenharmony_ci	[IFLA_BROADCAST]	= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
193362306a36Sopenharmony_ci	[IFLA_MAP]		= { .len = sizeof(struct rtnl_link_ifmap) },
193462306a36Sopenharmony_ci	[IFLA_MTU]		= { .type = NLA_U32 },
193562306a36Sopenharmony_ci	[IFLA_LINK]		= { .type = NLA_U32 },
193662306a36Sopenharmony_ci	[IFLA_MASTER]		= { .type = NLA_U32 },
193762306a36Sopenharmony_ci	[IFLA_CARRIER]		= { .type = NLA_U8 },
193862306a36Sopenharmony_ci	[IFLA_TXQLEN]		= { .type = NLA_U32 },
193962306a36Sopenharmony_ci	[IFLA_WEIGHT]		= { .type = NLA_U32 },
194062306a36Sopenharmony_ci	[IFLA_OPERSTATE]	= { .type = NLA_U8 },
194162306a36Sopenharmony_ci	[IFLA_LINKMODE]		= { .type = NLA_U8 },
194262306a36Sopenharmony_ci	[IFLA_LINKINFO]		= { .type = NLA_NESTED },
194362306a36Sopenharmony_ci	[IFLA_NET_NS_PID]	= { .type = NLA_U32 },
194462306a36Sopenharmony_ci	[IFLA_NET_NS_FD]	= { .type = NLA_U32 },
194562306a36Sopenharmony_ci	/* IFLA_IFALIAS is a string, but policy is set to NLA_BINARY to
194662306a36Sopenharmony_ci	 * allow 0-length string (needed to remove an alias).
194762306a36Sopenharmony_ci	 */
194862306a36Sopenharmony_ci	[IFLA_IFALIAS]	        = { .type = NLA_BINARY, .len = IFALIASZ - 1 },
194962306a36Sopenharmony_ci	[IFLA_VFINFO_LIST]	= {. type = NLA_NESTED },
195062306a36Sopenharmony_ci	[IFLA_VF_PORTS]		= { .type = NLA_NESTED },
195162306a36Sopenharmony_ci	[IFLA_PORT_SELF]	= { .type = NLA_NESTED },
195262306a36Sopenharmony_ci	[IFLA_AF_SPEC]		= { .type = NLA_NESTED },
195362306a36Sopenharmony_ci	[IFLA_EXT_MASK]		= { .type = NLA_U32 },
195462306a36Sopenharmony_ci	[IFLA_PROMISCUITY]	= { .type = NLA_U32 },
195562306a36Sopenharmony_ci	[IFLA_NUM_TX_QUEUES]	= { .type = NLA_U32 },
195662306a36Sopenharmony_ci	[IFLA_NUM_RX_QUEUES]	= { .type = NLA_U32 },
195762306a36Sopenharmony_ci	[IFLA_GSO_MAX_SEGS]	= { .type = NLA_U32 },
195862306a36Sopenharmony_ci	[IFLA_GSO_MAX_SIZE]	= { .type = NLA_U32 },
195962306a36Sopenharmony_ci	[IFLA_PHYS_PORT_ID]	= { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
196062306a36Sopenharmony_ci	[IFLA_CARRIER_CHANGES]	= { .type = NLA_U32 },  /* ignored */
196162306a36Sopenharmony_ci	[IFLA_PHYS_SWITCH_ID]	= { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
196262306a36Sopenharmony_ci	[IFLA_LINK_NETNSID]	= { .type = NLA_S32 },
196362306a36Sopenharmony_ci	[IFLA_PROTO_DOWN]	= { .type = NLA_U8 },
196462306a36Sopenharmony_ci	[IFLA_XDP]		= { .type = NLA_NESTED },
196562306a36Sopenharmony_ci	[IFLA_EVENT]		= { .type = NLA_U32 },
196662306a36Sopenharmony_ci	[IFLA_GROUP]		= { .type = NLA_U32 },
196762306a36Sopenharmony_ci	[IFLA_TARGET_NETNSID]	= { .type = NLA_S32 },
196862306a36Sopenharmony_ci	[IFLA_CARRIER_UP_COUNT]	= { .type = NLA_U32 },
196962306a36Sopenharmony_ci	[IFLA_CARRIER_DOWN_COUNT] = { .type = NLA_U32 },
197062306a36Sopenharmony_ci	[IFLA_MIN_MTU]		= { .type = NLA_U32 },
197162306a36Sopenharmony_ci	[IFLA_MAX_MTU]		= { .type = NLA_U32 },
197262306a36Sopenharmony_ci	[IFLA_PROP_LIST]	= { .type = NLA_NESTED },
197362306a36Sopenharmony_ci	[IFLA_ALT_IFNAME]	= { .type = NLA_STRING,
197462306a36Sopenharmony_ci				    .len = ALTIFNAMSIZ - 1 },
197562306a36Sopenharmony_ci	[IFLA_PERM_ADDRESS]	= { .type = NLA_REJECT },
197662306a36Sopenharmony_ci	[IFLA_PROTO_DOWN_REASON] = { .type = NLA_NESTED },
197762306a36Sopenharmony_ci	[IFLA_NEW_IFINDEX]	= NLA_POLICY_MIN(NLA_S32, 1),
197862306a36Sopenharmony_ci	[IFLA_PARENT_DEV_NAME]	= { .type = NLA_NUL_STRING },
197962306a36Sopenharmony_ci	[IFLA_GRO_MAX_SIZE]	= { .type = NLA_U32 },
198062306a36Sopenharmony_ci	[IFLA_TSO_MAX_SIZE]	= { .type = NLA_REJECT },
198162306a36Sopenharmony_ci	[IFLA_TSO_MAX_SEGS]	= { .type = NLA_REJECT },
198262306a36Sopenharmony_ci	[IFLA_ALLMULTI]		= { .type = NLA_REJECT },
198362306a36Sopenharmony_ci	[IFLA_GSO_IPV4_MAX_SIZE]	= { .type = NLA_U32 },
198462306a36Sopenharmony_ci	[IFLA_GRO_IPV4_MAX_SIZE]	= { .type = NLA_U32 },
198562306a36Sopenharmony_ci};
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_cistatic const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
198862306a36Sopenharmony_ci	[IFLA_INFO_KIND]	= { .type = NLA_STRING },
198962306a36Sopenharmony_ci	[IFLA_INFO_DATA]	= { .type = NLA_NESTED },
199062306a36Sopenharmony_ci	[IFLA_INFO_SLAVE_KIND]	= { .type = NLA_STRING },
199162306a36Sopenharmony_ci	[IFLA_INFO_SLAVE_DATA]	= { .type = NLA_NESTED },
199262306a36Sopenharmony_ci};
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_cistatic const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
199562306a36Sopenharmony_ci	[IFLA_VF_MAC]		= { .len = sizeof(struct ifla_vf_mac) },
199662306a36Sopenharmony_ci	[IFLA_VF_BROADCAST]	= { .type = NLA_REJECT },
199762306a36Sopenharmony_ci	[IFLA_VF_VLAN]		= { .len = sizeof(struct ifla_vf_vlan) },
199862306a36Sopenharmony_ci	[IFLA_VF_VLAN_LIST]     = { .type = NLA_NESTED },
199962306a36Sopenharmony_ci	[IFLA_VF_TX_RATE]	= { .len = sizeof(struct ifla_vf_tx_rate) },
200062306a36Sopenharmony_ci	[IFLA_VF_SPOOFCHK]	= { .len = sizeof(struct ifla_vf_spoofchk) },
200162306a36Sopenharmony_ci	[IFLA_VF_RATE]		= { .len = sizeof(struct ifla_vf_rate) },
200262306a36Sopenharmony_ci	[IFLA_VF_LINK_STATE]	= { .len = sizeof(struct ifla_vf_link_state) },
200362306a36Sopenharmony_ci	[IFLA_VF_RSS_QUERY_EN]	= { .len = sizeof(struct ifla_vf_rss_query_en) },
200462306a36Sopenharmony_ci	[IFLA_VF_STATS]		= { .type = NLA_NESTED },
200562306a36Sopenharmony_ci	[IFLA_VF_TRUST]		= { .len = sizeof(struct ifla_vf_trust) },
200662306a36Sopenharmony_ci	[IFLA_VF_IB_NODE_GUID]	= { .len = sizeof(struct ifla_vf_guid) },
200762306a36Sopenharmony_ci	[IFLA_VF_IB_PORT_GUID]	= { .len = sizeof(struct ifla_vf_guid) },
200862306a36Sopenharmony_ci};
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_cistatic const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
201162306a36Sopenharmony_ci	[IFLA_PORT_VF]		= { .type = NLA_U32 },
201262306a36Sopenharmony_ci	[IFLA_PORT_PROFILE]	= { .type = NLA_STRING,
201362306a36Sopenharmony_ci				    .len = PORT_PROFILE_MAX },
201462306a36Sopenharmony_ci	[IFLA_PORT_INSTANCE_UUID] = { .type = NLA_BINARY,
201562306a36Sopenharmony_ci				      .len = PORT_UUID_MAX },
201662306a36Sopenharmony_ci	[IFLA_PORT_HOST_UUID]	= { .type = NLA_STRING,
201762306a36Sopenharmony_ci				    .len = PORT_UUID_MAX },
201862306a36Sopenharmony_ci	[IFLA_PORT_REQUEST]	= { .type = NLA_U8, },
201962306a36Sopenharmony_ci	[IFLA_PORT_RESPONSE]	= { .type = NLA_U16, },
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	/* Unused, but we need to keep it here since user space could
202262306a36Sopenharmony_ci	 * fill it. It's also broken with regard to NLA_BINARY use in
202362306a36Sopenharmony_ci	 * combination with structs.
202462306a36Sopenharmony_ci	 */
202562306a36Sopenharmony_ci	[IFLA_PORT_VSI_TYPE]	= { .type = NLA_BINARY,
202662306a36Sopenharmony_ci				    .len = sizeof(struct ifla_port_vsi) },
202762306a36Sopenharmony_ci};
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_cistatic const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = {
203062306a36Sopenharmony_ci	[IFLA_XDP_UNSPEC]	= { .strict_start_type = IFLA_XDP_EXPECTED_FD },
203162306a36Sopenharmony_ci	[IFLA_XDP_FD]		= { .type = NLA_S32 },
203262306a36Sopenharmony_ci	[IFLA_XDP_EXPECTED_FD]	= { .type = NLA_S32 },
203362306a36Sopenharmony_ci	[IFLA_XDP_ATTACHED]	= { .type = NLA_U8 },
203462306a36Sopenharmony_ci	[IFLA_XDP_FLAGS]	= { .type = NLA_U32 },
203562306a36Sopenharmony_ci	[IFLA_XDP_PROG_ID]	= { .type = NLA_U32 },
203662306a36Sopenharmony_ci};
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cistatic const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla)
203962306a36Sopenharmony_ci{
204062306a36Sopenharmony_ci	const struct rtnl_link_ops *ops = NULL;
204162306a36Sopenharmony_ci	struct nlattr *linfo[IFLA_INFO_MAX + 1];
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	if (nla_parse_nested_deprecated(linfo, IFLA_INFO_MAX, nla, ifla_info_policy, NULL) < 0)
204462306a36Sopenharmony_ci		return NULL;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	if (linfo[IFLA_INFO_KIND]) {
204762306a36Sopenharmony_ci		char kind[MODULE_NAME_LEN];
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci		nla_strscpy(kind, linfo[IFLA_INFO_KIND], sizeof(kind));
205062306a36Sopenharmony_ci		ops = rtnl_link_ops_get(kind);
205162306a36Sopenharmony_ci	}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	return ops;
205462306a36Sopenharmony_ci}
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_cistatic bool link_master_filtered(struct net_device *dev, int master_idx)
205762306a36Sopenharmony_ci{
205862306a36Sopenharmony_ci	struct net_device *master;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	if (!master_idx)
206162306a36Sopenharmony_ci		return false;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	master = netdev_master_upper_dev_get(dev);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	/* 0 is already used to denote IFLA_MASTER wasn't passed, therefore need
206662306a36Sopenharmony_ci	 * another invalid value for ifindex to denote "no master".
206762306a36Sopenharmony_ci	 */
206862306a36Sopenharmony_ci	if (master_idx == -1)
206962306a36Sopenharmony_ci		return !!master;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	if (!master || master->ifindex != master_idx)
207262306a36Sopenharmony_ci		return true;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	return false;
207562306a36Sopenharmony_ci}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_cistatic bool link_kind_filtered(const struct net_device *dev,
207862306a36Sopenharmony_ci			       const struct rtnl_link_ops *kind_ops)
207962306a36Sopenharmony_ci{
208062306a36Sopenharmony_ci	if (kind_ops && dev->rtnl_link_ops != kind_ops)
208162306a36Sopenharmony_ci		return true;
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	return false;
208462306a36Sopenharmony_ci}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_cistatic bool link_dump_filtered(struct net_device *dev,
208762306a36Sopenharmony_ci			       int master_idx,
208862306a36Sopenharmony_ci			       const struct rtnl_link_ops *kind_ops)
208962306a36Sopenharmony_ci{
209062306a36Sopenharmony_ci	if (link_master_filtered(dev, master_idx) ||
209162306a36Sopenharmony_ci	    link_kind_filtered(dev, kind_ops))
209262306a36Sopenharmony_ci		return true;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	return false;
209562306a36Sopenharmony_ci}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci/**
209862306a36Sopenharmony_ci * rtnl_get_net_ns_capable - Get netns if sufficiently privileged.
209962306a36Sopenharmony_ci * @sk: netlink socket
210062306a36Sopenharmony_ci * @netnsid: network namespace identifier
210162306a36Sopenharmony_ci *
210262306a36Sopenharmony_ci * Returns the network namespace identified by netnsid on success or an error
210362306a36Sopenharmony_ci * pointer on failure.
210462306a36Sopenharmony_ci */
210562306a36Sopenharmony_cistruct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid)
210662306a36Sopenharmony_ci{
210762306a36Sopenharmony_ci	struct net *net;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	net = get_net_ns_by_id(sock_net(sk), netnsid);
211062306a36Sopenharmony_ci	if (!net)
211162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	/* For now, the caller is required to have CAP_NET_ADMIN in
211462306a36Sopenharmony_ci	 * the user namespace owning the target net ns.
211562306a36Sopenharmony_ci	 */
211662306a36Sopenharmony_ci	if (!sk_ns_capable(sk, net->user_ns, CAP_NET_ADMIN)) {
211762306a36Sopenharmony_ci		put_net(net);
211862306a36Sopenharmony_ci		return ERR_PTR(-EACCES);
211962306a36Sopenharmony_ci	}
212062306a36Sopenharmony_ci	return net;
212162306a36Sopenharmony_ci}
212262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable);
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_cistatic int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh,
212562306a36Sopenharmony_ci				      bool strict_check, struct nlattr **tb,
212662306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
212762306a36Sopenharmony_ci{
212862306a36Sopenharmony_ci	int hdrlen;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	if (strict_check) {
213162306a36Sopenharmony_ci		struct ifinfomsg *ifm;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
213462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid header for link dump");
213562306a36Sopenharmony_ci			return -EINVAL;
213662306a36Sopenharmony_ci		}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci		ifm = nlmsg_data(nlh);
213962306a36Sopenharmony_ci		if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
214062306a36Sopenharmony_ci		    ifm->ifi_change) {
214162306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid values in header for link dump request");
214262306a36Sopenharmony_ci			return -EINVAL;
214362306a36Sopenharmony_ci		}
214462306a36Sopenharmony_ci		if (ifm->ifi_index) {
214562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Filter by device index not supported for link dumps");
214662306a36Sopenharmony_ci			return -EINVAL;
214762306a36Sopenharmony_ci		}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci		return nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb,
215062306a36Sopenharmony_ci						     IFLA_MAX, ifla_policy,
215162306a36Sopenharmony_ci						     extack);
215262306a36Sopenharmony_ci	}
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	/* A hack to preserve kernel<->userspace interface.
215562306a36Sopenharmony_ci	 * The correct header is ifinfomsg. It is consistent with rtnl_getlink.
215662306a36Sopenharmony_ci	 * However, before Linux v3.9 the code here assumed rtgenmsg and that's
215762306a36Sopenharmony_ci	 * what iproute2 < v3.9.0 used.
215862306a36Sopenharmony_ci	 * We can detect the old iproute2. Even including the IFLA_EXT_MASK
215962306a36Sopenharmony_ci	 * attribute, its netlink message is shorter than struct ifinfomsg.
216062306a36Sopenharmony_ci	 */
216162306a36Sopenharmony_ci	hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ?
216262306a36Sopenharmony_ci		 sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	return nlmsg_parse_deprecated(nlh, hdrlen, tb, IFLA_MAX, ifla_policy,
216562306a36Sopenharmony_ci				      extack);
216662306a36Sopenharmony_ci}
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_cistatic int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
216962306a36Sopenharmony_ci{
217062306a36Sopenharmony_ci	struct netlink_ext_ack *extack = cb->extack;
217162306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
217262306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
217362306a36Sopenharmony_ci	struct net *tgt_net = net;
217462306a36Sopenharmony_ci	int h, s_h;
217562306a36Sopenharmony_ci	int idx = 0, s_idx;
217662306a36Sopenharmony_ci	struct net_device *dev;
217762306a36Sopenharmony_ci	struct hlist_head *head;
217862306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX+1];
217962306a36Sopenharmony_ci	u32 ext_filter_mask = 0;
218062306a36Sopenharmony_ci	const struct rtnl_link_ops *kind_ops = NULL;
218162306a36Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
218262306a36Sopenharmony_ci	int master_idx = 0;
218362306a36Sopenharmony_ci	int netnsid = -1;
218462306a36Sopenharmony_ci	int err, i;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	s_h = cb->args[0];
218762306a36Sopenharmony_ci	s_idx = cb->args[1];
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	err = rtnl_valid_dump_ifinfo_req(nlh, cb->strict_check, tb, extack);
219062306a36Sopenharmony_ci	if (err < 0) {
219162306a36Sopenharmony_ci		if (cb->strict_check)
219262306a36Sopenharmony_ci			return err;
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci		goto walk_entries;
219562306a36Sopenharmony_ci	}
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	for (i = 0; i <= IFLA_MAX; ++i) {
219862306a36Sopenharmony_ci		if (!tb[i])
219962306a36Sopenharmony_ci			continue;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci		/* new attributes should only be added with strict checking */
220262306a36Sopenharmony_ci		switch (i) {
220362306a36Sopenharmony_ci		case IFLA_TARGET_NETNSID:
220462306a36Sopenharmony_ci			netnsid = nla_get_s32(tb[i]);
220562306a36Sopenharmony_ci			tgt_net = rtnl_get_net_ns_capable(skb->sk, netnsid);
220662306a36Sopenharmony_ci			if (IS_ERR(tgt_net)) {
220762306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Invalid target network namespace id");
220862306a36Sopenharmony_ci				return PTR_ERR(tgt_net);
220962306a36Sopenharmony_ci			}
221062306a36Sopenharmony_ci			break;
221162306a36Sopenharmony_ci		case IFLA_EXT_MASK:
221262306a36Sopenharmony_ci			ext_filter_mask = nla_get_u32(tb[i]);
221362306a36Sopenharmony_ci			break;
221462306a36Sopenharmony_ci		case IFLA_MASTER:
221562306a36Sopenharmony_ci			master_idx = nla_get_u32(tb[i]);
221662306a36Sopenharmony_ci			break;
221762306a36Sopenharmony_ci		case IFLA_LINKINFO:
221862306a36Sopenharmony_ci			kind_ops = linkinfo_to_kind_ops(tb[i]);
221962306a36Sopenharmony_ci			break;
222062306a36Sopenharmony_ci		default:
222162306a36Sopenharmony_ci			if (cb->strict_check) {
222262306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Unsupported attribute in link dump request");
222362306a36Sopenharmony_ci				return -EINVAL;
222462306a36Sopenharmony_ci			}
222562306a36Sopenharmony_ci		}
222662306a36Sopenharmony_ci	}
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	if (master_idx || kind_ops)
222962306a36Sopenharmony_ci		flags |= NLM_F_DUMP_FILTERED;
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ciwalk_entries:
223262306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
223362306a36Sopenharmony_ci		idx = 0;
223462306a36Sopenharmony_ci		head = &tgt_net->dev_index_head[h];
223562306a36Sopenharmony_ci		hlist_for_each_entry(dev, head, index_hlist) {
223662306a36Sopenharmony_ci			if (link_dump_filtered(dev, master_idx, kind_ops))
223762306a36Sopenharmony_ci				goto cont;
223862306a36Sopenharmony_ci			if (idx < s_idx)
223962306a36Sopenharmony_ci				goto cont;
224062306a36Sopenharmony_ci			err = rtnl_fill_ifinfo(skb, dev, net,
224162306a36Sopenharmony_ci					       RTM_NEWLINK,
224262306a36Sopenharmony_ci					       NETLINK_CB(cb->skb).portid,
224362306a36Sopenharmony_ci					       nlh->nlmsg_seq, 0, flags,
224462306a36Sopenharmony_ci					       ext_filter_mask, 0, NULL, 0,
224562306a36Sopenharmony_ci					       netnsid, GFP_KERNEL);
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci			if (err < 0) {
224862306a36Sopenharmony_ci				if (likely(skb->len))
224962306a36Sopenharmony_ci					goto out;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci				goto out_err;
225262306a36Sopenharmony_ci			}
225362306a36Sopenharmony_cicont:
225462306a36Sopenharmony_ci			idx++;
225562306a36Sopenharmony_ci		}
225662306a36Sopenharmony_ci	}
225762306a36Sopenharmony_ciout:
225862306a36Sopenharmony_ci	err = skb->len;
225962306a36Sopenharmony_ciout_err:
226062306a36Sopenharmony_ci	cb->args[1] = idx;
226162306a36Sopenharmony_ci	cb->args[0] = h;
226262306a36Sopenharmony_ci	cb->seq = tgt_net->dev_base_seq;
226362306a36Sopenharmony_ci	nl_dump_check_consistent(cb, nlmsg_hdr(skb));
226462306a36Sopenharmony_ci	if (netnsid >= 0)
226562306a36Sopenharmony_ci		put_net(tgt_net);
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	return err;
226862306a36Sopenharmony_ci}
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ciint rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer,
227162306a36Sopenharmony_ci			     struct netlink_ext_ack *exterr)
227262306a36Sopenharmony_ci{
227362306a36Sopenharmony_ci	const struct ifinfomsg *ifmp;
227462306a36Sopenharmony_ci	const struct nlattr *attrs;
227562306a36Sopenharmony_ci	size_t len;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	ifmp = nla_data(nla_peer);
227862306a36Sopenharmony_ci	attrs = nla_data(nla_peer) + sizeof(struct ifinfomsg);
227962306a36Sopenharmony_ci	len = nla_len(nla_peer) - sizeof(struct ifinfomsg);
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	if (ifmp->ifi_index < 0) {
228262306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(exterr, nla_peer,
228362306a36Sopenharmony_ci				    "ifindex can't be negative");
228462306a36Sopenharmony_ci		return -EINVAL;
228562306a36Sopenharmony_ci	}
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	return nla_parse_deprecated(tb, IFLA_MAX, attrs, len, ifla_policy,
228862306a36Sopenharmony_ci				    exterr);
228962306a36Sopenharmony_ci}
229062306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_nla_parse_ifinfomsg);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_cistruct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[])
229362306a36Sopenharmony_ci{
229462306a36Sopenharmony_ci	struct net *net;
229562306a36Sopenharmony_ci	/* Examine the link attributes and figure out which
229662306a36Sopenharmony_ci	 * network namespace we are talking about.
229762306a36Sopenharmony_ci	 */
229862306a36Sopenharmony_ci	if (tb[IFLA_NET_NS_PID])
229962306a36Sopenharmony_ci		net = get_net_ns_by_pid(nla_get_u32(tb[IFLA_NET_NS_PID]));
230062306a36Sopenharmony_ci	else if (tb[IFLA_NET_NS_FD])
230162306a36Sopenharmony_ci		net = get_net_ns_by_fd(nla_get_u32(tb[IFLA_NET_NS_FD]));
230262306a36Sopenharmony_ci	else
230362306a36Sopenharmony_ci		net = get_net(src_net);
230462306a36Sopenharmony_ci	return net;
230562306a36Sopenharmony_ci}
230662306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_link_get_net);
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci/* Figure out which network namespace we are talking about by
230962306a36Sopenharmony_ci * examining the link attributes in the following order:
231062306a36Sopenharmony_ci *
231162306a36Sopenharmony_ci * 1. IFLA_NET_NS_PID
231262306a36Sopenharmony_ci * 2. IFLA_NET_NS_FD
231362306a36Sopenharmony_ci * 3. IFLA_TARGET_NETNSID
231462306a36Sopenharmony_ci */
231562306a36Sopenharmony_cistatic struct net *rtnl_link_get_net_by_nlattr(struct net *src_net,
231662306a36Sopenharmony_ci					       struct nlattr *tb[])
231762306a36Sopenharmony_ci{
231862306a36Sopenharmony_ci	struct net *net;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD])
232162306a36Sopenharmony_ci		return rtnl_link_get_net(src_net, tb);
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	if (!tb[IFLA_TARGET_NETNSID])
232462306a36Sopenharmony_ci		return get_net(src_net);
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	net = get_net_ns_by_id(src_net, nla_get_u32(tb[IFLA_TARGET_NETNSID]));
232762306a36Sopenharmony_ci	if (!net)
232862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	return net;
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic struct net *rtnl_link_get_net_capable(const struct sk_buff *skb,
233462306a36Sopenharmony_ci					     struct net *src_net,
233562306a36Sopenharmony_ci					     struct nlattr *tb[], int cap)
233662306a36Sopenharmony_ci{
233762306a36Sopenharmony_ci	struct net *net;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	net = rtnl_link_get_net_by_nlattr(src_net, tb);
234062306a36Sopenharmony_ci	if (IS_ERR(net))
234162306a36Sopenharmony_ci		return net;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	if (!netlink_ns_capable(skb, net->user_ns, cap)) {
234462306a36Sopenharmony_ci		put_net(net);
234562306a36Sopenharmony_ci		return ERR_PTR(-EPERM);
234662306a36Sopenharmony_ci	}
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci	return net;
234962306a36Sopenharmony_ci}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci/* Verify that rtnetlink requests do not pass additional properties
235262306a36Sopenharmony_ci * potentially referring to different network namespaces.
235362306a36Sopenharmony_ci */
235462306a36Sopenharmony_cistatic int rtnl_ensure_unique_netns(struct nlattr *tb[],
235562306a36Sopenharmony_ci				    struct netlink_ext_ack *extack,
235662306a36Sopenharmony_ci				    bool netns_id_only)
235762306a36Sopenharmony_ci{
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	if (netns_id_only) {
236062306a36Sopenharmony_ci		if (!tb[IFLA_NET_NS_PID] && !tb[IFLA_NET_NS_FD])
236162306a36Sopenharmony_ci			return 0;
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "specified netns attribute not supported");
236462306a36Sopenharmony_ci		return -EOPNOTSUPP;
236562306a36Sopenharmony_ci	}
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci	if (tb[IFLA_TARGET_NETNSID] && (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD]))
236862306a36Sopenharmony_ci		goto invalid_attr;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	if (tb[IFLA_NET_NS_PID] && (tb[IFLA_TARGET_NETNSID] || tb[IFLA_NET_NS_FD]))
237162306a36Sopenharmony_ci		goto invalid_attr;
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci	if (tb[IFLA_NET_NS_FD] && (tb[IFLA_TARGET_NETNSID] || tb[IFLA_NET_NS_PID]))
237462306a36Sopenharmony_ci		goto invalid_attr;
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	return 0;
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ciinvalid_attr:
237962306a36Sopenharmony_ci	NL_SET_ERR_MSG(extack, "multiple netns identifying attributes specified");
238062306a36Sopenharmony_ci	return -EINVAL;
238162306a36Sopenharmony_ci}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_cistatic	int rtnl_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate,
238462306a36Sopenharmony_ci			     int max_tx_rate)
238562306a36Sopenharmony_ci{
238662306a36Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	if (!ops->ndo_set_vf_rate)
238962306a36Sopenharmony_ci		return -EOPNOTSUPP;
239062306a36Sopenharmony_ci	if (max_tx_rate && max_tx_rate < min_tx_rate)
239162306a36Sopenharmony_ci		return -EINVAL;
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	return ops->ndo_set_vf_rate(dev, vf, min_tx_rate, max_tx_rate);
239462306a36Sopenharmony_ci}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_cistatic int validate_linkmsg(struct net_device *dev, struct nlattr *tb[],
239762306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
239862306a36Sopenharmony_ci{
239962306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS] &&
240062306a36Sopenharmony_ci	    nla_len(tb[IFLA_ADDRESS]) < dev->addr_len)
240162306a36Sopenharmony_ci		return -EINVAL;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	if (tb[IFLA_BROADCAST] &&
240462306a36Sopenharmony_ci	    nla_len(tb[IFLA_BROADCAST]) < dev->addr_len)
240562306a36Sopenharmony_ci		return -EINVAL;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	if (tb[IFLA_GSO_MAX_SIZE] &&
240862306a36Sopenharmony_ci	    nla_get_u32(tb[IFLA_GSO_MAX_SIZE]) > dev->tso_max_size) {
240962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "too big gso_max_size");
241062306a36Sopenharmony_ci		return -EINVAL;
241162306a36Sopenharmony_ci	}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	if (tb[IFLA_GSO_MAX_SEGS] &&
241462306a36Sopenharmony_ci	    (nla_get_u32(tb[IFLA_GSO_MAX_SEGS]) > GSO_MAX_SEGS ||
241562306a36Sopenharmony_ci	     nla_get_u32(tb[IFLA_GSO_MAX_SEGS]) > dev->tso_max_segs)) {
241662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "too big gso_max_segs");
241762306a36Sopenharmony_ci		return -EINVAL;
241862306a36Sopenharmony_ci	}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	if (tb[IFLA_GRO_MAX_SIZE] &&
242162306a36Sopenharmony_ci	    nla_get_u32(tb[IFLA_GRO_MAX_SIZE]) > GRO_MAX_SIZE) {
242262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "too big gro_max_size");
242362306a36Sopenharmony_ci		return -EINVAL;
242462306a36Sopenharmony_ci	}
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	if (tb[IFLA_GSO_IPV4_MAX_SIZE] &&
242762306a36Sopenharmony_ci	    nla_get_u32(tb[IFLA_GSO_IPV4_MAX_SIZE]) > dev->tso_max_size) {
242862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "too big gso_ipv4_max_size");
242962306a36Sopenharmony_ci		return -EINVAL;
243062306a36Sopenharmony_ci	}
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	if (tb[IFLA_GRO_IPV4_MAX_SIZE] &&
243362306a36Sopenharmony_ci	    nla_get_u32(tb[IFLA_GRO_IPV4_MAX_SIZE]) > GRO_MAX_SIZE) {
243462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "too big gro_ipv4_max_size");
243562306a36Sopenharmony_ci		return -EINVAL;
243662306a36Sopenharmony_ci	}
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	if (tb[IFLA_AF_SPEC]) {
243962306a36Sopenharmony_ci		struct nlattr *af;
244062306a36Sopenharmony_ci		int rem, err;
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_ci		nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) {
244362306a36Sopenharmony_ci			const struct rtnl_af_ops *af_ops;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci			af_ops = rtnl_af_lookup(nla_type(af));
244662306a36Sopenharmony_ci			if (!af_ops)
244762306a36Sopenharmony_ci				return -EAFNOSUPPORT;
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci			if (!af_ops->set_link_af)
245062306a36Sopenharmony_ci				return -EOPNOTSUPP;
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci			if (af_ops->validate_link_af) {
245362306a36Sopenharmony_ci				err = af_ops->validate_link_af(dev, af, extack);
245462306a36Sopenharmony_ci				if (err < 0)
245562306a36Sopenharmony_ci					return err;
245662306a36Sopenharmony_ci			}
245762306a36Sopenharmony_ci		}
245862306a36Sopenharmony_ci	}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	return 0;
246162306a36Sopenharmony_ci}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_cistatic int handle_infiniband_guid(struct net_device *dev, struct ifla_vf_guid *ivt,
246462306a36Sopenharmony_ci				  int guid_type)
246562306a36Sopenharmony_ci{
246662306a36Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	return ops->ndo_set_vf_guid(dev, ivt->vf, ivt->guid, guid_type);
246962306a36Sopenharmony_ci}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_cistatic int handle_vf_guid(struct net_device *dev, struct ifla_vf_guid *ivt, int guid_type)
247262306a36Sopenharmony_ci{
247362306a36Sopenharmony_ci	if (dev->type != ARPHRD_INFINIBAND)
247462306a36Sopenharmony_ci		return -EOPNOTSUPP;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	return handle_infiniband_guid(dev, ivt, guid_type);
247762306a36Sopenharmony_ci}
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_cistatic int do_setvfinfo(struct net_device *dev, struct nlattr **tb)
248062306a36Sopenharmony_ci{
248162306a36Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
248262306a36Sopenharmony_ci	int err = -EINVAL;
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	if (tb[IFLA_VF_MAC]) {
248562306a36Sopenharmony_ci		struct ifla_vf_mac *ivm = nla_data(tb[IFLA_VF_MAC]);
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci		if (ivm->vf >= INT_MAX)
248862306a36Sopenharmony_ci			return -EINVAL;
248962306a36Sopenharmony_ci		err = -EOPNOTSUPP;
249062306a36Sopenharmony_ci		if (ops->ndo_set_vf_mac)
249162306a36Sopenharmony_ci			err = ops->ndo_set_vf_mac(dev, ivm->vf,
249262306a36Sopenharmony_ci						  ivm->mac);
249362306a36Sopenharmony_ci		if (err < 0)
249462306a36Sopenharmony_ci			return err;
249562306a36Sopenharmony_ci	}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	if (tb[IFLA_VF_VLAN]) {
249862306a36Sopenharmony_ci		struct ifla_vf_vlan *ivv = nla_data(tb[IFLA_VF_VLAN]);
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci		if (ivv->vf >= INT_MAX)
250162306a36Sopenharmony_ci			return -EINVAL;
250262306a36Sopenharmony_ci		err = -EOPNOTSUPP;
250362306a36Sopenharmony_ci		if (ops->ndo_set_vf_vlan)
250462306a36Sopenharmony_ci			err = ops->ndo_set_vf_vlan(dev, ivv->vf, ivv->vlan,
250562306a36Sopenharmony_ci						   ivv->qos,
250662306a36Sopenharmony_ci						   htons(ETH_P_8021Q));
250762306a36Sopenharmony_ci		if (err < 0)
250862306a36Sopenharmony_ci			return err;
250962306a36Sopenharmony_ci	}
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	if (tb[IFLA_VF_VLAN_LIST]) {
251262306a36Sopenharmony_ci		struct ifla_vf_vlan_info *ivvl[MAX_VLAN_LIST_LEN];
251362306a36Sopenharmony_ci		struct nlattr *attr;
251462306a36Sopenharmony_ci		int rem, len = 0;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci		err = -EOPNOTSUPP;
251762306a36Sopenharmony_ci		if (!ops->ndo_set_vf_vlan)
251862306a36Sopenharmony_ci			return err;
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci		nla_for_each_nested(attr, tb[IFLA_VF_VLAN_LIST], rem) {
252162306a36Sopenharmony_ci			if (nla_type(attr) != IFLA_VF_VLAN_INFO ||
252262306a36Sopenharmony_ci			    nla_len(attr) < NLA_HDRLEN) {
252362306a36Sopenharmony_ci				return -EINVAL;
252462306a36Sopenharmony_ci			}
252562306a36Sopenharmony_ci			if (len >= MAX_VLAN_LIST_LEN)
252662306a36Sopenharmony_ci				return -EOPNOTSUPP;
252762306a36Sopenharmony_ci			ivvl[len] = nla_data(attr);
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci			len++;
253062306a36Sopenharmony_ci		}
253162306a36Sopenharmony_ci		if (len == 0)
253262306a36Sopenharmony_ci			return -EINVAL;
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci		if (ivvl[0]->vf >= INT_MAX)
253562306a36Sopenharmony_ci			return -EINVAL;
253662306a36Sopenharmony_ci		err = ops->ndo_set_vf_vlan(dev, ivvl[0]->vf, ivvl[0]->vlan,
253762306a36Sopenharmony_ci					   ivvl[0]->qos, ivvl[0]->vlan_proto);
253862306a36Sopenharmony_ci		if (err < 0)
253962306a36Sopenharmony_ci			return err;
254062306a36Sopenharmony_ci	}
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	if (tb[IFLA_VF_TX_RATE]) {
254362306a36Sopenharmony_ci		struct ifla_vf_tx_rate *ivt = nla_data(tb[IFLA_VF_TX_RATE]);
254462306a36Sopenharmony_ci		struct ifla_vf_info ivf;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci		if (ivt->vf >= INT_MAX)
254762306a36Sopenharmony_ci			return -EINVAL;
254862306a36Sopenharmony_ci		err = -EOPNOTSUPP;
254962306a36Sopenharmony_ci		if (ops->ndo_get_vf_config)
255062306a36Sopenharmony_ci			err = ops->ndo_get_vf_config(dev, ivt->vf, &ivf);
255162306a36Sopenharmony_ci		if (err < 0)
255262306a36Sopenharmony_ci			return err;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci		err = rtnl_set_vf_rate(dev, ivt->vf,
255562306a36Sopenharmony_ci				       ivf.min_tx_rate, ivt->rate);
255662306a36Sopenharmony_ci		if (err < 0)
255762306a36Sopenharmony_ci			return err;
255862306a36Sopenharmony_ci	}
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	if (tb[IFLA_VF_RATE]) {
256162306a36Sopenharmony_ci		struct ifla_vf_rate *ivt = nla_data(tb[IFLA_VF_RATE]);
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci		if (ivt->vf >= INT_MAX)
256462306a36Sopenharmony_ci			return -EINVAL;
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci		err = rtnl_set_vf_rate(dev, ivt->vf,
256762306a36Sopenharmony_ci				       ivt->min_tx_rate, ivt->max_tx_rate);
256862306a36Sopenharmony_ci		if (err < 0)
256962306a36Sopenharmony_ci			return err;
257062306a36Sopenharmony_ci	}
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	if (tb[IFLA_VF_SPOOFCHK]) {
257362306a36Sopenharmony_ci		struct ifla_vf_spoofchk *ivs = nla_data(tb[IFLA_VF_SPOOFCHK]);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci		if (ivs->vf >= INT_MAX)
257662306a36Sopenharmony_ci			return -EINVAL;
257762306a36Sopenharmony_ci		err = -EOPNOTSUPP;
257862306a36Sopenharmony_ci		if (ops->ndo_set_vf_spoofchk)
257962306a36Sopenharmony_ci			err = ops->ndo_set_vf_spoofchk(dev, ivs->vf,
258062306a36Sopenharmony_ci						       ivs->setting);
258162306a36Sopenharmony_ci		if (err < 0)
258262306a36Sopenharmony_ci			return err;
258362306a36Sopenharmony_ci	}
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	if (tb[IFLA_VF_LINK_STATE]) {
258662306a36Sopenharmony_ci		struct ifla_vf_link_state *ivl = nla_data(tb[IFLA_VF_LINK_STATE]);
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci		if (ivl->vf >= INT_MAX)
258962306a36Sopenharmony_ci			return -EINVAL;
259062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
259162306a36Sopenharmony_ci		if (ops->ndo_set_vf_link_state)
259262306a36Sopenharmony_ci			err = ops->ndo_set_vf_link_state(dev, ivl->vf,
259362306a36Sopenharmony_ci							 ivl->link_state);
259462306a36Sopenharmony_ci		if (err < 0)
259562306a36Sopenharmony_ci			return err;
259662306a36Sopenharmony_ci	}
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	if (tb[IFLA_VF_RSS_QUERY_EN]) {
259962306a36Sopenharmony_ci		struct ifla_vf_rss_query_en *ivrssq_en;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci		err = -EOPNOTSUPP;
260262306a36Sopenharmony_ci		ivrssq_en = nla_data(tb[IFLA_VF_RSS_QUERY_EN]);
260362306a36Sopenharmony_ci		if (ivrssq_en->vf >= INT_MAX)
260462306a36Sopenharmony_ci			return -EINVAL;
260562306a36Sopenharmony_ci		if (ops->ndo_set_vf_rss_query_en)
260662306a36Sopenharmony_ci			err = ops->ndo_set_vf_rss_query_en(dev, ivrssq_en->vf,
260762306a36Sopenharmony_ci							   ivrssq_en->setting);
260862306a36Sopenharmony_ci		if (err < 0)
260962306a36Sopenharmony_ci			return err;
261062306a36Sopenharmony_ci	}
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	if (tb[IFLA_VF_TRUST]) {
261362306a36Sopenharmony_ci		struct ifla_vf_trust *ivt = nla_data(tb[IFLA_VF_TRUST]);
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci		if (ivt->vf >= INT_MAX)
261662306a36Sopenharmony_ci			return -EINVAL;
261762306a36Sopenharmony_ci		err = -EOPNOTSUPP;
261862306a36Sopenharmony_ci		if (ops->ndo_set_vf_trust)
261962306a36Sopenharmony_ci			err = ops->ndo_set_vf_trust(dev, ivt->vf, ivt->setting);
262062306a36Sopenharmony_ci		if (err < 0)
262162306a36Sopenharmony_ci			return err;
262262306a36Sopenharmony_ci	}
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	if (tb[IFLA_VF_IB_NODE_GUID]) {
262562306a36Sopenharmony_ci		struct ifla_vf_guid *ivt = nla_data(tb[IFLA_VF_IB_NODE_GUID]);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci		if (ivt->vf >= INT_MAX)
262862306a36Sopenharmony_ci			return -EINVAL;
262962306a36Sopenharmony_ci		if (!ops->ndo_set_vf_guid)
263062306a36Sopenharmony_ci			return -EOPNOTSUPP;
263162306a36Sopenharmony_ci		return handle_vf_guid(dev, ivt, IFLA_VF_IB_NODE_GUID);
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	if (tb[IFLA_VF_IB_PORT_GUID]) {
263562306a36Sopenharmony_ci		struct ifla_vf_guid *ivt = nla_data(tb[IFLA_VF_IB_PORT_GUID]);
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci		if (ivt->vf >= INT_MAX)
263862306a36Sopenharmony_ci			return -EINVAL;
263962306a36Sopenharmony_ci		if (!ops->ndo_set_vf_guid)
264062306a36Sopenharmony_ci			return -EOPNOTSUPP;
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci		return handle_vf_guid(dev, ivt, IFLA_VF_IB_PORT_GUID);
264362306a36Sopenharmony_ci	}
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	return err;
264662306a36Sopenharmony_ci}
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_cistatic int do_set_master(struct net_device *dev, int ifindex,
264962306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
265062306a36Sopenharmony_ci{
265162306a36Sopenharmony_ci	struct net_device *upper_dev = netdev_master_upper_dev_get(dev);
265262306a36Sopenharmony_ci	const struct net_device_ops *ops;
265362306a36Sopenharmony_ci	int err;
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci	if (upper_dev) {
265662306a36Sopenharmony_ci		if (upper_dev->ifindex == ifindex)
265762306a36Sopenharmony_ci			return 0;
265862306a36Sopenharmony_ci		ops = upper_dev->netdev_ops;
265962306a36Sopenharmony_ci		if (ops->ndo_del_slave) {
266062306a36Sopenharmony_ci			err = ops->ndo_del_slave(upper_dev, dev);
266162306a36Sopenharmony_ci			if (err)
266262306a36Sopenharmony_ci				return err;
266362306a36Sopenharmony_ci		} else {
266462306a36Sopenharmony_ci			return -EOPNOTSUPP;
266562306a36Sopenharmony_ci		}
266662306a36Sopenharmony_ci	}
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	if (ifindex) {
266962306a36Sopenharmony_ci		upper_dev = __dev_get_by_index(dev_net(dev), ifindex);
267062306a36Sopenharmony_ci		if (!upper_dev)
267162306a36Sopenharmony_ci			return -EINVAL;
267262306a36Sopenharmony_ci		ops = upper_dev->netdev_ops;
267362306a36Sopenharmony_ci		if (ops->ndo_add_slave) {
267462306a36Sopenharmony_ci			err = ops->ndo_add_slave(upper_dev, dev, extack);
267562306a36Sopenharmony_ci			if (err)
267662306a36Sopenharmony_ci				return err;
267762306a36Sopenharmony_ci		} else {
267862306a36Sopenharmony_ci			return -EOPNOTSUPP;
267962306a36Sopenharmony_ci		}
268062306a36Sopenharmony_ci	}
268162306a36Sopenharmony_ci	return 0;
268262306a36Sopenharmony_ci}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_cistatic const struct nla_policy ifla_proto_down_reason_policy[IFLA_PROTO_DOWN_REASON_VALUE + 1] = {
268562306a36Sopenharmony_ci	[IFLA_PROTO_DOWN_REASON_MASK]	= { .type = NLA_U32 },
268662306a36Sopenharmony_ci	[IFLA_PROTO_DOWN_REASON_VALUE]	= { .type = NLA_U32 },
268762306a36Sopenharmony_ci};
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_cistatic int do_set_proto_down(struct net_device *dev,
269062306a36Sopenharmony_ci			     struct nlattr *nl_proto_down,
269162306a36Sopenharmony_ci			     struct nlattr *nl_proto_down_reason,
269262306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
269362306a36Sopenharmony_ci{
269462306a36Sopenharmony_ci	struct nlattr *pdreason[IFLA_PROTO_DOWN_REASON_MAX + 1];
269562306a36Sopenharmony_ci	unsigned long mask = 0;
269662306a36Sopenharmony_ci	u32 value;
269762306a36Sopenharmony_ci	bool proto_down;
269862306a36Sopenharmony_ci	int err;
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ci	if (!(dev->priv_flags & IFF_CHANGE_PROTO_DOWN)) {
270162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,  "Protodown not supported by device");
270262306a36Sopenharmony_ci		return -EOPNOTSUPP;
270362306a36Sopenharmony_ci	}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci	if (nl_proto_down_reason) {
270662306a36Sopenharmony_ci		err = nla_parse_nested_deprecated(pdreason,
270762306a36Sopenharmony_ci						  IFLA_PROTO_DOWN_REASON_MAX,
270862306a36Sopenharmony_ci						  nl_proto_down_reason,
270962306a36Sopenharmony_ci						  ifla_proto_down_reason_policy,
271062306a36Sopenharmony_ci						  NULL);
271162306a36Sopenharmony_ci		if (err < 0)
271262306a36Sopenharmony_ci			return err;
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci		if (!pdreason[IFLA_PROTO_DOWN_REASON_VALUE]) {
271562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid protodown reason value");
271662306a36Sopenharmony_ci			return -EINVAL;
271762306a36Sopenharmony_ci		}
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci		value = nla_get_u32(pdreason[IFLA_PROTO_DOWN_REASON_VALUE]);
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci		if (pdreason[IFLA_PROTO_DOWN_REASON_MASK])
272262306a36Sopenharmony_ci			mask = nla_get_u32(pdreason[IFLA_PROTO_DOWN_REASON_MASK]);
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci		dev_change_proto_down_reason(dev, mask, value);
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	if (nl_proto_down) {
272862306a36Sopenharmony_ci		proto_down = nla_get_u8(nl_proto_down);
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci		/* Don't turn off protodown if there are active reasons */
273162306a36Sopenharmony_ci		if (!proto_down && dev->proto_down_reason) {
273262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Cannot clear protodown, active reasons");
273362306a36Sopenharmony_ci			return -EBUSY;
273462306a36Sopenharmony_ci		}
273562306a36Sopenharmony_ci		err = dev_change_proto_down(dev,
273662306a36Sopenharmony_ci					    proto_down);
273762306a36Sopenharmony_ci		if (err)
273862306a36Sopenharmony_ci			return err;
273962306a36Sopenharmony_ci	}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	return 0;
274262306a36Sopenharmony_ci}
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci#define DO_SETLINK_MODIFIED	0x01
274562306a36Sopenharmony_ci/* notify flag means notify + modified. */
274662306a36Sopenharmony_ci#define DO_SETLINK_NOTIFY	0x03
274762306a36Sopenharmony_cistatic int do_setlink(const struct sk_buff *skb,
274862306a36Sopenharmony_ci		      struct net_device *dev, struct ifinfomsg *ifm,
274962306a36Sopenharmony_ci		      struct netlink_ext_ack *extack,
275062306a36Sopenharmony_ci		      struct nlattr **tb, int status)
275162306a36Sopenharmony_ci{
275262306a36Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
275362306a36Sopenharmony_ci	char ifname[IFNAMSIZ];
275462306a36Sopenharmony_ci	int err;
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	if (tb[IFLA_IFNAME])
275762306a36Sopenharmony_ci		nla_strscpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
275862306a36Sopenharmony_ci	else
275962306a36Sopenharmony_ci		ifname[0] = '\0';
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	if (tb[IFLA_NET_NS_PID] || tb[IFLA_NET_NS_FD] || tb[IFLA_TARGET_NETNSID]) {
276262306a36Sopenharmony_ci		const char *pat = ifname[0] ? ifname : NULL;
276362306a36Sopenharmony_ci		struct net *net;
276462306a36Sopenharmony_ci		int new_ifindex;
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci		net = rtnl_link_get_net_capable(skb, dev_net(dev),
276762306a36Sopenharmony_ci						tb, CAP_NET_ADMIN);
276862306a36Sopenharmony_ci		if (IS_ERR(net)) {
276962306a36Sopenharmony_ci			err = PTR_ERR(net);
277062306a36Sopenharmony_ci			goto errout;
277162306a36Sopenharmony_ci		}
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci		if (tb[IFLA_NEW_IFINDEX])
277462306a36Sopenharmony_ci			new_ifindex = nla_get_s32(tb[IFLA_NEW_IFINDEX]);
277562306a36Sopenharmony_ci		else
277662306a36Sopenharmony_ci			new_ifindex = 0;
277762306a36Sopenharmony_ci
277862306a36Sopenharmony_ci		err = __dev_change_net_namespace(dev, net, pat, new_ifindex);
277962306a36Sopenharmony_ci		put_net(net);
278062306a36Sopenharmony_ci		if (err)
278162306a36Sopenharmony_ci			goto errout;
278262306a36Sopenharmony_ci		status |= DO_SETLINK_MODIFIED;
278362306a36Sopenharmony_ci	}
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	if (tb[IFLA_MAP]) {
278662306a36Sopenharmony_ci		struct rtnl_link_ifmap *u_map;
278762306a36Sopenharmony_ci		struct ifmap k_map;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci		if (!ops->ndo_set_config) {
279062306a36Sopenharmony_ci			err = -EOPNOTSUPP;
279162306a36Sopenharmony_ci			goto errout;
279262306a36Sopenharmony_ci		}
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci		if (!netif_device_present(dev)) {
279562306a36Sopenharmony_ci			err = -ENODEV;
279662306a36Sopenharmony_ci			goto errout;
279762306a36Sopenharmony_ci		}
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci		u_map = nla_data(tb[IFLA_MAP]);
280062306a36Sopenharmony_ci		k_map.mem_start = (unsigned long) u_map->mem_start;
280162306a36Sopenharmony_ci		k_map.mem_end = (unsigned long) u_map->mem_end;
280262306a36Sopenharmony_ci		k_map.base_addr = (unsigned short) u_map->base_addr;
280362306a36Sopenharmony_ci		k_map.irq = (unsigned char) u_map->irq;
280462306a36Sopenharmony_ci		k_map.dma = (unsigned char) u_map->dma;
280562306a36Sopenharmony_ci		k_map.port = (unsigned char) u_map->port;
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci		err = ops->ndo_set_config(dev, &k_map);
280862306a36Sopenharmony_ci		if (err < 0)
280962306a36Sopenharmony_ci			goto errout;
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_ci		status |= DO_SETLINK_NOTIFY;
281262306a36Sopenharmony_ci	}
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
281562306a36Sopenharmony_ci		struct sockaddr *sa;
281662306a36Sopenharmony_ci		int len;
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci		len = sizeof(sa_family_t) + max_t(size_t, dev->addr_len,
281962306a36Sopenharmony_ci						  sizeof(*sa));
282062306a36Sopenharmony_ci		sa = kmalloc(len, GFP_KERNEL);
282162306a36Sopenharmony_ci		if (!sa) {
282262306a36Sopenharmony_ci			err = -ENOMEM;
282362306a36Sopenharmony_ci			goto errout;
282462306a36Sopenharmony_ci		}
282562306a36Sopenharmony_ci		sa->sa_family = dev->type;
282662306a36Sopenharmony_ci		memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]),
282762306a36Sopenharmony_ci		       dev->addr_len);
282862306a36Sopenharmony_ci		err = dev_set_mac_address_user(dev, sa, extack);
282962306a36Sopenharmony_ci		kfree(sa);
283062306a36Sopenharmony_ci		if (err)
283162306a36Sopenharmony_ci			goto errout;
283262306a36Sopenharmony_ci		status |= DO_SETLINK_MODIFIED;
283362306a36Sopenharmony_ci	}
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	if (tb[IFLA_MTU]) {
283662306a36Sopenharmony_ci		err = dev_set_mtu_ext(dev, nla_get_u32(tb[IFLA_MTU]), extack);
283762306a36Sopenharmony_ci		if (err < 0)
283862306a36Sopenharmony_ci			goto errout;
283962306a36Sopenharmony_ci		status |= DO_SETLINK_MODIFIED;
284062306a36Sopenharmony_ci	}
284162306a36Sopenharmony_ci
284262306a36Sopenharmony_ci	if (tb[IFLA_GROUP]) {
284362306a36Sopenharmony_ci		dev_set_group(dev, nla_get_u32(tb[IFLA_GROUP]));
284462306a36Sopenharmony_ci		status |= DO_SETLINK_NOTIFY;
284562306a36Sopenharmony_ci	}
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	/*
284862306a36Sopenharmony_ci	 * Interface selected by interface index but interface
284962306a36Sopenharmony_ci	 * name provided implies that a name change has been
285062306a36Sopenharmony_ci	 * requested.
285162306a36Sopenharmony_ci	 */
285262306a36Sopenharmony_ci	if (ifm->ifi_index > 0 && ifname[0]) {
285362306a36Sopenharmony_ci		err = dev_change_name(dev, ifname);
285462306a36Sopenharmony_ci		if (err < 0)
285562306a36Sopenharmony_ci			goto errout;
285662306a36Sopenharmony_ci		status |= DO_SETLINK_MODIFIED;
285762306a36Sopenharmony_ci	}
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci	if (tb[IFLA_IFALIAS]) {
286062306a36Sopenharmony_ci		err = dev_set_alias(dev, nla_data(tb[IFLA_IFALIAS]),
286162306a36Sopenharmony_ci				    nla_len(tb[IFLA_IFALIAS]));
286262306a36Sopenharmony_ci		if (err < 0)
286362306a36Sopenharmony_ci			goto errout;
286462306a36Sopenharmony_ci		status |= DO_SETLINK_NOTIFY;
286562306a36Sopenharmony_ci	}
286662306a36Sopenharmony_ci
286762306a36Sopenharmony_ci	if (tb[IFLA_BROADCAST]) {
286862306a36Sopenharmony_ci		nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len);
286962306a36Sopenharmony_ci		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
287062306a36Sopenharmony_ci	}
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci	if (ifm->ifi_flags || ifm->ifi_change) {
287362306a36Sopenharmony_ci		err = dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
287462306a36Sopenharmony_ci				       extack);
287562306a36Sopenharmony_ci		if (err < 0)
287662306a36Sopenharmony_ci			goto errout;
287762306a36Sopenharmony_ci	}
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_ci	if (tb[IFLA_MASTER]) {
288062306a36Sopenharmony_ci		err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
288162306a36Sopenharmony_ci		if (err)
288262306a36Sopenharmony_ci			goto errout;
288362306a36Sopenharmony_ci		status |= DO_SETLINK_MODIFIED;
288462306a36Sopenharmony_ci	}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci	if (tb[IFLA_CARRIER]) {
288762306a36Sopenharmony_ci		err = dev_change_carrier(dev, nla_get_u8(tb[IFLA_CARRIER]));
288862306a36Sopenharmony_ci		if (err)
288962306a36Sopenharmony_ci			goto errout;
289062306a36Sopenharmony_ci		status |= DO_SETLINK_MODIFIED;
289162306a36Sopenharmony_ci	}
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	if (tb[IFLA_TXQLEN]) {
289462306a36Sopenharmony_ci		unsigned int value = nla_get_u32(tb[IFLA_TXQLEN]);
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci		err = dev_change_tx_queue_len(dev, value);
289762306a36Sopenharmony_ci		if (err)
289862306a36Sopenharmony_ci			goto errout;
289962306a36Sopenharmony_ci		status |= DO_SETLINK_MODIFIED;
290062306a36Sopenharmony_ci	}
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	if (tb[IFLA_GSO_MAX_SIZE]) {
290362306a36Sopenharmony_ci		u32 max_size = nla_get_u32(tb[IFLA_GSO_MAX_SIZE]);
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_ci		if (dev->gso_max_size ^ max_size) {
290662306a36Sopenharmony_ci			netif_set_gso_max_size(dev, max_size);
290762306a36Sopenharmony_ci			status |= DO_SETLINK_MODIFIED;
290862306a36Sopenharmony_ci		}
290962306a36Sopenharmony_ci	}
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	if (tb[IFLA_GSO_MAX_SEGS]) {
291262306a36Sopenharmony_ci		u32 max_segs = nla_get_u32(tb[IFLA_GSO_MAX_SEGS]);
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci		if (dev->gso_max_segs ^ max_segs) {
291562306a36Sopenharmony_ci			netif_set_gso_max_segs(dev, max_segs);
291662306a36Sopenharmony_ci			status |= DO_SETLINK_MODIFIED;
291762306a36Sopenharmony_ci		}
291862306a36Sopenharmony_ci	}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	if (tb[IFLA_GRO_MAX_SIZE]) {
292162306a36Sopenharmony_ci		u32 gro_max_size = nla_get_u32(tb[IFLA_GRO_MAX_SIZE]);
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci		if (dev->gro_max_size ^ gro_max_size) {
292462306a36Sopenharmony_ci			netif_set_gro_max_size(dev, gro_max_size);
292562306a36Sopenharmony_ci			status |= DO_SETLINK_MODIFIED;
292662306a36Sopenharmony_ci		}
292762306a36Sopenharmony_ci	}
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	if (tb[IFLA_GSO_IPV4_MAX_SIZE]) {
293062306a36Sopenharmony_ci		u32 max_size = nla_get_u32(tb[IFLA_GSO_IPV4_MAX_SIZE]);
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci		if (dev->gso_ipv4_max_size ^ max_size) {
293362306a36Sopenharmony_ci			netif_set_gso_ipv4_max_size(dev, max_size);
293462306a36Sopenharmony_ci			status |= DO_SETLINK_MODIFIED;
293562306a36Sopenharmony_ci		}
293662306a36Sopenharmony_ci	}
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	if (tb[IFLA_GRO_IPV4_MAX_SIZE]) {
293962306a36Sopenharmony_ci		u32 gro_max_size = nla_get_u32(tb[IFLA_GRO_IPV4_MAX_SIZE]);
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci		if (dev->gro_ipv4_max_size ^ gro_max_size) {
294262306a36Sopenharmony_ci			netif_set_gro_ipv4_max_size(dev, gro_max_size);
294362306a36Sopenharmony_ci			status |= DO_SETLINK_MODIFIED;
294462306a36Sopenharmony_ci		}
294562306a36Sopenharmony_ci	}
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	if (tb[IFLA_OPERSTATE])
294862306a36Sopenharmony_ci		set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	if (tb[IFLA_LINKMODE]) {
295162306a36Sopenharmony_ci		unsigned char value = nla_get_u8(tb[IFLA_LINKMODE]);
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci		write_lock(&dev_base_lock);
295462306a36Sopenharmony_ci		if (dev->link_mode ^ value)
295562306a36Sopenharmony_ci			status |= DO_SETLINK_NOTIFY;
295662306a36Sopenharmony_ci		dev->link_mode = value;
295762306a36Sopenharmony_ci		write_unlock(&dev_base_lock);
295862306a36Sopenharmony_ci	}
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	if (tb[IFLA_VFINFO_LIST]) {
296162306a36Sopenharmony_ci		struct nlattr *vfinfo[IFLA_VF_MAX + 1];
296262306a36Sopenharmony_ci		struct nlattr *attr;
296362306a36Sopenharmony_ci		int rem;
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci		nla_for_each_nested(attr, tb[IFLA_VFINFO_LIST], rem) {
296662306a36Sopenharmony_ci			if (nla_type(attr) != IFLA_VF_INFO ||
296762306a36Sopenharmony_ci			    nla_len(attr) < NLA_HDRLEN) {
296862306a36Sopenharmony_ci				err = -EINVAL;
296962306a36Sopenharmony_ci				goto errout;
297062306a36Sopenharmony_ci			}
297162306a36Sopenharmony_ci			err = nla_parse_nested_deprecated(vfinfo, IFLA_VF_MAX,
297262306a36Sopenharmony_ci							  attr,
297362306a36Sopenharmony_ci							  ifla_vf_policy,
297462306a36Sopenharmony_ci							  NULL);
297562306a36Sopenharmony_ci			if (err < 0)
297662306a36Sopenharmony_ci				goto errout;
297762306a36Sopenharmony_ci			err = do_setvfinfo(dev, vfinfo);
297862306a36Sopenharmony_ci			if (err < 0)
297962306a36Sopenharmony_ci				goto errout;
298062306a36Sopenharmony_ci			status |= DO_SETLINK_NOTIFY;
298162306a36Sopenharmony_ci		}
298262306a36Sopenharmony_ci	}
298362306a36Sopenharmony_ci	err = 0;
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	if (tb[IFLA_VF_PORTS]) {
298662306a36Sopenharmony_ci		struct nlattr *port[IFLA_PORT_MAX+1];
298762306a36Sopenharmony_ci		struct nlattr *attr;
298862306a36Sopenharmony_ci		int vf;
298962306a36Sopenharmony_ci		int rem;
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci		err = -EOPNOTSUPP;
299262306a36Sopenharmony_ci		if (!ops->ndo_set_vf_port)
299362306a36Sopenharmony_ci			goto errout;
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci		nla_for_each_nested(attr, tb[IFLA_VF_PORTS], rem) {
299662306a36Sopenharmony_ci			if (nla_type(attr) != IFLA_VF_PORT ||
299762306a36Sopenharmony_ci			    nla_len(attr) < NLA_HDRLEN) {
299862306a36Sopenharmony_ci				err = -EINVAL;
299962306a36Sopenharmony_ci				goto errout;
300062306a36Sopenharmony_ci			}
300162306a36Sopenharmony_ci			err = nla_parse_nested_deprecated(port, IFLA_PORT_MAX,
300262306a36Sopenharmony_ci							  attr,
300362306a36Sopenharmony_ci							  ifla_port_policy,
300462306a36Sopenharmony_ci							  NULL);
300562306a36Sopenharmony_ci			if (err < 0)
300662306a36Sopenharmony_ci				goto errout;
300762306a36Sopenharmony_ci			if (!port[IFLA_PORT_VF]) {
300862306a36Sopenharmony_ci				err = -EOPNOTSUPP;
300962306a36Sopenharmony_ci				goto errout;
301062306a36Sopenharmony_ci			}
301162306a36Sopenharmony_ci			vf = nla_get_u32(port[IFLA_PORT_VF]);
301262306a36Sopenharmony_ci			err = ops->ndo_set_vf_port(dev, vf, port);
301362306a36Sopenharmony_ci			if (err < 0)
301462306a36Sopenharmony_ci				goto errout;
301562306a36Sopenharmony_ci			status |= DO_SETLINK_NOTIFY;
301662306a36Sopenharmony_ci		}
301762306a36Sopenharmony_ci	}
301862306a36Sopenharmony_ci	err = 0;
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci	if (tb[IFLA_PORT_SELF]) {
302162306a36Sopenharmony_ci		struct nlattr *port[IFLA_PORT_MAX+1];
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci		err = nla_parse_nested_deprecated(port, IFLA_PORT_MAX,
302462306a36Sopenharmony_ci						  tb[IFLA_PORT_SELF],
302562306a36Sopenharmony_ci						  ifla_port_policy, NULL);
302662306a36Sopenharmony_ci		if (err < 0)
302762306a36Sopenharmony_ci			goto errout;
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci		err = -EOPNOTSUPP;
303062306a36Sopenharmony_ci		if (ops->ndo_set_vf_port)
303162306a36Sopenharmony_ci			err = ops->ndo_set_vf_port(dev, PORT_SELF_VF, port);
303262306a36Sopenharmony_ci		if (err < 0)
303362306a36Sopenharmony_ci			goto errout;
303462306a36Sopenharmony_ci		status |= DO_SETLINK_NOTIFY;
303562306a36Sopenharmony_ci	}
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	if (tb[IFLA_AF_SPEC]) {
303862306a36Sopenharmony_ci		struct nlattr *af;
303962306a36Sopenharmony_ci		int rem;
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci		nla_for_each_nested(af, tb[IFLA_AF_SPEC], rem) {
304262306a36Sopenharmony_ci			const struct rtnl_af_ops *af_ops;
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci			BUG_ON(!(af_ops = rtnl_af_lookup(nla_type(af))));
304562306a36Sopenharmony_ci
304662306a36Sopenharmony_ci			err = af_ops->set_link_af(dev, af, extack);
304762306a36Sopenharmony_ci			if (err < 0)
304862306a36Sopenharmony_ci				goto errout;
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci			status |= DO_SETLINK_NOTIFY;
305162306a36Sopenharmony_ci		}
305262306a36Sopenharmony_ci	}
305362306a36Sopenharmony_ci	err = 0;
305462306a36Sopenharmony_ci
305562306a36Sopenharmony_ci	if (tb[IFLA_PROTO_DOWN] || tb[IFLA_PROTO_DOWN_REASON]) {
305662306a36Sopenharmony_ci		err = do_set_proto_down(dev, tb[IFLA_PROTO_DOWN],
305762306a36Sopenharmony_ci					tb[IFLA_PROTO_DOWN_REASON], extack);
305862306a36Sopenharmony_ci		if (err)
305962306a36Sopenharmony_ci			goto errout;
306062306a36Sopenharmony_ci		status |= DO_SETLINK_NOTIFY;
306162306a36Sopenharmony_ci	}
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ci	if (tb[IFLA_XDP]) {
306462306a36Sopenharmony_ci		struct nlattr *xdp[IFLA_XDP_MAX + 1];
306562306a36Sopenharmony_ci		u32 xdp_flags = 0;
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci		err = nla_parse_nested_deprecated(xdp, IFLA_XDP_MAX,
306862306a36Sopenharmony_ci						  tb[IFLA_XDP],
306962306a36Sopenharmony_ci						  ifla_xdp_policy, NULL);
307062306a36Sopenharmony_ci		if (err < 0)
307162306a36Sopenharmony_ci			goto errout;
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci		if (xdp[IFLA_XDP_ATTACHED] || xdp[IFLA_XDP_PROG_ID]) {
307462306a36Sopenharmony_ci			err = -EINVAL;
307562306a36Sopenharmony_ci			goto errout;
307662306a36Sopenharmony_ci		}
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci		if (xdp[IFLA_XDP_FLAGS]) {
307962306a36Sopenharmony_ci			xdp_flags = nla_get_u32(xdp[IFLA_XDP_FLAGS]);
308062306a36Sopenharmony_ci			if (xdp_flags & ~XDP_FLAGS_MASK) {
308162306a36Sopenharmony_ci				err = -EINVAL;
308262306a36Sopenharmony_ci				goto errout;
308362306a36Sopenharmony_ci			}
308462306a36Sopenharmony_ci			if (hweight32(xdp_flags & XDP_FLAGS_MODES) > 1) {
308562306a36Sopenharmony_ci				err = -EINVAL;
308662306a36Sopenharmony_ci				goto errout;
308762306a36Sopenharmony_ci			}
308862306a36Sopenharmony_ci		}
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci		if (xdp[IFLA_XDP_FD]) {
309162306a36Sopenharmony_ci			int expected_fd = -1;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci			if (xdp_flags & XDP_FLAGS_REPLACE) {
309462306a36Sopenharmony_ci				if (!xdp[IFLA_XDP_EXPECTED_FD]) {
309562306a36Sopenharmony_ci					err = -EINVAL;
309662306a36Sopenharmony_ci					goto errout;
309762306a36Sopenharmony_ci				}
309862306a36Sopenharmony_ci				expected_fd =
309962306a36Sopenharmony_ci					nla_get_s32(xdp[IFLA_XDP_EXPECTED_FD]);
310062306a36Sopenharmony_ci			}
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci			err = dev_change_xdp_fd(dev, extack,
310362306a36Sopenharmony_ci						nla_get_s32(xdp[IFLA_XDP_FD]),
310462306a36Sopenharmony_ci						expected_fd,
310562306a36Sopenharmony_ci						xdp_flags);
310662306a36Sopenharmony_ci			if (err)
310762306a36Sopenharmony_ci				goto errout;
310862306a36Sopenharmony_ci			status |= DO_SETLINK_NOTIFY;
310962306a36Sopenharmony_ci		}
311062306a36Sopenharmony_ci	}
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_cierrout:
311362306a36Sopenharmony_ci	if (status & DO_SETLINK_MODIFIED) {
311462306a36Sopenharmony_ci		if ((status & DO_SETLINK_NOTIFY) == DO_SETLINK_NOTIFY)
311562306a36Sopenharmony_ci			netdev_state_change(dev);
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci		if (err < 0)
311862306a36Sopenharmony_ci			net_warn_ratelimited("A link change request failed with some changes committed already. Interface %s may have been left with an inconsistent configuration, please check.\n",
311962306a36Sopenharmony_ci					     dev->name);
312062306a36Sopenharmony_ci	}
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci	return err;
312362306a36Sopenharmony_ci}
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_cistatic struct net_device *rtnl_dev_get(struct net *net,
312662306a36Sopenharmony_ci				       struct nlattr *tb[])
312762306a36Sopenharmony_ci{
312862306a36Sopenharmony_ci	char ifname[ALTIFNAMSIZ];
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	if (tb[IFLA_IFNAME])
313162306a36Sopenharmony_ci		nla_strscpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
313262306a36Sopenharmony_ci	else if (tb[IFLA_ALT_IFNAME])
313362306a36Sopenharmony_ci		nla_strscpy(ifname, tb[IFLA_ALT_IFNAME], ALTIFNAMSIZ);
313462306a36Sopenharmony_ci	else
313562306a36Sopenharmony_ci		return NULL;
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci	return __dev_get_by_name(net, ifname);
313862306a36Sopenharmony_ci}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_cistatic int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
314162306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
314262306a36Sopenharmony_ci{
314362306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
314462306a36Sopenharmony_ci	struct ifinfomsg *ifm;
314562306a36Sopenharmony_ci	struct net_device *dev;
314662306a36Sopenharmony_ci	int err;
314762306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX+1];
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFLA_MAX,
315062306a36Sopenharmony_ci				     ifla_policy, extack);
315162306a36Sopenharmony_ci	if (err < 0)
315262306a36Sopenharmony_ci		goto errout;
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	err = rtnl_ensure_unique_netns(tb, extack, false);
315562306a36Sopenharmony_ci	if (err < 0)
315662306a36Sopenharmony_ci		goto errout;
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	err = -EINVAL;
315962306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
316062306a36Sopenharmony_ci	if (ifm->ifi_index > 0)
316162306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ifm->ifi_index);
316262306a36Sopenharmony_ci	else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
316362306a36Sopenharmony_ci		dev = rtnl_dev_get(net, tb);
316462306a36Sopenharmony_ci	else
316562306a36Sopenharmony_ci		goto errout;
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	if (dev == NULL) {
316862306a36Sopenharmony_ci		err = -ENODEV;
316962306a36Sopenharmony_ci		goto errout;
317062306a36Sopenharmony_ci	}
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	err = validate_linkmsg(dev, tb, extack);
317362306a36Sopenharmony_ci	if (err < 0)
317462306a36Sopenharmony_ci		goto errout;
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci	err = do_setlink(skb, dev, ifm, extack, tb, 0);
317762306a36Sopenharmony_cierrout:
317862306a36Sopenharmony_ci	return err;
317962306a36Sopenharmony_ci}
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_cistatic int rtnl_group_dellink(const struct net *net, int group)
318262306a36Sopenharmony_ci{
318362306a36Sopenharmony_ci	struct net_device *dev, *aux;
318462306a36Sopenharmony_ci	LIST_HEAD(list_kill);
318562306a36Sopenharmony_ci	bool found = false;
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	if (!group)
318862306a36Sopenharmony_ci		return -EPERM;
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci	for_each_netdev(net, dev) {
319162306a36Sopenharmony_ci		if (dev->group == group) {
319262306a36Sopenharmony_ci			const struct rtnl_link_ops *ops;
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci			found = true;
319562306a36Sopenharmony_ci			ops = dev->rtnl_link_ops;
319662306a36Sopenharmony_ci			if (!ops || !ops->dellink)
319762306a36Sopenharmony_ci				return -EOPNOTSUPP;
319862306a36Sopenharmony_ci		}
319962306a36Sopenharmony_ci	}
320062306a36Sopenharmony_ci
320162306a36Sopenharmony_ci	if (!found)
320262306a36Sopenharmony_ci		return -ENODEV;
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci	for_each_netdev_safe(net, dev, aux) {
320562306a36Sopenharmony_ci		if (dev->group == group) {
320662306a36Sopenharmony_ci			const struct rtnl_link_ops *ops;
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci			ops = dev->rtnl_link_ops;
320962306a36Sopenharmony_ci			ops->dellink(dev, &list_kill);
321062306a36Sopenharmony_ci		}
321162306a36Sopenharmony_ci	}
321262306a36Sopenharmony_ci	unregister_netdevice_many(&list_kill);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	return 0;
321562306a36Sopenharmony_ci}
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ciint rtnl_delete_link(struct net_device *dev, u32 portid, const struct nlmsghdr *nlh)
321862306a36Sopenharmony_ci{
321962306a36Sopenharmony_ci	const struct rtnl_link_ops *ops;
322062306a36Sopenharmony_ci	LIST_HEAD(list_kill);
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci	ops = dev->rtnl_link_ops;
322362306a36Sopenharmony_ci	if (!ops || !ops->dellink)
322462306a36Sopenharmony_ci		return -EOPNOTSUPP;
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci	ops->dellink(dev, &list_kill);
322762306a36Sopenharmony_ci	unregister_netdevice_many_notify(&list_kill, portid, nlh);
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci	return 0;
323062306a36Sopenharmony_ci}
323162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rtnl_delete_link);
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_cistatic int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
323462306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
323562306a36Sopenharmony_ci{
323662306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
323762306a36Sopenharmony_ci	u32 portid = NETLINK_CB(skb).portid;
323862306a36Sopenharmony_ci	struct net *tgt_net = net;
323962306a36Sopenharmony_ci	struct net_device *dev = NULL;
324062306a36Sopenharmony_ci	struct ifinfomsg *ifm;
324162306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX+1];
324262306a36Sopenharmony_ci	int err;
324362306a36Sopenharmony_ci	int netnsid = -1;
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFLA_MAX,
324662306a36Sopenharmony_ci				     ifla_policy, extack);
324762306a36Sopenharmony_ci	if (err < 0)
324862306a36Sopenharmony_ci		return err;
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci	err = rtnl_ensure_unique_netns(tb, extack, true);
325162306a36Sopenharmony_ci	if (err < 0)
325262306a36Sopenharmony_ci		return err;
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_ci	if (tb[IFLA_TARGET_NETNSID]) {
325562306a36Sopenharmony_ci		netnsid = nla_get_s32(tb[IFLA_TARGET_NETNSID]);
325662306a36Sopenharmony_ci		tgt_net = rtnl_get_net_ns_capable(NETLINK_CB(skb).sk, netnsid);
325762306a36Sopenharmony_ci		if (IS_ERR(tgt_net))
325862306a36Sopenharmony_ci			return PTR_ERR(tgt_net);
325962306a36Sopenharmony_ci	}
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci	err = -EINVAL;
326262306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
326362306a36Sopenharmony_ci	if (ifm->ifi_index > 0)
326462306a36Sopenharmony_ci		dev = __dev_get_by_index(tgt_net, ifm->ifi_index);
326562306a36Sopenharmony_ci	else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
326662306a36Sopenharmony_ci		dev = rtnl_dev_get(net, tb);
326762306a36Sopenharmony_ci	else if (tb[IFLA_GROUP])
326862306a36Sopenharmony_ci		err = rtnl_group_dellink(tgt_net, nla_get_u32(tb[IFLA_GROUP]));
326962306a36Sopenharmony_ci	else
327062306a36Sopenharmony_ci		goto out;
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	if (!dev) {
327362306a36Sopenharmony_ci		if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME] || ifm->ifi_index > 0)
327462306a36Sopenharmony_ci			err = -ENODEV;
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci		goto out;
327762306a36Sopenharmony_ci	}
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	err = rtnl_delete_link(dev, portid, nlh);
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ciout:
328262306a36Sopenharmony_ci	if (netnsid >= 0)
328362306a36Sopenharmony_ci		put_net(tgt_net);
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci	return err;
328662306a36Sopenharmony_ci}
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_ciint rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm,
328962306a36Sopenharmony_ci			u32 portid, const struct nlmsghdr *nlh)
329062306a36Sopenharmony_ci{
329162306a36Sopenharmony_ci	unsigned int old_flags;
329262306a36Sopenharmony_ci	int err;
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	old_flags = dev->flags;
329562306a36Sopenharmony_ci	if (ifm && (ifm->ifi_flags || ifm->ifi_change)) {
329662306a36Sopenharmony_ci		err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
329762306a36Sopenharmony_ci					 NULL);
329862306a36Sopenharmony_ci		if (err < 0)
329962306a36Sopenharmony_ci			return err;
330062306a36Sopenharmony_ci	}
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	if (dev->rtnl_link_state == RTNL_LINK_INITIALIZED) {
330362306a36Sopenharmony_ci		__dev_notify_flags(dev, old_flags, (old_flags ^ dev->flags), portid, nlh);
330462306a36Sopenharmony_ci	} else {
330562306a36Sopenharmony_ci		dev->rtnl_link_state = RTNL_LINK_INITIALIZED;
330662306a36Sopenharmony_ci		__dev_notify_flags(dev, old_flags, ~0U, portid, nlh);
330762306a36Sopenharmony_ci	}
330862306a36Sopenharmony_ci	return 0;
330962306a36Sopenharmony_ci}
331062306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_configure_link);
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_cistruct net_device *rtnl_create_link(struct net *net, const char *ifname,
331362306a36Sopenharmony_ci				    unsigned char name_assign_type,
331462306a36Sopenharmony_ci				    const struct rtnl_link_ops *ops,
331562306a36Sopenharmony_ci				    struct nlattr *tb[],
331662306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
331762306a36Sopenharmony_ci{
331862306a36Sopenharmony_ci	struct net_device *dev;
331962306a36Sopenharmony_ci	unsigned int num_tx_queues = 1;
332062306a36Sopenharmony_ci	unsigned int num_rx_queues = 1;
332162306a36Sopenharmony_ci	int err;
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	if (tb[IFLA_NUM_TX_QUEUES])
332462306a36Sopenharmony_ci		num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]);
332562306a36Sopenharmony_ci	else if (ops->get_num_tx_queues)
332662306a36Sopenharmony_ci		num_tx_queues = ops->get_num_tx_queues();
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	if (tb[IFLA_NUM_RX_QUEUES])
332962306a36Sopenharmony_ci		num_rx_queues = nla_get_u32(tb[IFLA_NUM_RX_QUEUES]);
333062306a36Sopenharmony_ci	else if (ops->get_num_rx_queues)
333162306a36Sopenharmony_ci		num_rx_queues = ops->get_num_rx_queues();
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	if (num_tx_queues < 1 || num_tx_queues > 4096) {
333462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid number of transmit queues");
333562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
333662306a36Sopenharmony_ci	}
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	if (num_rx_queues < 1 || num_rx_queues > 4096) {
333962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid number of receive queues");
334062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
334162306a36Sopenharmony_ci	}
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_ci	if (ops->alloc) {
334462306a36Sopenharmony_ci		dev = ops->alloc(tb, ifname, name_assign_type,
334562306a36Sopenharmony_ci				 num_tx_queues, num_rx_queues);
334662306a36Sopenharmony_ci		if (IS_ERR(dev))
334762306a36Sopenharmony_ci			return dev;
334862306a36Sopenharmony_ci	} else {
334962306a36Sopenharmony_ci		dev = alloc_netdev_mqs(ops->priv_size, ifname,
335062306a36Sopenharmony_ci				       name_assign_type, ops->setup,
335162306a36Sopenharmony_ci				       num_tx_queues, num_rx_queues);
335262306a36Sopenharmony_ci	}
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci	if (!dev)
335562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci	err = validate_linkmsg(dev, tb, extack);
335862306a36Sopenharmony_ci	if (err < 0) {
335962306a36Sopenharmony_ci		free_netdev(dev);
336062306a36Sopenharmony_ci		return ERR_PTR(err);
336162306a36Sopenharmony_ci	}
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci	dev_net_set(dev, net);
336462306a36Sopenharmony_ci	dev->rtnl_link_ops = ops;
336562306a36Sopenharmony_ci	dev->rtnl_link_state = RTNL_LINK_INITIALIZING;
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	if (tb[IFLA_MTU]) {
336862306a36Sopenharmony_ci		u32 mtu = nla_get_u32(tb[IFLA_MTU]);
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci		err = dev_validate_mtu(dev, mtu, extack);
337162306a36Sopenharmony_ci		if (err) {
337262306a36Sopenharmony_ci			free_netdev(dev);
337362306a36Sopenharmony_ci			return ERR_PTR(err);
337462306a36Sopenharmony_ci		}
337562306a36Sopenharmony_ci		dev->mtu = mtu;
337662306a36Sopenharmony_ci	}
337762306a36Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
337862306a36Sopenharmony_ci		__dev_addr_set(dev, nla_data(tb[IFLA_ADDRESS]),
337962306a36Sopenharmony_ci			       nla_len(tb[IFLA_ADDRESS]));
338062306a36Sopenharmony_ci		dev->addr_assign_type = NET_ADDR_SET;
338162306a36Sopenharmony_ci	}
338262306a36Sopenharmony_ci	if (tb[IFLA_BROADCAST])
338362306a36Sopenharmony_ci		memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]),
338462306a36Sopenharmony_ci				nla_len(tb[IFLA_BROADCAST]));
338562306a36Sopenharmony_ci	if (tb[IFLA_TXQLEN])
338662306a36Sopenharmony_ci		dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
338762306a36Sopenharmony_ci	if (tb[IFLA_OPERSTATE])
338862306a36Sopenharmony_ci		set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
338962306a36Sopenharmony_ci	if (tb[IFLA_LINKMODE])
339062306a36Sopenharmony_ci		dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
339162306a36Sopenharmony_ci	if (tb[IFLA_GROUP])
339262306a36Sopenharmony_ci		dev_set_group(dev, nla_get_u32(tb[IFLA_GROUP]));
339362306a36Sopenharmony_ci	if (tb[IFLA_GSO_MAX_SIZE])
339462306a36Sopenharmony_ci		netif_set_gso_max_size(dev, nla_get_u32(tb[IFLA_GSO_MAX_SIZE]));
339562306a36Sopenharmony_ci	if (tb[IFLA_GSO_MAX_SEGS])
339662306a36Sopenharmony_ci		netif_set_gso_max_segs(dev, nla_get_u32(tb[IFLA_GSO_MAX_SEGS]));
339762306a36Sopenharmony_ci	if (tb[IFLA_GRO_MAX_SIZE])
339862306a36Sopenharmony_ci		netif_set_gro_max_size(dev, nla_get_u32(tb[IFLA_GRO_MAX_SIZE]));
339962306a36Sopenharmony_ci	if (tb[IFLA_GSO_IPV4_MAX_SIZE])
340062306a36Sopenharmony_ci		netif_set_gso_ipv4_max_size(dev, nla_get_u32(tb[IFLA_GSO_IPV4_MAX_SIZE]));
340162306a36Sopenharmony_ci	if (tb[IFLA_GRO_IPV4_MAX_SIZE])
340262306a36Sopenharmony_ci		netif_set_gro_ipv4_max_size(dev, nla_get_u32(tb[IFLA_GRO_IPV4_MAX_SIZE]));
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci	return dev;
340562306a36Sopenharmony_ci}
340662306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_create_link);
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_cistatic int rtnl_group_changelink(const struct sk_buff *skb,
340962306a36Sopenharmony_ci		struct net *net, int group,
341062306a36Sopenharmony_ci		struct ifinfomsg *ifm,
341162306a36Sopenharmony_ci		struct netlink_ext_ack *extack,
341262306a36Sopenharmony_ci		struct nlattr **tb)
341362306a36Sopenharmony_ci{
341462306a36Sopenharmony_ci	struct net_device *dev, *aux;
341562306a36Sopenharmony_ci	int err;
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	for_each_netdev_safe(net, dev, aux) {
341862306a36Sopenharmony_ci		if (dev->group == group) {
341962306a36Sopenharmony_ci			err = validate_linkmsg(dev, tb, extack);
342062306a36Sopenharmony_ci			if (err < 0)
342162306a36Sopenharmony_ci				return err;
342262306a36Sopenharmony_ci			err = do_setlink(skb, dev, ifm, extack, tb, 0);
342362306a36Sopenharmony_ci			if (err < 0)
342462306a36Sopenharmony_ci				return err;
342562306a36Sopenharmony_ci		}
342662306a36Sopenharmony_ci	}
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_ci	return 0;
342962306a36Sopenharmony_ci}
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_cistatic int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
343262306a36Sopenharmony_ci			       const struct rtnl_link_ops *ops,
343362306a36Sopenharmony_ci			       const struct nlmsghdr *nlh,
343462306a36Sopenharmony_ci			       struct nlattr **tb, struct nlattr **data,
343562306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
343662306a36Sopenharmony_ci{
343762306a36Sopenharmony_ci	unsigned char name_assign_type = NET_NAME_USER;
343862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
343962306a36Sopenharmony_ci	u32 portid = NETLINK_CB(skb).portid;
344062306a36Sopenharmony_ci	struct net *dest_net, *link_net;
344162306a36Sopenharmony_ci	struct net_device *dev;
344262306a36Sopenharmony_ci	char ifname[IFNAMSIZ];
344362306a36Sopenharmony_ci	int err;
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci	if (!ops->alloc && !ops->setup)
344662306a36Sopenharmony_ci		return -EOPNOTSUPP;
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci	if (tb[IFLA_IFNAME]) {
344962306a36Sopenharmony_ci		nla_strscpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
345062306a36Sopenharmony_ci	} else {
345162306a36Sopenharmony_ci		snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
345262306a36Sopenharmony_ci		name_assign_type = NET_NAME_ENUM;
345362306a36Sopenharmony_ci	}
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci	dest_net = rtnl_link_get_net_capable(skb, net, tb, CAP_NET_ADMIN);
345662306a36Sopenharmony_ci	if (IS_ERR(dest_net))
345762306a36Sopenharmony_ci		return PTR_ERR(dest_net);
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	if (tb[IFLA_LINK_NETNSID]) {
346062306a36Sopenharmony_ci		int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci		link_net = get_net_ns_by_id(dest_net, id);
346362306a36Sopenharmony_ci		if (!link_net) {
346462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Unknown network namespace id");
346562306a36Sopenharmony_ci			err =  -EINVAL;
346662306a36Sopenharmony_ci			goto out;
346762306a36Sopenharmony_ci		}
346862306a36Sopenharmony_ci		err = -EPERM;
346962306a36Sopenharmony_ci		if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
347062306a36Sopenharmony_ci			goto out;
347162306a36Sopenharmony_ci	} else {
347262306a36Sopenharmony_ci		link_net = NULL;
347362306a36Sopenharmony_ci	}
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	dev = rtnl_create_link(link_net ? : dest_net, ifname,
347662306a36Sopenharmony_ci			       name_assign_type, ops, tb, extack);
347762306a36Sopenharmony_ci	if (IS_ERR(dev)) {
347862306a36Sopenharmony_ci		err = PTR_ERR(dev);
347962306a36Sopenharmony_ci		goto out;
348062306a36Sopenharmony_ci	}
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	dev->ifindex = ifm->ifi_index;
348362306a36Sopenharmony_ci
348462306a36Sopenharmony_ci	if (ops->newlink)
348562306a36Sopenharmony_ci		err = ops->newlink(link_net ? : net, dev, tb, data, extack);
348662306a36Sopenharmony_ci	else
348762306a36Sopenharmony_ci		err = register_netdevice(dev);
348862306a36Sopenharmony_ci	if (err < 0) {
348962306a36Sopenharmony_ci		free_netdev(dev);
349062306a36Sopenharmony_ci		goto out;
349162306a36Sopenharmony_ci	}
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	err = rtnl_configure_link(dev, ifm, portid, nlh);
349462306a36Sopenharmony_ci	if (err < 0)
349562306a36Sopenharmony_ci		goto out_unregister;
349662306a36Sopenharmony_ci	if (link_net) {
349762306a36Sopenharmony_ci		err = dev_change_net_namespace(dev, dest_net, ifname);
349862306a36Sopenharmony_ci		if (err < 0)
349962306a36Sopenharmony_ci			goto out_unregister;
350062306a36Sopenharmony_ci	}
350162306a36Sopenharmony_ci	if (tb[IFLA_MASTER]) {
350262306a36Sopenharmony_ci		err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
350362306a36Sopenharmony_ci		if (err)
350462306a36Sopenharmony_ci			goto out_unregister;
350562306a36Sopenharmony_ci	}
350662306a36Sopenharmony_ciout:
350762306a36Sopenharmony_ci	if (link_net)
350862306a36Sopenharmony_ci		put_net(link_net);
350962306a36Sopenharmony_ci	put_net(dest_net);
351062306a36Sopenharmony_ci	return err;
351162306a36Sopenharmony_ciout_unregister:
351262306a36Sopenharmony_ci	if (ops->newlink) {
351362306a36Sopenharmony_ci		LIST_HEAD(list_kill);
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci		ops->dellink(dev, &list_kill);
351662306a36Sopenharmony_ci		unregister_netdevice_many(&list_kill);
351762306a36Sopenharmony_ci	} else {
351862306a36Sopenharmony_ci		unregister_netdevice(dev);
351962306a36Sopenharmony_ci	}
352062306a36Sopenharmony_ci	goto out;
352162306a36Sopenharmony_ci}
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_cistruct rtnl_newlink_tbs {
352462306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX + 1];
352562306a36Sopenharmony_ci	struct nlattr *attr[RTNL_MAX_TYPE + 1];
352662306a36Sopenharmony_ci	struct nlattr *slave_attr[RTNL_SLAVE_MAX_TYPE + 1];
352762306a36Sopenharmony_ci};
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_cistatic int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
353062306a36Sopenharmony_ci			  struct rtnl_newlink_tbs *tbs,
353162306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
353262306a36Sopenharmony_ci{
353362306a36Sopenharmony_ci	struct nlattr *linkinfo[IFLA_INFO_MAX + 1];
353462306a36Sopenharmony_ci	struct nlattr ** const tb = tbs->tb;
353562306a36Sopenharmony_ci	const struct rtnl_link_ops *m_ops;
353662306a36Sopenharmony_ci	struct net_device *master_dev;
353762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
353862306a36Sopenharmony_ci	const struct rtnl_link_ops *ops;
353962306a36Sopenharmony_ci	struct nlattr **slave_data;
354062306a36Sopenharmony_ci	char kind[MODULE_NAME_LEN];
354162306a36Sopenharmony_ci	struct net_device *dev;
354262306a36Sopenharmony_ci	struct ifinfomsg *ifm;
354362306a36Sopenharmony_ci	struct nlattr **data;
354462306a36Sopenharmony_ci	bool link_specified;
354562306a36Sopenharmony_ci	int err;
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci#ifdef CONFIG_MODULES
354862306a36Sopenharmony_cireplay:
354962306a36Sopenharmony_ci#endif
355062306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFLA_MAX,
355162306a36Sopenharmony_ci				     ifla_policy, extack);
355262306a36Sopenharmony_ci	if (err < 0)
355362306a36Sopenharmony_ci		return err;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	err = rtnl_ensure_unique_netns(tb, extack, false);
355662306a36Sopenharmony_ci	if (err < 0)
355762306a36Sopenharmony_ci		return err;
355862306a36Sopenharmony_ci
355962306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
356062306a36Sopenharmony_ci	if (ifm->ifi_index > 0) {
356162306a36Sopenharmony_ci		link_specified = true;
356262306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ifm->ifi_index);
356362306a36Sopenharmony_ci	} else if (ifm->ifi_index < 0) {
356462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ifindex can't be negative");
356562306a36Sopenharmony_ci		return -EINVAL;
356662306a36Sopenharmony_ci	} else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME]) {
356762306a36Sopenharmony_ci		link_specified = true;
356862306a36Sopenharmony_ci		dev = rtnl_dev_get(net, tb);
356962306a36Sopenharmony_ci	} else {
357062306a36Sopenharmony_ci		link_specified = false;
357162306a36Sopenharmony_ci		dev = NULL;
357262306a36Sopenharmony_ci	}
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci	master_dev = NULL;
357562306a36Sopenharmony_ci	m_ops = NULL;
357662306a36Sopenharmony_ci	if (dev) {
357762306a36Sopenharmony_ci		master_dev = netdev_master_upper_dev_get(dev);
357862306a36Sopenharmony_ci		if (master_dev)
357962306a36Sopenharmony_ci			m_ops = master_dev->rtnl_link_ops;
358062306a36Sopenharmony_ci	}
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_ci	if (tb[IFLA_LINKINFO]) {
358362306a36Sopenharmony_ci		err = nla_parse_nested_deprecated(linkinfo, IFLA_INFO_MAX,
358462306a36Sopenharmony_ci						  tb[IFLA_LINKINFO],
358562306a36Sopenharmony_ci						  ifla_info_policy, NULL);
358662306a36Sopenharmony_ci		if (err < 0)
358762306a36Sopenharmony_ci			return err;
358862306a36Sopenharmony_ci	} else
358962306a36Sopenharmony_ci		memset(linkinfo, 0, sizeof(linkinfo));
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci	if (linkinfo[IFLA_INFO_KIND]) {
359262306a36Sopenharmony_ci		nla_strscpy(kind, linkinfo[IFLA_INFO_KIND], sizeof(kind));
359362306a36Sopenharmony_ci		ops = rtnl_link_ops_get(kind);
359462306a36Sopenharmony_ci	} else {
359562306a36Sopenharmony_ci		kind[0] = '\0';
359662306a36Sopenharmony_ci		ops = NULL;
359762306a36Sopenharmony_ci	}
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	data = NULL;
360062306a36Sopenharmony_ci	if (ops) {
360162306a36Sopenharmony_ci		if (ops->maxtype > RTNL_MAX_TYPE)
360262306a36Sopenharmony_ci			return -EINVAL;
360362306a36Sopenharmony_ci
360462306a36Sopenharmony_ci		if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) {
360562306a36Sopenharmony_ci			err = nla_parse_nested_deprecated(tbs->attr, ops->maxtype,
360662306a36Sopenharmony_ci							  linkinfo[IFLA_INFO_DATA],
360762306a36Sopenharmony_ci							  ops->policy, extack);
360862306a36Sopenharmony_ci			if (err < 0)
360962306a36Sopenharmony_ci				return err;
361062306a36Sopenharmony_ci			data = tbs->attr;
361162306a36Sopenharmony_ci		}
361262306a36Sopenharmony_ci		if (ops->validate) {
361362306a36Sopenharmony_ci			err = ops->validate(tb, data, extack);
361462306a36Sopenharmony_ci			if (err < 0)
361562306a36Sopenharmony_ci				return err;
361662306a36Sopenharmony_ci		}
361762306a36Sopenharmony_ci	}
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci	slave_data = NULL;
362062306a36Sopenharmony_ci	if (m_ops) {
362162306a36Sopenharmony_ci		if (m_ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE)
362262306a36Sopenharmony_ci			return -EINVAL;
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci		if (m_ops->slave_maxtype &&
362562306a36Sopenharmony_ci		    linkinfo[IFLA_INFO_SLAVE_DATA]) {
362662306a36Sopenharmony_ci			err = nla_parse_nested_deprecated(tbs->slave_attr,
362762306a36Sopenharmony_ci							  m_ops->slave_maxtype,
362862306a36Sopenharmony_ci							  linkinfo[IFLA_INFO_SLAVE_DATA],
362962306a36Sopenharmony_ci							  m_ops->slave_policy,
363062306a36Sopenharmony_ci							  extack);
363162306a36Sopenharmony_ci			if (err < 0)
363262306a36Sopenharmony_ci				return err;
363362306a36Sopenharmony_ci			slave_data = tbs->slave_attr;
363462306a36Sopenharmony_ci		}
363562306a36Sopenharmony_ci	}
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	if (dev) {
363862306a36Sopenharmony_ci		int status = 0;
363962306a36Sopenharmony_ci
364062306a36Sopenharmony_ci		if (nlh->nlmsg_flags & NLM_F_EXCL)
364162306a36Sopenharmony_ci			return -EEXIST;
364262306a36Sopenharmony_ci		if (nlh->nlmsg_flags & NLM_F_REPLACE)
364362306a36Sopenharmony_ci			return -EOPNOTSUPP;
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_ci		err = validate_linkmsg(dev, tb, extack);
364662306a36Sopenharmony_ci		if (err < 0)
364762306a36Sopenharmony_ci			return err;
364862306a36Sopenharmony_ci
364962306a36Sopenharmony_ci		if (linkinfo[IFLA_INFO_DATA]) {
365062306a36Sopenharmony_ci			if (!ops || ops != dev->rtnl_link_ops ||
365162306a36Sopenharmony_ci			    !ops->changelink)
365262306a36Sopenharmony_ci				return -EOPNOTSUPP;
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ci			err = ops->changelink(dev, tb, data, extack);
365562306a36Sopenharmony_ci			if (err < 0)
365662306a36Sopenharmony_ci				return err;
365762306a36Sopenharmony_ci			status |= DO_SETLINK_NOTIFY;
365862306a36Sopenharmony_ci		}
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci		if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
366162306a36Sopenharmony_ci			if (!m_ops || !m_ops->slave_changelink)
366262306a36Sopenharmony_ci				return -EOPNOTSUPP;
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci			err = m_ops->slave_changelink(master_dev, dev, tb,
366562306a36Sopenharmony_ci						      slave_data, extack);
366662306a36Sopenharmony_ci			if (err < 0)
366762306a36Sopenharmony_ci				return err;
366862306a36Sopenharmony_ci			status |= DO_SETLINK_NOTIFY;
366962306a36Sopenharmony_ci		}
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci		return do_setlink(skb, dev, ifm, extack, tb, status);
367262306a36Sopenharmony_ci	}
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_ci	if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
367562306a36Sopenharmony_ci		/* No dev found and NLM_F_CREATE not set. Requested dev does not exist,
367662306a36Sopenharmony_ci		 * or it's for a group
367762306a36Sopenharmony_ci		*/
367862306a36Sopenharmony_ci		if (link_specified)
367962306a36Sopenharmony_ci			return -ENODEV;
368062306a36Sopenharmony_ci		if (tb[IFLA_GROUP])
368162306a36Sopenharmony_ci			return rtnl_group_changelink(skb, net,
368262306a36Sopenharmony_ci						nla_get_u32(tb[IFLA_GROUP]),
368362306a36Sopenharmony_ci						ifm, extack, tb);
368462306a36Sopenharmony_ci		return -ENODEV;
368562306a36Sopenharmony_ci	}
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci	if (tb[IFLA_MAP] || tb[IFLA_PROTINFO])
368862306a36Sopenharmony_ci		return -EOPNOTSUPP;
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci	if (!ops) {
369162306a36Sopenharmony_ci#ifdef CONFIG_MODULES
369262306a36Sopenharmony_ci		if (kind[0]) {
369362306a36Sopenharmony_ci			__rtnl_unlock();
369462306a36Sopenharmony_ci			request_module("rtnl-link-%s", kind);
369562306a36Sopenharmony_ci			rtnl_lock();
369662306a36Sopenharmony_ci			ops = rtnl_link_ops_get(kind);
369762306a36Sopenharmony_ci			if (ops)
369862306a36Sopenharmony_ci				goto replay;
369962306a36Sopenharmony_ci		}
370062306a36Sopenharmony_ci#endif
370162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Unknown device type");
370262306a36Sopenharmony_ci		return -EOPNOTSUPP;
370362306a36Sopenharmony_ci	}
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci	return rtnl_newlink_create(skb, ifm, ops, nlh, tb, data, extack);
370662306a36Sopenharmony_ci}
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_cistatic int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
370962306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
371062306a36Sopenharmony_ci{
371162306a36Sopenharmony_ci	struct rtnl_newlink_tbs *tbs;
371262306a36Sopenharmony_ci	int ret;
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci	tbs = kmalloc(sizeof(*tbs), GFP_KERNEL);
371562306a36Sopenharmony_ci	if (!tbs)
371662306a36Sopenharmony_ci		return -ENOMEM;
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_ci	ret = __rtnl_newlink(skb, nlh, tbs, extack);
371962306a36Sopenharmony_ci	kfree(tbs);
372062306a36Sopenharmony_ci	return ret;
372162306a36Sopenharmony_ci}
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_cistatic int rtnl_valid_getlink_req(struct sk_buff *skb,
372462306a36Sopenharmony_ci				  const struct nlmsghdr *nlh,
372562306a36Sopenharmony_ci				  struct nlattr **tb,
372662306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
372762306a36Sopenharmony_ci{
372862306a36Sopenharmony_ci	struct ifinfomsg *ifm;
372962306a36Sopenharmony_ci	int i, err;
373062306a36Sopenharmony_ci
373162306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
373262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid header for get link");
373362306a36Sopenharmony_ci		return -EINVAL;
373462306a36Sopenharmony_ci	}
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	if (!netlink_strict_get_check(skb))
373762306a36Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFLA_MAX,
373862306a36Sopenharmony_ci					      ifla_policy, extack);
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
374162306a36Sopenharmony_ci	if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
374262306a36Sopenharmony_ci	    ifm->ifi_change) {
374362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid values in header for get link request");
374462306a36Sopenharmony_ci		return -EINVAL;
374562306a36Sopenharmony_ci	}
374662306a36Sopenharmony_ci
374762306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFLA_MAX,
374862306a36Sopenharmony_ci					    ifla_policy, extack);
374962306a36Sopenharmony_ci	if (err)
375062306a36Sopenharmony_ci		return err;
375162306a36Sopenharmony_ci
375262306a36Sopenharmony_ci	for (i = 0; i <= IFLA_MAX; i++) {
375362306a36Sopenharmony_ci		if (!tb[i])
375462306a36Sopenharmony_ci			continue;
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci		switch (i) {
375762306a36Sopenharmony_ci		case IFLA_IFNAME:
375862306a36Sopenharmony_ci		case IFLA_ALT_IFNAME:
375962306a36Sopenharmony_ci		case IFLA_EXT_MASK:
376062306a36Sopenharmony_ci		case IFLA_TARGET_NETNSID:
376162306a36Sopenharmony_ci			break;
376262306a36Sopenharmony_ci		default:
376362306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Unsupported attribute in get link request");
376462306a36Sopenharmony_ci			return -EINVAL;
376562306a36Sopenharmony_ci		}
376662306a36Sopenharmony_ci	}
376762306a36Sopenharmony_ci
376862306a36Sopenharmony_ci	return 0;
376962306a36Sopenharmony_ci}
377062306a36Sopenharmony_ci
377162306a36Sopenharmony_cistatic int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
377262306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
377362306a36Sopenharmony_ci{
377462306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
377562306a36Sopenharmony_ci	struct net *tgt_net = net;
377662306a36Sopenharmony_ci	struct ifinfomsg *ifm;
377762306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX+1];
377862306a36Sopenharmony_ci	struct net_device *dev = NULL;
377962306a36Sopenharmony_ci	struct sk_buff *nskb;
378062306a36Sopenharmony_ci	int netnsid = -1;
378162306a36Sopenharmony_ci	int err;
378262306a36Sopenharmony_ci	u32 ext_filter_mask = 0;
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci	err = rtnl_valid_getlink_req(skb, nlh, tb, extack);
378562306a36Sopenharmony_ci	if (err < 0)
378662306a36Sopenharmony_ci		return err;
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_ci	err = rtnl_ensure_unique_netns(tb, extack, true);
378962306a36Sopenharmony_ci	if (err < 0)
379062306a36Sopenharmony_ci		return err;
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ci	if (tb[IFLA_TARGET_NETNSID]) {
379362306a36Sopenharmony_ci		netnsid = nla_get_s32(tb[IFLA_TARGET_NETNSID]);
379462306a36Sopenharmony_ci		tgt_net = rtnl_get_net_ns_capable(NETLINK_CB(skb).sk, netnsid);
379562306a36Sopenharmony_ci		if (IS_ERR(tgt_net))
379662306a36Sopenharmony_ci			return PTR_ERR(tgt_net);
379762306a36Sopenharmony_ci	}
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_ci	if (tb[IFLA_EXT_MASK])
380062306a36Sopenharmony_ci		ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_ci	err = -EINVAL;
380362306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
380462306a36Sopenharmony_ci	if (ifm->ifi_index > 0)
380562306a36Sopenharmony_ci		dev = __dev_get_by_index(tgt_net, ifm->ifi_index);
380662306a36Sopenharmony_ci	else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
380762306a36Sopenharmony_ci		dev = rtnl_dev_get(tgt_net, tb);
380862306a36Sopenharmony_ci	else
380962306a36Sopenharmony_ci		goto out;
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci	err = -ENODEV;
381262306a36Sopenharmony_ci	if (dev == NULL)
381362306a36Sopenharmony_ci		goto out;
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ci	err = -ENOBUFS;
381662306a36Sopenharmony_ci	nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL);
381762306a36Sopenharmony_ci	if (nskb == NULL)
381862306a36Sopenharmony_ci		goto out;
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_ci	err = rtnl_fill_ifinfo(nskb, dev, net,
382162306a36Sopenharmony_ci			       RTM_NEWLINK, NETLINK_CB(skb).portid,
382262306a36Sopenharmony_ci			       nlh->nlmsg_seq, 0, 0, ext_filter_mask,
382362306a36Sopenharmony_ci			       0, NULL, 0, netnsid, GFP_KERNEL);
382462306a36Sopenharmony_ci	if (err < 0) {
382562306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in if_nlmsg_size */
382662306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
382762306a36Sopenharmony_ci		kfree_skb(nskb);
382862306a36Sopenharmony_ci	} else
382962306a36Sopenharmony_ci		err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid);
383062306a36Sopenharmony_ciout:
383162306a36Sopenharmony_ci	if (netnsid >= 0)
383262306a36Sopenharmony_ci		put_net(tgt_net);
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci	return err;
383562306a36Sopenharmony_ci}
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_cistatic int rtnl_alt_ifname(int cmd, struct net_device *dev, struct nlattr *attr,
383862306a36Sopenharmony_ci			   bool *changed, struct netlink_ext_ack *extack)
383962306a36Sopenharmony_ci{
384062306a36Sopenharmony_ci	char *alt_ifname;
384162306a36Sopenharmony_ci	size_t size;
384262306a36Sopenharmony_ci	int err;
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci	err = nla_validate(attr, attr->nla_len, IFLA_MAX, ifla_policy, extack);
384562306a36Sopenharmony_ci	if (err)
384662306a36Sopenharmony_ci		return err;
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci	if (cmd == RTM_NEWLINKPROP) {
384962306a36Sopenharmony_ci		size = rtnl_prop_list_size(dev);
385062306a36Sopenharmony_ci		size += nla_total_size(ALTIFNAMSIZ);
385162306a36Sopenharmony_ci		if (size >= U16_MAX) {
385262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
385362306a36Sopenharmony_ci				       "effective property list too long");
385462306a36Sopenharmony_ci			return -EINVAL;
385562306a36Sopenharmony_ci		}
385662306a36Sopenharmony_ci	}
385762306a36Sopenharmony_ci
385862306a36Sopenharmony_ci	alt_ifname = nla_strdup(attr, GFP_KERNEL_ACCOUNT);
385962306a36Sopenharmony_ci	if (!alt_ifname)
386062306a36Sopenharmony_ci		return -ENOMEM;
386162306a36Sopenharmony_ci
386262306a36Sopenharmony_ci	if (cmd == RTM_NEWLINKPROP) {
386362306a36Sopenharmony_ci		err = netdev_name_node_alt_create(dev, alt_ifname);
386462306a36Sopenharmony_ci		if (!err)
386562306a36Sopenharmony_ci			alt_ifname = NULL;
386662306a36Sopenharmony_ci	} else if (cmd == RTM_DELLINKPROP) {
386762306a36Sopenharmony_ci		err = netdev_name_node_alt_destroy(dev, alt_ifname);
386862306a36Sopenharmony_ci	} else {
386962306a36Sopenharmony_ci		WARN_ON_ONCE(1);
387062306a36Sopenharmony_ci		err = -EINVAL;
387162306a36Sopenharmony_ci	}
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_ci	kfree(alt_ifname);
387462306a36Sopenharmony_ci	if (!err)
387562306a36Sopenharmony_ci		*changed = true;
387662306a36Sopenharmony_ci	return err;
387762306a36Sopenharmony_ci}
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_cistatic int rtnl_linkprop(int cmd, struct sk_buff *skb, struct nlmsghdr *nlh,
388062306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
388162306a36Sopenharmony_ci{
388262306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
388362306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX + 1];
388462306a36Sopenharmony_ci	struct net_device *dev;
388562306a36Sopenharmony_ci	struct ifinfomsg *ifm;
388662306a36Sopenharmony_ci	bool changed = false;
388762306a36Sopenharmony_ci	struct nlattr *attr;
388862306a36Sopenharmony_ci	int err, rem;
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack);
389162306a36Sopenharmony_ci	if (err)
389262306a36Sopenharmony_ci		return err;
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	err = rtnl_ensure_unique_netns(tb, extack, true);
389562306a36Sopenharmony_ci	if (err)
389662306a36Sopenharmony_ci		return err;
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
389962306a36Sopenharmony_ci	if (ifm->ifi_index > 0)
390062306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ifm->ifi_index);
390162306a36Sopenharmony_ci	else if (tb[IFLA_IFNAME] || tb[IFLA_ALT_IFNAME])
390262306a36Sopenharmony_ci		dev = rtnl_dev_get(net, tb);
390362306a36Sopenharmony_ci	else
390462306a36Sopenharmony_ci		return -EINVAL;
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	if (!dev)
390762306a36Sopenharmony_ci		return -ENODEV;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	if (!tb[IFLA_PROP_LIST])
391062306a36Sopenharmony_ci		return 0;
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	nla_for_each_nested(attr, tb[IFLA_PROP_LIST], rem) {
391362306a36Sopenharmony_ci		switch (nla_type(attr)) {
391462306a36Sopenharmony_ci		case IFLA_ALT_IFNAME:
391562306a36Sopenharmony_ci			err = rtnl_alt_ifname(cmd, dev, attr, &changed, extack);
391662306a36Sopenharmony_ci			if (err)
391762306a36Sopenharmony_ci				return err;
391862306a36Sopenharmony_ci			break;
391962306a36Sopenharmony_ci		}
392062306a36Sopenharmony_ci	}
392162306a36Sopenharmony_ci
392262306a36Sopenharmony_ci	if (changed)
392362306a36Sopenharmony_ci		netdev_state_change(dev);
392462306a36Sopenharmony_ci	return 0;
392562306a36Sopenharmony_ci}
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_cistatic int rtnl_newlinkprop(struct sk_buff *skb, struct nlmsghdr *nlh,
392862306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
392962306a36Sopenharmony_ci{
393062306a36Sopenharmony_ci	return rtnl_linkprop(RTM_NEWLINKPROP, skb, nlh, extack);
393162306a36Sopenharmony_ci}
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_cistatic int rtnl_dellinkprop(struct sk_buff *skb, struct nlmsghdr *nlh,
393462306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
393562306a36Sopenharmony_ci{
393662306a36Sopenharmony_ci	return rtnl_linkprop(RTM_DELLINKPROP, skb, nlh, extack);
393762306a36Sopenharmony_ci}
393862306a36Sopenharmony_ci
393962306a36Sopenharmony_cistatic u32 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh)
394062306a36Sopenharmony_ci{
394162306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
394262306a36Sopenharmony_ci	size_t min_ifinfo_dump_size = 0;
394362306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX+1];
394462306a36Sopenharmony_ci	u32 ext_filter_mask = 0;
394562306a36Sopenharmony_ci	struct net_device *dev;
394662306a36Sopenharmony_ci	int hdrlen;
394762306a36Sopenharmony_ci
394862306a36Sopenharmony_ci	/* Same kernel<->userspace interface hack as in rtnl_dump_ifinfo. */
394962306a36Sopenharmony_ci	hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ?
395062306a36Sopenharmony_ci		 sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg);
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_ci	if (nlmsg_parse_deprecated(nlh, hdrlen, tb, IFLA_MAX, ifla_policy, NULL) >= 0) {
395362306a36Sopenharmony_ci		if (tb[IFLA_EXT_MASK])
395462306a36Sopenharmony_ci			ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
395562306a36Sopenharmony_ci	}
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	if (!ext_filter_mask)
395862306a36Sopenharmony_ci		return NLMSG_GOODSIZE;
395962306a36Sopenharmony_ci	/*
396062306a36Sopenharmony_ci	 * traverse the list of net devices and compute the minimum
396162306a36Sopenharmony_ci	 * buffer size based upon the filter mask.
396262306a36Sopenharmony_ci	 */
396362306a36Sopenharmony_ci	rcu_read_lock();
396462306a36Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
396562306a36Sopenharmony_ci		min_ifinfo_dump_size = max(min_ifinfo_dump_size,
396662306a36Sopenharmony_ci					   if_nlmsg_size(dev, ext_filter_mask));
396762306a36Sopenharmony_ci	}
396862306a36Sopenharmony_ci	rcu_read_unlock();
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_ci	return nlmsg_total_size(min_ifinfo_dump_size);
397162306a36Sopenharmony_ci}
397262306a36Sopenharmony_ci
397362306a36Sopenharmony_cistatic int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
397462306a36Sopenharmony_ci{
397562306a36Sopenharmony_ci	int idx;
397662306a36Sopenharmony_ci	int s_idx = cb->family;
397762306a36Sopenharmony_ci	int type = cb->nlh->nlmsg_type - RTM_BASE;
397862306a36Sopenharmony_ci	int ret = 0;
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	if (s_idx == 0)
398162306a36Sopenharmony_ci		s_idx = 1;
398262306a36Sopenharmony_ci
398362306a36Sopenharmony_ci	for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) {
398462306a36Sopenharmony_ci		struct rtnl_link __rcu **tab;
398562306a36Sopenharmony_ci		struct rtnl_link *link;
398662306a36Sopenharmony_ci		rtnl_dumpit_func dumpit;
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_ci		if (idx < s_idx || idx == PF_PACKET)
398962306a36Sopenharmony_ci			continue;
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_ci		if (type < 0 || type >= RTM_NR_MSGTYPES)
399262306a36Sopenharmony_ci			continue;
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_ci		tab = rcu_dereference_rtnl(rtnl_msg_handlers[idx]);
399562306a36Sopenharmony_ci		if (!tab)
399662306a36Sopenharmony_ci			continue;
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci		link = rcu_dereference_rtnl(tab[type]);
399962306a36Sopenharmony_ci		if (!link)
400062306a36Sopenharmony_ci			continue;
400162306a36Sopenharmony_ci
400262306a36Sopenharmony_ci		dumpit = link->dumpit;
400362306a36Sopenharmony_ci		if (!dumpit)
400462306a36Sopenharmony_ci			continue;
400562306a36Sopenharmony_ci
400662306a36Sopenharmony_ci		if (idx > s_idx) {
400762306a36Sopenharmony_ci			memset(&cb->args[0], 0, sizeof(cb->args));
400862306a36Sopenharmony_ci			cb->prev_seq = 0;
400962306a36Sopenharmony_ci			cb->seq = 0;
401062306a36Sopenharmony_ci		}
401162306a36Sopenharmony_ci		ret = dumpit(skb, cb);
401262306a36Sopenharmony_ci		if (ret)
401362306a36Sopenharmony_ci			break;
401462306a36Sopenharmony_ci	}
401562306a36Sopenharmony_ci	cb->family = idx;
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_ci	return skb->len ? : ret;
401862306a36Sopenharmony_ci}
401962306a36Sopenharmony_ci
402062306a36Sopenharmony_cistruct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
402162306a36Sopenharmony_ci				       unsigned int change,
402262306a36Sopenharmony_ci				       u32 event, gfp_t flags, int *new_nsid,
402362306a36Sopenharmony_ci				       int new_ifindex, u32 portid,
402462306a36Sopenharmony_ci				       const struct nlmsghdr *nlh)
402562306a36Sopenharmony_ci{
402662306a36Sopenharmony_ci	struct net *net = dev_net(dev);
402762306a36Sopenharmony_ci	struct sk_buff *skb;
402862306a36Sopenharmony_ci	int err = -ENOBUFS;
402962306a36Sopenharmony_ci	u32 seq = 0;
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_ci	skb = nlmsg_new(if_nlmsg_size(dev, 0), flags);
403262306a36Sopenharmony_ci	if (skb == NULL)
403362306a36Sopenharmony_ci		goto errout;
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_ci	if (nlmsg_report(nlh))
403662306a36Sopenharmony_ci		seq = nlmsg_seq(nlh);
403762306a36Sopenharmony_ci	else
403862306a36Sopenharmony_ci		portid = 0;
403962306a36Sopenharmony_ci
404062306a36Sopenharmony_ci	err = rtnl_fill_ifinfo(skb, dev, dev_net(dev),
404162306a36Sopenharmony_ci			       type, portid, seq, change, 0, 0, event,
404262306a36Sopenharmony_ci			       new_nsid, new_ifindex, -1, flags);
404362306a36Sopenharmony_ci	if (err < 0) {
404462306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in if_nlmsg_size() */
404562306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
404662306a36Sopenharmony_ci		kfree_skb(skb);
404762306a36Sopenharmony_ci		goto errout;
404862306a36Sopenharmony_ci	}
404962306a36Sopenharmony_ci	return skb;
405062306a36Sopenharmony_cierrout:
405162306a36Sopenharmony_ci	if (err < 0)
405262306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_LINK, err);
405362306a36Sopenharmony_ci	return NULL;
405462306a36Sopenharmony_ci}
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_civoid rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags,
405762306a36Sopenharmony_ci		       u32 portid, const struct nlmsghdr *nlh)
405862306a36Sopenharmony_ci{
405962306a36Sopenharmony_ci	struct net *net = dev_net(dev);
406062306a36Sopenharmony_ci
406162306a36Sopenharmony_ci	rtnl_notify(skb, net, portid, RTNLGRP_LINK, nlh, flags);
406262306a36Sopenharmony_ci}
406362306a36Sopenharmony_ci
406462306a36Sopenharmony_cistatic void rtmsg_ifinfo_event(int type, struct net_device *dev,
406562306a36Sopenharmony_ci			       unsigned int change, u32 event,
406662306a36Sopenharmony_ci			       gfp_t flags, int *new_nsid, int new_ifindex,
406762306a36Sopenharmony_ci			       u32 portid, const struct nlmsghdr *nlh)
406862306a36Sopenharmony_ci{
406962306a36Sopenharmony_ci	struct sk_buff *skb;
407062306a36Sopenharmony_ci
407162306a36Sopenharmony_ci	if (dev->reg_state != NETREG_REGISTERED)
407262306a36Sopenharmony_ci		return;
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	skb = rtmsg_ifinfo_build_skb(type, dev, change, event, flags, new_nsid,
407562306a36Sopenharmony_ci				     new_ifindex, portid, nlh);
407662306a36Sopenharmony_ci	if (skb)
407762306a36Sopenharmony_ci		rtmsg_ifinfo_send(skb, dev, flags, portid, nlh);
407862306a36Sopenharmony_ci}
407962306a36Sopenharmony_ci
408062306a36Sopenharmony_civoid rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change,
408162306a36Sopenharmony_ci		  gfp_t flags, u32 portid, const struct nlmsghdr *nlh)
408262306a36Sopenharmony_ci{
408362306a36Sopenharmony_ci	rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags,
408462306a36Sopenharmony_ci			   NULL, 0, portid, nlh);
408562306a36Sopenharmony_ci}
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_civoid rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change,
408862306a36Sopenharmony_ci			 gfp_t flags, int *new_nsid, int new_ifindex)
408962306a36Sopenharmony_ci{
409062306a36Sopenharmony_ci	rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags,
409162306a36Sopenharmony_ci			   new_nsid, new_ifindex, 0, NULL);
409262306a36Sopenharmony_ci}
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_cistatic int nlmsg_populate_fdb_fill(struct sk_buff *skb,
409562306a36Sopenharmony_ci				   struct net_device *dev,
409662306a36Sopenharmony_ci				   u8 *addr, u16 vid, u32 pid, u32 seq,
409762306a36Sopenharmony_ci				   int type, unsigned int flags,
409862306a36Sopenharmony_ci				   int nlflags, u16 ndm_state)
409962306a36Sopenharmony_ci{
410062306a36Sopenharmony_ci	struct nlmsghdr *nlh;
410162306a36Sopenharmony_ci	struct ndmsg *ndm;
410262306a36Sopenharmony_ci
410362306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), nlflags);
410462306a36Sopenharmony_ci	if (!nlh)
410562306a36Sopenharmony_ci		return -EMSGSIZE;
410662306a36Sopenharmony_ci
410762306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
410862306a36Sopenharmony_ci	ndm->ndm_family  = AF_BRIDGE;
410962306a36Sopenharmony_ci	ndm->ndm_pad1	 = 0;
411062306a36Sopenharmony_ci	ndm->ndm_pad2    = 0;
411162306a36Sopenharmony_ci	ndm->ndm_flags	 = flags;
411262306a36Sopenharmony_ci	ndm->ndm_type	 = 0;
411362306a36Sopenharmony_ci	ndm->ndm_ifindex = dev->ifindex;
411462306a36Sopenharmony_ci	ndm->ndm_state   = ndm_state;
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	if (nla_put(skb, NDA_LLADDR, dev->addr_len, addr))
411762306a36Sopenharmony_ci		goto nla_put_failure;
411862306a36Sopenharmony_ci	if (vid)
411962306a36Sopenharmony_ci		if (nla_put(skb, NDA_VLAN, sizeof(u16), &vid))
412062306a36Sopenharmony_ci			goto nla_put_failure;
412162306a36Sopenharmony_ci
412262306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
412362306a36Sopenharmony_ci	return 0;
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_cinla_put_failure:
412662306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
412762306a36Sopenharmony_ci	return -EMSGSIZE;
412862306a36Sopenharmony_ci}
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_cistatic inline size_t rtnl_fdb_nlmsg_size(const struct net_device *dev)
413162306a36Sopenharmony_ci{
413262306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ndmsg)) +
413362306a36Sopenharmony_ci	       nla_total_size(dev->addr_len) +	/* NDA_LLADDR */
413462306a36Sopenharmony_ci	       nla_total_size(sizeof(u16)) +	/* NDA_VLAN */
413562306a36Sopenharmony_ci	       0;
413662306a36Sopenharmony_ci}
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_cistatic void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type,
413962306a36Sopenharmony_ci			    u16 ndm_state)
414062306a36Sopenharmony_ci{
414162306a36Sopenharmony_ci	struct net *net = dev_net(dev);
414262306a36Sopenharmony_ci	struct sk_buff *skb;
414362306a36Sopenharmony_ci	int err = -ENOBUFS;
414462306a36Sopenharmony_ci
414562306a36Sopenharmony_ci	skb = nlmsg_new(rtnl_fdb_nlmsg_size(dev), GFP_ATOMIC);
414662306a36Sopenharmony_ci	if (!skb)
414762306a36Sopenharmony_ci		goto errout;
414862306a36Sopenharmony_ci
414962306a36Sopenharmony_ci	err = nlmsg_populate_fdb_fill(skb, dev, addr, vid,
415062306a36Sopenharmony_ci				      0, 0, type, NTF_SELF, 0, ndm_state);
415162306a36Sopenharmony_ci	if (err < 0) {
415262306a36Sopenharmony_ci		kfree_skb(skb);
415362306a36Sopenharmony_ci		goto errout;
415462306a36Sopenharmony_ci	}
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
415762306a36Sopenharmony_ci	return;
415862306a36Sopenharmony_cierrout:
415962306a36Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
416062306a36Sopenharmony_ci}
416162306a36Sopenharmony_ci
416262306a36Sopenharmony_ci/*
416362306a36Sopenharmony_ci * ndo_dflt_fdb_add - default netdevice operation to add an FDB entry
416462306a36Sopenharmony_ci */
416562306a36Sopenharmony_ciint ndo_dflt_fdb_add(struct ndmsg *ndm,
416662306a36Sopenharmony_ci		     struct nlattr *tb[],
416762306a36Sopenharmony_ci		     struct net_device *dev,
416862306a36Sopenharmony_ci		     const unsigned char *addr, u16 vid,
416962306a36Sopenharmony_ci		     u16 flags)
417062306a36Sopenharmony_ci{
417162306a36Sopenharmony_ci	int err = -EINVAL;
417262306a36Sopenharmony_ci
417362306a36Sopenharmony_ci	/* If aging addresses are supported device will need to
417462306a36Sopenharmony_ci	 * implement its own handler for this.
417562306a36Sopenharmony_ci	 */
417662306a36Sopenharmony_ci	if (ndm->ndm_state && !(ndm->ndm_state & NUD_PERMANENT)) {
417762306a36Sopenharmony_ci		netdev_info(dev, "default FDB implementation only supports local addresses\n");
417862306a36Sopenharmony_ci		return err;
417962306a36Sopenharmony_ci	}
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	if (tb[NDA_FLAGS_EXT]) {
418262306a36Sopenharmony_ci		netdev_info(dev, "invalid flags given to default FDB implementation\n");
418362306a36Sopenharmony_ci		return err;
418462306a36Sopenharmony_ci	}
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	if (vid) {
418762306a36Sopenharmony_ci		netdev_info(dev, "vlans aren't supported yet for dev_uc|mc_add()\n");
418862306a36Sopenharmony_ci		return err;
418962306a36Sopenharmony_ci	}
419062306a36Sopenharmony_ci
419162306a36Sopenharmony_ci	if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
419262306a36Sopenharmony_ci		err = dev_uc_add_excl(dev, addr);
419362306a36Sopenharmony_ci	else if (is_multicast_ether_addr(addr))
419462306a36Sopenharmony_ci		err = dev_mc_add_excl(dev, addr);
419562306a36Sopenharmony_ci
419662306a36Sopenharmony_ci	/* Only return duplicate errors if NLM_F_EXCL is set */
419762306a36Sopenharmony_ci	if (err == -EEXIST && !(flags & NLM_F_EXCL))
419862306a36Sopenharmony_ci		err = 0;
419962306a36Sopenharmony_ci
420062306a36Sopenharmony_ci	return err;
420162306a36Sopenharmony_ci}
420262306a36Sopenharmony_ciEXPORT_SYMBOL(ndo_dflt_fdb_add);
420362306a36Sopenharmony_ci
420462306a36Sopenharmony_cistatic int fdb_vid_parse(struct nlattr *vlan_attr, u16 *p_vid,
420562306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
420662306a36Sopenharmony_ci{
420762306a36Sopenharmony_ci	u16 vid = 0;
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci	if (vlan_attr) {
421062306a36Sopenharmony_ci		if (nla_len(vlan_attr) != sizeof(u16)) {
421162306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "invalid vlan attribute size");
421262306a36Sopenharmony_ci			return -EINVAL;
421362306a36Sopenharmony_ci		}
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_ci		vid = nla_get_u16(vlan_attr);
421662306a36Sopenharmony_ci
421762306a36Sopenharmony_ci		if (!vid || vid >= VLAN_VID_MASK) {
421862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "invalid vlan id");
421962306a36Sopenharmony_ci			return -EINVAL;
422062306a36Sopenharmony_ci		}
422162306a36Sopenharmony_ci	}
422262306a36Sopenharmony_ci	*p_vid = vid;
422362306a36Sopenharmony_ci	return 0;
422462306a36Sopenharmony_ci}
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_cistatic int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
422762306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
422862306a36Sopenharmony_ci{
422962306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
423062306a36Sopenharmony_ci	struct ndmsg *ndm;
423162306a36Sopenharmony_ci	struct nlattr *tb[NDA_MAX+1];
423262306a36Sopenharmony_ci	struct net_device *dev;
423362306a36Sopenharmony_ci	u8 *addr;
423462306a36Sopenharmony_ci	u16 vid;
423562306a36Sopenharmony_ci	int err;
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, NULL,
423862306a36Sopenharmony_ci				     extack);
423962306a36Sopenharmony_ci	if (err < 0)
424062306a36Sopenharmony_ci		return err;
424162306a36Sopenharmony_ci
424262306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
424362306a36Sopenharmony_ci	if (ndm->ndm_ifindex == 0) {
424462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "invalid ifindex");
424562306a36Sopenharmony_ci		return -EINVAL;
424662306a36Sopenharmony_ci	}
424762306a36Sopenharmony_ci
424862306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ndm->ndm_ifindex);
424962306a36Sopenharmony_ci	if (dev == NULL) {
425062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "unknown ifindex");
425162306a36Sopenharmony_ci		return -ENODEV;
425262306a36Sopenharmony_ci	}
425362306a36Sopenharmony_ci
425462306a36Sopenharmony_ci	if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
425562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "invalid address");
425662306a36Sopenharmony_ci		return -EINVAL;
425762306a36Sopenharmony_ci	}
425862306a36Sopenharmony_ci
425962306a36Sopenharmony_ci	if (dev->type != ARPHRD_ETHER) {
426062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "FDB add only supported for Ethernet devices");
426162306a36Sopenharmony_ci		return -EINVAL;
426262306a36Sopenharmony_ci	}
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	addr = nla_data(tb[NDA_LLADDR]);
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_ci	err = fdb_vid_parse(tb[NDA_VLAN], &vid, extack);
426762306a36Sopenharmony_ci	if (err)
426862306a36Sopenharmony_ci		return err;
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci	err = -EOPNOTSUPP;
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_ci	/* Support fdb on master device the net/bridge default case */
427362306a36Sopenharmony_ci	if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
427462306a36Sopenharmony_ci	    netif_is_bridge_port(dev)) {
427562306a36Sopenharmony_ci		struct net_device *br_dev = netdev_master_upper_dev_get(dev);
427662306a36Sopenharmony_ci		const struct net_device_ops *ops = br_dev->netdev_ops;
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_ci		err = ops->ndo_fdb_add(ndm, tb, dev, addr, vid,
427962306a36Sopenharmony_ci				       nlh->nlmsg_flags, extack);
428062306a36Sopenharmony_ci		if (err)
428162306a36Sopenharmony_ci			goto out;
428262306a36Sopenharmony_ci		else
428362306a36Sopenharmony_ci			ndm->ndm_flags &= ~NTF_MASTER;
428462306a36Sopenharmony_ci	}
428562306a36Sopenharmony_ci
428662306a36Sopenharmony_ci	/* Embedded bridge, macvlan, and any other device support */
428762306a36Sopenharmony_ci	if ((ndm->ndm_flags & NTF_SELF)) {
428862306a36Sopenharmony_ci		if (dev->netdev_ops->ndo_fdb_add)
428962306a36Sopenharmony_ci			err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr,
429062306a36Sopenharmony_ci							   vid,
429162306a36Sopenharmony_ci							   nlh->nlmsg_flags,
429262306a36Sopenharmony_ci							   extack);
429362306a36Sopenharmony_ci		else
429462306a36Sopenharmony_ci			err = ndo_dflt_fdb_add(ndm, tb, dev, addr, vid,
429562306a36Sopenharmony_ci					       nlh->nlmsg_flags);
429662306a36Sopenharmony_ci
429762306a36Sopenharmony_ci		if (!err) {
429862306a36Sopenharmony_ci			rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH,
429962306a36Sopenharmony_ci					ndm->ndm_state);
430062306a36Sopenharmony_ci			ndm->ndm_flags &= ~NTF_SELF;
430162306a36Sopenharmony_ci		}
430262306a36Sopenharmony_ci	}
430362306a36Sopenharmony_ciout:
430462306a36Sopenharmony_ci	return err;
430562306a36Sopenharmony_ci}
430662306a36Sopenharmony_ci
430762306a36Sopenharmony_ci/*
430862306a36Sopenharmony_ci * ndo_dflt_fdb_del - default netdevice operation to delete an FDB entry
430962306a36Sopenharmony_ci */
431062306a36Sopenharmony_ciint ndo_dflt_fdb_del(struct ndmsg *ndm,
431162306a36Sopenharmony_ci		     struct nlattr *tb[],
431262306a36Sopenharmony_ci		     struct net_device *dev,
431362306a36Sopenharmony_ci		     const unsigned char *addr, u16 vid)
431462306a36Sopenharmony_ci{
431562306a36Sopenharmony_ci	int err = -EINVAL;
431662306a36Sopenharmony_ci
431762306a36Sopenharmony_ci	/* If aging addresses are supported device will need to
431862306a36Sopenharmony_ci	 * implement its own handler for this.
431962306a36Sopenharmony_ci	 */
432062306a36Sopenharmony_ci	if (!(ndm->ndm_state & NUD_PERMANENT)) {
432162306a36Sopenharmony_ci		netdev_info(dev, "default FDB implementation only supports local addresses\n");
432262306a36Sopenharmony_ci		return err;
432362306a36Sopenharmony_ci	}
432462306a36Sopenharmony_ci
432562306a36Sopenharmony_ci	if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr))
432662306a36Sopenharmony_ci		err = dev_uc_del(dev, addr);
432762306a36Sopenharmony_ci	else if (is_multicast_ether_addr(addr))
432862306a36Sopenharmony_ci		err = dev_mc_del(dev, addr);
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci	return err;
433162306a36Sopenharmony_ci}
433262306a36Sopenharmony_ciEXPORT_SYMBOL(ndo_dflt_fdb_del);
433362306a36Sopenharmony_ci
433462306a36Sopenharmony_cistatic const struct nla_policy fdb_del_bulk_policy[NDA_MAX + 1] = {
433562306a36Sopenharmony_ci	[NDA_VLAN]	= { .type = NLA_U16 },
433662306a36Sopenharmony_ci	[NDA_IFINDEX]	= NLA_POLICY_MIN(NLA_S32, 1),
433762306a36Sopenharmony_ci	[NDA_NDM_STATE_MASK]	= { .type = NLA_U16  },
433862306a36Sopenharmony_ci	[NDA_NDM_FLAGS_MASK]	= { .type = NLA_U8 },
433962306a36Sopenharmony_ci};
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_cistatic int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
434262306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
434362306a36Sopenharmony_ci{
434462306a36Sopenharmony_ci	bool del_bulk = !!(nlh->nlmsg_flags & NLM_F_BULK);
434562306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
434662306a36Sopenharmony_ci	const struct net_device_ops *ops;
434762306a36Sopenharmony_ci	struct ndmsg *ndm;
434862306a36Sopenharmony_ci	struct nlattr *tb[NDA_MAX+1];
434962306a36Sopenharmony_ci	struct net_device *dev;
435062306a36Sopenharmony_ci	__u8 *addr = NULL;
435162306a36Sopenharmony_ci	int err;
435262306a36Sopenharmony_ci	u16 vid;
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_NET_ADMIN))
435562306a36Sopenharmony_ci		return -EPERM;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	if (!del_bulk) {
435862306a36Sopenharmony_ci		err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX,
435962306a36Sopenharmony_ci					     NULL, extack);
436062306a36Sopenharmony_ci	} else {
436162306a36Sopenharmony_ci		err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX,
436262306a36Sopenharmony_ci				  fdb_del_bulk_policy, extack);
436362306a36Sopenharmony_ci	}
436462306a36Sopenharmony_ci	if (err < 0)
436562306a36Sopenharmony_ci		return err;
436662306a36Sopenharmony_ci
436762306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
436862306a36Sopenharmony_ci	if (ndm->ndm_ifindex == 0) {
436962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "invalid ifindex");
437062306a36Sopenharmony_ci		return -EINVAL;
437162306a36Sopenharmony_ci	}
437262306a36Sopenharmony_ci
437362306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ndm->ndm_ifindex);
437462306a36Sopenharmony_ci	if (dev == NULL) {
437562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "unknown ifindex");
437662306a36Sopenharmony_ci		return -ENODEV;
437762306a36Sopenharmony_ci	}
437862306a36Sopenharmony_ci
437962306a36Sopenharmony_ci	if (!del_bulk) {
438062306a36Sopenharmony_ci		if (!tb[NDA_LLADDR] || nla_len(tb[NDA_LLADDR]) != ETH_ALEN) {
438162306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "invalid address");
438262306a36Sopenharmony_ci			return -EINVAL;
438362306a36Sopenharmony_ci		}
438462306a36Sopenharmony_ci		addr = nla_data(tb[NDA_LLADDR]);
438562306a36Sopenharmony_ci	}
438662306a36Sopenharmony_ci
438762306a36Sopenharmony_ci	if (dev->type != ARPHRD_ETHER) {
438862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "FDB delete only supported for Ethernet devices");
438962306a36Sopenharmony_ci		return -EINVAL;
439062306a36Sopenharmony_ci	}
439162306a36Sopenharmony_ci
439262306a36Sopenharmony_ci	err = fdb_vid_parse(tb[NDA_VLAN], &vid, extack);
439362306a36Sopenharmony_ci	if (err)
439462306a36Sopenharmony_ci		return err;
439562306a36Sopenharmony_ci
439662306a36Sopenharmony_ci	err = -EOPNOTSUPP;
439762306a36Sopenharmony_ci
439862306a36Sopenharmony_ci	/* Support fdb on master device the net/bridge default case */
439962306a36Sopenharmony_ci	if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
440062306a36Sopenharmony_ci	    netif_is_bridge_port(dev)) {
440162306a36Sopenharmony_ci		struct net_device *br_dev = netdev_master_upper_dev_get(dev);
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci		ops = br_dev->netdev_ops;
440462306a36Sopenharmony_ci		if (!del_bulk) {
440562306a36Sopenharmony_ci			if (ops->ndo_fdb_del)
440662306a36Sopenharmony_ci				err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid, extack);
440762306a36Sopenharmony_ci		} else {
440862306a36Sopenharmony_ci			if (ops->ndo_fdb_del_bulk)
440962306a36Sopenharmony_ci				err = ops->ndo_fdb_del_bulk(ndm, tb, dev, vid,
441062306a36Sopenharmony_ci							    extack);
441162306a36Sopenharmony_ci		}
441262306a36Sopenharmony_ci
441362306a36Sopenharmony_ci		if (err)
441462306a36Sopenharmony_ci			goto out;
441562306a36Sopenharmony_ci		else
441662306a36Sopenharmony_ci			ndm->ndm_flags &= ~NTF_MASTER;
441762306a36Sopenharmony_ci	}
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci	/* Embedded bridge, macvlan, and any other device support */
442062306a36Sopenharmony_ci	if (ndm->ndm_flags & NTF_SELF) {
442162306a36Sopenharmony_ci		ops = dev->netdev_ops;
442262306a36Sopenharmony_ci		if (!del_bulk) {
442362306a36Sopenharmony_ci			if (ops->ndo_fdb_del)
442462306a36Sopenharmony_ci				err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid, extack);
442562306a36Sopenharmony_ci			else
442662306a36Sopenharmony_ci				err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
442762306a36Sopenharmony_ci		} else {
442862306a36Sopenharmony_ci			/* in case err was cleared by NTF_MASTER call */
442962306a36Sopenharmony_ci			err = -EOPNOTSUPP;
443062306a36Sopenharmony_ci			if (ops->ndo_fdb_del_bulk)
443162306a36Sopenharmony_ci				err = ops->ndo_fdb_del_bulk(ndm, tb, dev, vid,
443262306a36Sopenharmony_ci							    extack);
443362306a36Sopenharmony_ci		}
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci		if (!err) {
443662306a36Sopenharmony_ci			if (!del_bulk)
443762306a36Sopenharmony_ci				rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH,
443862306a36Sopenharmony_ci						ndm->ndm_state);
443962306a36Sopenharmony_ci			ndm->ndm_flags &= ~NTF_SELF;
444062306a36Sopenharmony_ci		}
444162306a36Sopenharmony_ci	}
444262306a36Sopenharmony_ciout:
444362306a36Sopenharmony_ci	return err;
444462306a36Sopenharmony_ci}
444562306a36Sopenharmony_ci
444662306a36Sopenharmony_cistatic int nlmsg_populate_fdb(struct sk_buff *skb,
444762306a36Sopenharmony_ci			      struct netlink_callback *cb,
444862306a36Sopenharmony_ci			      struct net_device *dev,
444962306a36Sopenharmony_ci			      int *idx,
445062306a36Sopenharmony_ci			      struct netdev_hw_addr_list *list)
445162306a36Sopenharmony_ci{
445262306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
445362306a36Sopenharmony_ci	int err;
445462306a36Sopenharmony_ci	u32 portid, seq;
445562306a36Sopenharmony_ci
445662306a36Sopenharmony_ci	portid = NETLINK_CB(cb->skb).portid;
445762306a36Sopenharmony_ci	seq = cb->nlh->nlmsg_seq;
445862306a36Sopenharmony_ci
445962306a36Sopenharmony_ci	list_for_each_entry(ha, &list->list, list) {
446062306a36Sopenharmony_ci		if (*idx < cb->args[2])
446162306a36Sopenharmony_ci			goto skip;
446262306a36Sopenharmony_ci
446362306a36Sopenharmony_ci		err = nlmsg_populate_fdb_fill(skb, dev, ha->addr, 0,
446462306a36Sopenharmony_ci					      portid, seq,
446562306a36Sopenharmony_ci					      RTM_NEWNEIGH, NTF_SELF,
446662306a36Sopenharmony_ci					      NLM_F_MULTI, NUD_PERMANENT);
446762306a36Sopenharmony_ci		if (err < 0)
446862306a36Sopenharmony_ci			return err;
446962306a36Sopenharmony_ciskip:
447062306a36Sopenharmony_ci		*idx += 1;
447162306a36Sopenharmony_ci	}
447262306a36Sopenharmony_ci	return 0;
447362306a36Sopenharmony_ci}
447462306a36Sopenharmony_ci
447562306a36Sopenharmony_ci/**
447662306a36Sopenharmony_ci * ndo_dflt_fdb_dump - default netdevice operation to dump an FDB table.
447762306a36Sopenharmony_ci * @skb: socket buffer to store message in
447862306a36Sopenharmony_ci * @cb: netlink callback
447962306a36Sopenharmony_ci * @dev: netdevice
448062306a36Sopenharmony_ci * @filter_dev: ignored
448162306a36Sopenharmony_ci * @idx: the number of FDB table entries dumped is added to *@idx
448262306a36Sopenharmony_ci *
448362306a36Sopenharmony_ci * Default netdevice operation to dump the existing unicast address list.
448462306a36Sopenharmony_ci * Returns number of addresses from list put in skb.
448562306a36Sopenharmony_ci */
448662306a36Sopenharmony_ciint ndo_dflt_fdb_dump(struct sk_buff *skb,
448762306a36Sopenharmony_ci		      struct netlink_callback *cb,
448862306a36Sopenharmony_ci		      struct net_device *dev,
448962306a36Sopenharmony_ci		      struct net_device *filter_dev,
449062306a36Sopenharmony_ci		      int *idx)
449162306a36Sopenharmony_ci{
449262306a36Sopenharmony_ci	int err;
449362306a36Sopenharmony_ci
449462306a36Sopenharmony_ci	if (dev->type != ARPHRD_ETHER)
449562306a36Sopenharmony_ci		return -EINVAL;
449662306a36Sopenharmony_ci
449762306a36Sopenharmony_ci	netif_addr_lock_bh(dev);
449862306a36Sopenharmony_ci	err = nlmsg_populate_fdb(skb, cb, dev, idx, &dev->uc);
449962306a36Sopenharmony_ci	if (err)
450062306a36Sopenharmony_ci		goto out;
450162306a36Sopenharmony_ci	err = nlmsg_populate_fdb(skb, cb, dev, idx, &dev->mc);
450262306a36Sopenharmony_ciout:
450362306a36Sopenharmony_ci	netif_addr_unlock_bh(dev);
450462306a36Sopenharmony_ci	return err;
450562306a36Sopenharmony_ci}
450662306a36Sopenharmony_ciEXPORT_SYMBOL(ndo_dflt_fdb_dump);
450762306a36Sopenharmony_ci
450862306a36Sopenharmony_cistatic int valid_fdb_dump_strict(const struct nlmsghdr *nlh,
450962306a36Sopenharmony_ci				 int *br_idx, int *brport_idx,
451062306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
451162306a36Sopenharmony_ci{
451262306a36Sopenharmony_ci	struct nlattr *tb[NDA_MAX + 1];
451362306a36Sopenharmony_ci	struct ndmsg *ndm;
451462306a36Sopenharmony_ci	int err, i;
451562306a36Sopenharmony_ci
451662306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
451762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid header for fdb dump request");
451862306a36Sopenharmony_ci		return -EINVAL;
451962306a36Sopenharmony_ci	}
452062306a36Sopenharmony_ci
452162306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
452262306a36Sopenharmony_ci	if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_state ||
452362306a36Sopenharmony_ci	    ndm->ndm_flags || ndm->ndm_type) {
452462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid values in header for fdb dump request");
452562306a36Sopenharmony_ci		return -EINVAL;
452662306a36Sopenharmony_ci	}
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb,
452962306a36Sopenharmony_ci					    NDA_MAX, NULL, extack);
453062306a36Sopenharmony_ci	if (err < 0)
453162306a36Sopenharmony_ci		return err;
453262306a36Sopenharmony_ci
453362306a36Sopenharmony_ci	*brport_idx = ndm->ndm_ifindex;
453462306a36Sopenharmony_ci	for (i = 0; i <= NDA_MAX; ++i) {
453562306a36Sopenharmony_ci		if (!tb[i])
453662306a36Sopenharmony_ci			continue;
453762306a36Sopenharmony_ci
453862306a36Sopenharmony_ci		switch (i) {
453962306a36Sopenharmony_ci		case NDA_IFINDEX:
454062306a36Sopenharmony_ci			if (nla_len(tb[i]) != sizeof(u32)) {
454162306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Invalid IFINDEX attribute in fdb dump request");
454262306a36Sopenharmony_ci				return -EINVAL;
454362306a36Sopenharmony_ci			}
454462306a36Sopenharmony_ci			*brport_idx = nla_get_u32(tb[NDA_IFINDEX]);
454562306a36Sopenharmony_ci			break;
454662306a36Sopenharmony_ci		case NDA_MASTER:
454762306a36Sopenharmony_ci			if (nla_len(tb[i]) != sizeof(u32)) {
454862306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Invalid MASTER attribute in fdb dump request");
454962306a36Sopenharmony_ci				return -EINVAL;
455062306a36Sopenharmony_ci			}
455162306a36Sopenharmony_ci			*br_idx = nla_get_u32(tb[NDA_MASTER]);
455262306a36Sopenharmony_ci			break;
455362306a36Sopenharmony_ci		default:
455462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Unsupported attribute in fdb dump request");
455562306a36Sopenharmony_ci			return -EINVAL;
455662306a36Sopenharmony_ci		}
455762306a36Sopenharmony_ci	}
455862306a36Sopenharmony_ci
455962306a36Sopenharmony_ci	return 0;
456062306a36Sopenharmony_ci}
456162306a36Sopenharmony_ci
456262306a36Sopenharmony_cistatic int valid_fdb_dump_legacy(const struct nlmsghdr *nlh,
456362306a36Sopenharmony_ci				 int *br_idx, int *brport_idx,
456462306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
456562306a36Sopenharmony_ci{
456662306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX+1];
456762306a36Sopenharmony_ci	int err;
456862306a36Sopenharmony_ci
456962306a36Sopenharmony_ci	/* A hack to preserve kernel<->userspace interface.
457062306a36Sopenharmony_ci	 * Before Linux v4.12 this code accepted ndmsg since iproute2 v3.3.0.
457162306a36Sopenharmony_ci	 * However, ndmsg is shorter than ifinfomsg thus nlmsg_parse() bails.
457262306a36Sopenharmony_ci	 * So, check for ndmsg with an optional u32 attribute (not used here).
457362306a36Sopenharmony_ci	 * Fortunately these sizes don't conflict with the size of ifinfomsg
457462306a36Sopenharmony_ci	 * with an optional attribute.
457562306a36Sopenharmony_ci	 */
457662306a36Sopenharmony_ci	if (nlmsg_len(nlh) != sizeof(struct ndmsg) &&
457762306a36Sopenharmony_ci	    (nlmsg_len(nlh) != sizeof(struct ndmsg) +
457862306a36Sopenharmony_ci	     nla_attr_size(sizeof(u32)))) {
457962306a36Sopenharmony_ci		struct ifinfomsg *ifm;
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci		err = nlmsg_parse_deprecated(nlh, sizeof(struct ifinfomsg),
458262306a36Sopenharmony_ci					     tb, IFLA_MAX, ifla_policy,
458362306a36Sopenharmony_ci					     extack);
458462306a36Sopenharmony_ci		if (err < 0) {
458562306a36Sopenharmony_ci			return -EINVAL;
458662306a36Sopenharmony_ci		} else if (err == 0) {
458762306a36Sopenharmony_ci			if (tb[IFLA_MASTER])
458862306a36Sopenharmony_ci				*br_idx = nla_get_u32(tb[IFLA_MASTER]);
458962306a36Sopenharmony_ci		}
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ci		ifm = nlmsg_data(nlh);
459262306a36Sopenharmony_ci		*brport_idx = ifm->ifi_index;
459362306a36Sopenharmony_ci	}
459462306a36Sopenharmony_ci	return 0;
459562306a36Sopenharmony_ci}
459662306a36Sopenharmony_ci
459762306a36Sopenharmony_cistatic int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
459862306a36Sopenharmony_ci{
459962306a36Sopenharmony_ci	struct net_device *dev;
460062306a36Sopenharmony_ci	struct net_device *br_dev = NULL;
460162306a36Sopenharmony_ci	const struct net_device_ops *ops = NULL;
460262306a36Sopenharmony_ci	const struct net_device_ops *cops = NULL;
460362306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
460462306a36Sopenharmony_ci	struct hlist_head *head;
460562306a36Sopenharmony_ci	int brport_idx = 0;
460662306a36Sopenharmony_ci	int br_idx = 0;
460762306a36Sopenharmony_ci	int h, s_h;
460862306a36Sopenharmony_ci	int idx = 0, s_idx;
460962306a36Sopenharmony_ci	int err = 0;
461062306a36Sopenharmony_ci	int fidx = 0;
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_ci	if (cb->strict_check)
461362306a36Sopenharmony_ci		err = valid_fdb_dump_strict(cb->nlh, &br_idx, &brport_idx,
461462306a36Sopenharmony_ci					    cb->extack);
461562306a36Sopenharmony_ci	else
461662306a36Sopenharmony_ci		err = valid_fdb_dump_legacy(cb->nlh, &br_idx, &brport_idx,
461762306a36Sopenharmony_ci					    cb->extack);
461862306a36Sopenharmony_ci	if (err < 0)
461962306a36Sopenharmony_ci		return err;
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_ci	if (br_idx) {
462262306a36Sopenharmony_ci		br_dev = __dev_get_by_index(net, br_idx);
462362306a36Sopenharmony_ci		if (!br_dev)
462462306a36Sopenharmony_ci			return -ENODEV;
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ci		ops = br_dev->netdev_ops;
462762306a36Sopenharmony_ci	}
462862306a36Sopenharmony_ci
462962306a36Sopenharmony_ci	s_h = cb->args[0];
463062306a36Sopenharmony_ci	s_idx = cb->args[1];
463162306a36Sopenharmony_ci
463262306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
463362306a36Sopenharmony_ci		idx = 0;
463462306a36Sopenharmony_ci		head = &net->dev_index_head[h];
463562306a36Sopenharmony_ci		hlist_for_each_entry(dev, head, index_hlist) {
463662306a36Sopenharmony_ci
463762306a36Sopenharmony_ci			if (brport_idx && (dev->ifindex != brport_idx))
463862306a36Sopenharmony_ci				continue;
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci			if (!br_idx) { /* user did not specify a specific bridge */
464162306a36Sopenharmony_ci				if (netif_is_bridge_port(dev)) {
464262306a36Sopenharmony_ci					br_dev = netdev_master_upper_dev_get(dev);
464362306a36Sopenharmony_ci					cops = br_dev->netdev_ops;
464462306a36Sopenharmony_ci				}
464562306a36Sopenharmony_ci			} else {
464662306a36Sopenharmony_ci				if (dev != br_dev &&
464762306a36Sopenharmony_ci				    !netif_is_bridge_port(dev))
464862306a36Sopenharmony_ci					continue;
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci				if (br_dev != netdev_master_upper_dev_get(dev) &&
465162306a36Sopenharmony_ci				    !netif_is_bridge_master(dev))
465262306a36Sopenharmony_ci					continue;
465362306a36Sopenharmony_ci				cops = ops;
465462306a36Sopenharmony_ci			}
465562306a36Sopenharmony_ci
465662306a36Sopenharmony_ci			if (idx < s_idx)
465762306a36Sopenharmony_ci				goto cont;
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci			if (netif_is_bridge_port(dev)) {
466062306a36Sopenharmony_ci				if (cops && cops->ndo_fdb_dump) {
466162306a36Sopenharmony_ci					err = cops->ndo_fdb_dump(skb, cb,
466262306a36Sopenharmony_ci								br_dev, dev,
466362306a36Sopenharmony_ci								&fidx);
466462306a36Sopenharmony_ci					if (err == -EMSGSIZE)
466562306a36Sopenharmony_ci						goto out;
466662306a36Sopenharmony_ci				}
466762306a36Sopenharmony_ci			}
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci			if (dev->netdev_ops->ndo_fdb_dump)
467062306a36Sopenharmony_ci				err = dev->netdev_ops->ndo_fdb_dump(skb, cb,
467162306a36Sopenharmony_ci								    dev, NULL,
467262306a36Sopenharmony_ci								    &fidx);
467362306a36Sopenharmony_ci			else
467462306a36Sopenharmony_ci				err = ndo_dflt_fdb_dump(skb, cb, dev, NULL,
467562306a36Sopenharmony_ci							&fidx);
467662306a36Sopenharmony_ci			if (err == -EMSGSIZE)
467762306a36Sopenharmony_ci				goto out;
467862306a36Sopenharmony_ci
467962306a36Sopenharmony_ci			cops = NULL;
468062306a36Sopenharmony_ci
468162306a36Sopenharmony_ci			/* reset fdb offset to 0 for rest of the interfaces */
468262306a36Sopenharmony_ci			cb->args[2] = 0;
468362306a36Sopenharmony_ci			fidx = 0;
468462306a36Sopenharmony_cicont:
468562306a36Sopenharmony_ci			idx++;
468662306a36Sopenharmony_ci		}
468762306a36Sopenharmony_ci	}
468862306a36Sopenharmony_ci
468962306a36Sopenharmony_ciout:
469062306a36Sopenharmony_ci	cb->args[0] = h;
469162306a36Sopenharmony_ci	cb->args[1] = idx;
469262306a36Sopenharmony_ci	cb->args[2] = fidx;
469362306a36Sopenharmony_ci
469462306a36Sopenharmony_ci	return skb->len;
469562306a36Sopenharmony_ci}
469662306a36Sopenharmony_ci
469762306a36Sopenharmony_cistatic int valid_fdb_get_strict(const struct nlmsghdr *nlh,
469862306a36Sopenharmony_ci				struct nlattr **tb, u8 *ndm_flags,
469962306a36Sopenharmony_ci				int *br_idx, int *brport_idx, u8 **addr,
470062306a36Sopenharmony_ci				u16 *vid, struct netlink_ext_ack *extack)
470162306a36Sopenharmony_ci{
470262306a36Sopenharmony_ci	struct ndmsg *ndm;
470362306a36Sopenharmony_ci	int err, i;
470462306a36Sopenharmony_ci
470562306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
470662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid header for fdb get request");
470762306a36Sopenharmony_ci		return -EINVAL;
470862306a36Sopenharmony_ci	}
470962306a36Sopenharmony_ci
471062306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
471162306a36Sopenharmony_ci	if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_state ||
471262306a36Sopenharmony_ci	    ndm->ndm_type) {
471362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid values in header for fdb get request");
471462306a36Sopenharmony_ci		return -EINVAL;
471562306a36Sopenharmony_ci	}
471662306a36Sopenharmony_ci
471762306a36Sopenharmony_ci	if (ndm->ndm_flags & ~(NTF_MASTER | NTF_SELF)) {
471862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid flags in header for fdb get request");
471962306a36Sopenharmony_ci		return -EINVAL;
472062306a36Sopenharmony_ci	}
472162306a36Sopenharmony_ci
472262306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb,
472362306a36Sopenharmony_ci					    NDA_MAX, nda_policy, extack);
472462306a36Sopenharmony_ci	if (err < 0)
472562306a36Sopenharmony_ci		return err;
472662306a36Sopenharmony_ci
472762306a36Sopenharmony_ci	*ndm_flags = ndm->ndm_flags;
472862306a36Sopenharmony_ci	*brport_idx = ndm->ndm_ifindex;
472962306a36Sopenharmony_ci	for (i = 0; i <= NDA_MAX; ++i) {
473062306a36Sopenharmony_ci		if (!tb[i])
473162306a36Sopenharmony_ci			continue;
473262306a36Sopenharmony_ci
473362306a36Sopenharmony_ci		switch (i) {
473462306a36Sopenharmony_ci		case NDA_MASTER:
473562306a36Sopenharmony_ci			*br_idx = nla_get_u32(tb[i]);
473662306a36Sopenharmony_ci			break;
473762306a36Sopenharmony_ci		case NDA_LLADDR:
473862306a36Sopenharmony_ci			if (nla_len(tb[i]) != ETH_ALEN) {
473962306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Invalid address in fdb get request");
474062306a36Sopenharmony_ci				return -EINVAL;
474162306a36Sopenharmony_ci			}
474262306a36Sopenharmony_ci			*addr = nla_data(tb[i]);
474362306a36Sopenharmony_ci			break;
474462306a36Sopenharmony_ci		case NDA_VLAN:
474562306a36Sopenharmony_ci			err = fdb_vid_parse(tb[i], vid, extack);
474662306a36Sopenharmony_ci			if (err)
474762306a36Sopenharmony_ci				return err;
474862306a36Sopenharmony_ci			break;
474962306a36Sopenharmony_ci		case NDA_VNI:
475062306a36Sopenharmony_ci			break;
475162306a36Sopenharmony_ci		default:
475262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Unsupported attribute in fdb get request");
475362306a36Sopenharmony_ci			return -EINVAL;
475462306a36Sopenharmony_ci		}
475562306a36Sopenharmony_ci	}
475662306a36Sopenharmony_ci
475762306a36Sopenharmony_ci	return 0;
475862306a36Sopenharmony_ci}
475962306a36Sopenharmony_ci
476062306a36Sopenharmony_cistatic int rtnl_fdb_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
476162306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
476262306a36Sopenharmony_ci{
476362306a36Sopenharmony_ci	struct net_device *dev = NULL, *br_dev = NULL;
476462306a36Sopenharmony_ci	const struct net_device_ops *ops = NULL;
476562306a36Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
476662306a36Sopenharmony_ci	struct nlattr *tb[NDA_MAX + 1];
476762306a36Sopenharmony_ci	struct sk_buff *skb;
476862306a36Sopenharmony_ci	int brport_idx = 0;
476962306a36Sopenharmony_ci	u8 ndm_flags = 0;
477062306a36Sopenharmony_ci	int br_idx = 0;
477162306a36Sopenharmony_ci	u8 *addr = NULL;
477262306a36Sopenharmony_ci	u16 vid = 0;
477362306a36Sopenharmony_ci	int err;
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	err = valid_fdb_get_strict(nlh, tb, &ndm_flags, &br_idx,
477662306a36Sopenharmony_ci				   &brport_idx, &addr, &vid, extack);
477762306a36Sopenharmony_ci	if (err < 0)
477862306a36Sopenharmony_ci		return err;
477962306a36Sopenharmony_ci
478062306a36Sopenharmony_ci	if (!addr) {
478162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Missing lookup address for fdb get request");
478262306a36Sopenharmony_ci		return -EINVAL;
478362306a36Sopenharmony_ci	}
478462306a36Sopenharmony_ci
478562306a36Sopenharmony_ci	if (brport_idx) {
478662306a36Sopenharmony_ci		dev = __dev_get_by_index(net, brport_idx);
478762306a36Sopenharmony_ci		if (!dev) {
478862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Unknown device ifindex");
478962306a36Sopenharmony_ci			return -ENODEV;
479062306a36Sopenharmony_ci		}
479162306a36Sopenharmony_ci	}
479262306a36Sopenharmony_ci
479362306a36Sopenharmony_ci	if (br_idx) {
479462306a36Sopenharmony_ci		if (dev) {
479562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Master and device are mutually exclusive");
479662306a36Sopenharmony_ci			return -EINVAL;
479762306a36Sopenharmony_ci		}
479862306a36Sopenharmony_ci
479962306a36Sopenharmony_ci		br_dev = __dev_get_by_index(net, br_idx);
480062306a36Sopenharmony_ci		if (!br_dev) {
480162306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid master ifindex");
480262306a36Sopenharmony_ci			return -EINVAL;
480362306a36Sopenharmony_ci		}
480462306a36Sopenharmony_ci		ops = br_dev->netdev_ops;
480562306a36Sopenharmony_ci	}
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci	if (dev) {
480862306a36Sopenharmony_ci		if (!ndm_flags || (ndm_flags & NTF_MASTER)) {
480962306a36Sopenharmony_ci			if (!netif_is_bridge_port(dev)) {
481062306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Device is not a bridge port");
481162306a36Sopenharmony_ci				return -EINVAL;
481262306a36Sopenharmony_ci			}
481362306a36Sopenharmony_ci			br_dev = netdev_master_upper_dev_get(dev);
481462306a36Sopenharmony_ci			if (!br_dev) {
481562306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Master of device not found");
481662306a36Sopenharmony_ci				return -EINVAL;
481762306a36Sopenharmony_ci			}
481862306a36Sopenharmony_ci			ops = br_dev->netdev_ops;
481962306a36Sopenharmony_ci		} else {
482062306a36Sopenharmony_ci			if (!(ndm_flags & NTF_SELF)) {
482162306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Missing NTF_SELF");
482262306a36Sopenharmony_ci				return -EINVAL;
482362306a36Sopenharmony_ci			}
482462306a36Sopenharmony_ci			ops = dev->netdev_ops;
482562306a36Sopenharmony_ci		}
482662306a36Sopenharmony_ci	}
482762306a36Sopenharmony_ci
482862306a36Sopenharmony_ci	if (!br_dev && !dev) {
482962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "No device specified");
483062306a36Sopenharmony_ci		return -ENODEV;
483162306a36Sopenharmony_ci	}
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	if (!ops || !ops->ndo_fdb_get) {
483462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Fdb get operation not supported by device");
483562306a36Sopenharmony_ci		return -EOPNOTSUPP;
483662306a36Sopenharmony_ci	}
483762306a36Sopenharmony_ci
483862306a36Sopenharmony_ci	skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
483962306a36Sopenharmony_ci	if (!skb)
484062306a36Sopenharmony_ci		return -ENOBUFS;
484162306a36Sopenharmony_ci
484262306a36Sopenharmony_ci	if (br_dev)
484362306a36Sopenharmony_ci		dev = br_dev;
484462306a36Sopenharmony_ci	err = ops->ndo_fdb_get(skb, tb, dev, addr, vid,
484562306a36Sopenharmony_ci			       NETLINK_CB(in_skb).portid,
484662306a36Sopenharmony_ci			       nlh->nlmsg_seq, extack);
484762306a36Sopenharmony_ci	if (err)
484862306a36Sopenharmony_ci		goto out;
484962306a36Sopenharmony_ci
485062306a36Sopenharmony_ci	return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
485162306a36Sopenharmony_ciout:
485262306a36Sopenharmony_ci	kfree_skb(skb);
485362306a36Sopenharmony_ci	return err;
485462306a36Sopenharmony_ci}
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_cistatic int brport_nla_put_flag(struct sk_buff *skb, u32 flags, u32 mask,
485762306a36Sopenharmony_ci			       unsigned int attrnum, unsigned int flag)
485862306a36Sopenharmony_ci{
485962306a36Sopenharmony_ci	if (mask & flag)
486062306a36Sopenharmony_ci		return nla_put_u8(skb, attrnum, !!(flags & flag));
486162306a36Sopenharmony_ci	return 0;
486262306a36Sopenharmony_ci}
486362306a36Sopenharmony_ci
486462306a36Sopenharmony_ciint ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
486562306a36Sopenharmony_ci			    struct net_device *dev, u16 mode,
486662306a36Sopenharmony_ci			    u32 flags, u32 mask, int nlflags,
486762306a36Sopenharmony_ci			    u32 filter_mask,
486862306a36Sopenharmony_ci			    int (*vlan_fill)(struct sk_buff *skb,
486962306a36Sopenharmony_ci					     struct net_device *dev,
487062306a36Sopenharmony_ci					     u32 filter_mask))
487162306a36Sopenharmony_ci{
487262306a36Sopenharmony_ci	struct nlmsghdr *nlh;
487362306a36Sopenharmony_ci	struct ifinfomsg *ifm;
487462306a36Sopenharmony_ci	struct nlattr *br_afspec;
487562306a36Sopenharmony_ci	struct nlattr *protinfo;
487662306a36Sopenharmony_ci	u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
487762306a36Sopenharmony_ci	struct net_device *br_dev = netdev_master_upper_dev_get(dev);
487862306a36Sopenharmony_ci	int err = 0;
487962306a36Sopenharmony_ci
488062306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), nlflags);
488162306a36Sopenharmony_ci	if (nlh == NULL)
488262306a36Sopenharmony_ci		return -EMSGSIZE;
488362306a36Sopenharmony_ci
488462306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
488562306a36Sopenharmony_ci	ifm->ifi_family = AF_BRIDGE;
488662306a36Sopenharmony_ci	ifm->__ifi_pad = 0;
488762306a36Sopenharmony_ci	ifm->ifi_type = dev->type;
488862306a36Sopenharmony_ci	ifm->ifi_index = dev->ifindex;
488962306a36Sopenharmony_ci	ifm->ifi_flags = dev_get_flags(dev);
489062306a36Sopenharmony_ci	ifm->ifi_change = 0;
489162306a36Sopenharmony_ci
489262306a36Sopenharmony_ci
489362306a36Sopenharmony_ci	if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
489462306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
489562306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_OPERSTATE, operstate) ||
489662306a36Sopenharmony_ci	    (br_dev &&
489762306a36Sopenharmony_ci	     nla_put_u32(skb, IFLA_MASTER, br_dev->ifindex)) ||
489862306a36Sopenharmony_ci	    (dev->addr_len &&
489962306a36Sopenharmony_ci	     nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
490062306a36Sopenharmony_ci	    (dev->ifindex != dev_get_iflink(dev) &&
490162306a36Sopenharmony_ci	     nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))))
490262306a36Sopenharmony_ci		goto nla_put_failure;
490362306a36Sopenharmony_ci
490462306a36Sopenharmony_ci	br_afspec = nla_nest_start_noflag(skb, IFLA_AF_SPEC);
490562306a36Sopenharmony_ci	if (!br_afspec)
490662306a36Sopenharmony_ci		goto nla_put_failure;
490762306a36Sopenharmony_ci
490862306a36Sopenharmony_ci	if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF)) {
490962306a36Sopenharmony_ci		nla_nest_cancel(skb, br_afspec);
491062306a36Sopenharmony_ci		goto nla_put_failure;
491162306a36Sopenharmony_ci	}
491262306a36Sopenharmony_ci
491362306a36Sopenharmony_ci	if (mode != BRIDGE_MODE_UNDEF) {
491462306a36Sopenharmony_ci		if (nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) {
491562306a36Sopenharmony_ci			nla_nest_cancel(skb, br_afspec);
491662306a36Sopenharmony_ci			goto nla_put_failure;
491762306a36Sopenharmony_ci		}
491862306a36Sopenharmony_ci	}
491962306a36Sopenharmony_ci	if (vlan_fill) {
492062306a36Sopenharmony_ci		err = vlan_fill(skb, dev, filter_mask);
492162306a36Sopenharmony_ci		if (err) {
492262306a36Sopenharmony_ci			nla_nest_cancel(skb, br_afspec);
492362306a36Sopenharmony_ci			goto nla_put_failure;
492462306a36Sopenharmony_ci		}
492562306a36Sopenharmony_ci	}
492662306a36Sopenharmony_ci	nla_nest_end(skb, br_afspec);
492762306a36Sopenharmony_ci
492862306a36Sopenharmony_ci	protinfo = nla_nest_start(skb, IFLA_PROTINFO);
492962306a36Sopenharmony_ci	if (!protinfo)
493062306a36Sopenharmony_ci		goto nla_put_failure;
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci	if (brport_nla_put_flag(skb, flags, mask,
493362306a36Sopenharmony_ci				IFLA_BRPORT_MODE, BR_HAIRPIN_MODE) ||
493462306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
493562306a36Sopenharmony_ci				IFLA_BRPORT_GUARD, BR_BPDU_GUARD) ||
493662306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
493762306a36Sopenharmony_ci				IFLA_BRPORT_FAST_LEAVE,
493862306a36Sopenharmony_ci				BR_MULTICAST_FAST_LEAVE) ||
493962306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
494062306a36Sopenharmony_ci				IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK) ||
494162306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
494262306a36Sopenharmony_ci				IFLA_BRPORT_LEARNING, BR_LEARNING) ||
494362306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
494462306a36Sopenharmony_ci				IFLA_BRPORT_LEARNING_SYNC, BR_LEARNING_SYNC) ||
494562306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
494662306a36Sopenharmony_ci				IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD) ||
494762306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
494862306a36Sopenharmony_ci				IFLA_BRPORT_PROXYARP, BR_PROXYARP) ||
494962306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
495062306a36Sopenharmony_ci				IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD) ||
495162306a36Sopenharmony_ci	    brport_nla_put_flag(skb, flags, mask,
495262306a36Sopenharmony_ci				IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD)) {
495362306a36Sopenharmony_ci		nla_nest_cancel(skb, protinfo);
495462306a36Sopenharmony_ci		goto nla_put_failure;
495562306a36Sopenharmony_ci	}
495662306a36Sopenharmony_ci
495762306a36Sopenharmony_ci	nla_nest_end(skb, protinfo);
495862306a36Sopenharmony_ci
495962306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
496062306a36Sopenharmony_ci	return 0;
496162306a36Sopenharmony_cinla_put_failure:
496262306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
496362306a36Sopenharmony_ci	return err ? err : -EMSGSIZE;
496462306a36Sopenharmony_ci}
496562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ndo_dflt_bridge_getlink);
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_cistatic int valid_bridge_getlink_req(const struct nlmsghdr *nlh,
496862306a36Sopenharmony_ci				    bool strict_check, u32 *filter_mask,
496962306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
497062306a36Sopenharmony_ci{
497162306a36Sopenharmony_ci	struct nlattr *tb[IFLA_MAX+1];
497262306a36Sopenharmony_ci	int err, i;
497362306a36Sopenharmony_ci
497462306a36Sopenharmony_ci	if (strict_check) {
497562306a36Sopenharmony_ci		struct ifinfomsg *ifm;
497662306a36Sopenharmony_ci
497762306a36Sopenharmony_ci		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
497862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid header for bridge link dump");
497962306a36Sopenharmony_ci			return -EINVAL;
498062306a36Sopenharmony_ci		}
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_ci		ifm = nlmsg_data(nlh);
498362306a36Sopenharmony_ci		if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
498462306a36Sopenharmony_ci		    ifm->ifi_change || ifm->ifi_index) {
498562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid values in header for bridge link dump request");
498662306a36Sopenharmony_ci			return -EINVAL;
498762306a36Sopenharmony_ci		}
498862306a36Sopenharmony_ci
498962306a36Sopenharmony_ci		err = nlmsg_parse_deprecated_strict(nlh,
499062306a36Sopenharmony_ci						    sizeof(struct ifinfomsg),
499162306a36Sopenharmony_ci						    tb, IFLA_MAX, ifla_policy,
499262306a36Sopenharmony_ci						    extack);
499362306a36Sopenharmony_ci	} else {
499462306a36Sopenharmony_ci		err = nlmsg_parse_deprecated(nlh, sizeof(struct ifinfomsg),
499562306a36Sopenharmony_ci					     tb, IFLA_MAX, ifla_policy,
499662306a36Sopenharmony_ci					     extack);
499762306a36Sopenharmony_ci	}
499862306a36Sopenharmony_ci	if (err < 0)
499962306a36Sopenharmony_ci		return err;
500062306a36Sopenharmony_ci
500162306a36Sopenharmony_ci	/* new attributes should only be added with strict checking */
500262306a36Sopenharmony_ci	for (i = 0; i <= IFLA_MAX; ++i) {
500362306a36Sopenharmony_ci		if (!tb[i])
500462306a36Sopenharmony_ci			continue;
500562306a36Sopenharmony_ci
500662306a36Sopenharmony_ci		switch (i) {
500762306a36Sopenharmony_ci		case IFLA_EXT_MASK:
500862306a36Sopenharmony_ci			*filter_mask = nla_get_u32(tb[i]);
500962306a36Sopenharmony_ci			break;
501062306a36Sopenharmony_ci		default:
501162306a36Sopenharmony_ci			if (strict_check) {
501262306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Unsupported attribute in bridge link dump request");
501362306a36Sopenharmony_ci				return -EINVAL;
501462306a36Sopenharmony_ci			}
501562306a36Sopenharmony_ci		}
501662306a36Sopenharmony_ci	}
501762306a36Sopenharmony_ci
501862306a36Sopenharmony_ci	return 0;
501962306a36Sopenharmony_ci}
502062306a36Sopenharmony_ci
502162306a36Sopenharmony_cistatic int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
502262306a36Sopenharmony_ci{
502362306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
502462306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
502562306a36Sopenharmony_ci	struct net_device *dev;
502662306a36Sopenharmony_ci	int idx = 0;
502762306a36Sopenharmony_ci	u32 portid = NETLINK_CB(cb->skb).portid;
502862306a36Sopenharmony_ci	u32 seq = nlh->nlmsg_seq;
502962306a36Sopenharmony_ci	u32 filter_mask = 0;
503062306a36Sopenharmony_ci	int err;
503162306a36Sopenharmony_ci
503262306a36Sopenharmony_ci	err = valid_bridge_getlink_req(nlh, cb->strict_check, &filter_mask,
503362306a36Sopenharmony_ci				       cb->extack);
503462306a36Sopenharmony_ci	if (err < 0 && cb->strict_check)
503562306a36Sopenharmony_ci		return err;
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci	rcu_read_lock();
503862306a36Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
503962306a36Sopenharmony_ci		const struct net_device_ops *ops = dev->netdev_ops;
504062306a36Sopenharmony_ci		struct net_device *br_dev = netdev_master_upper_dev_get(dev);
504162306a36Sopenharmony_ci
504262306a36Sopenharmony_ci		if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
504362306a36Sopenharmony_ci			if (idx >= cb->args[0]) {
504462306a36Sopenharmony_ci				err = br_dev->netdev_ops->ndo_bridge_getlink(
504562306a36Sopenharmony_ci						skb, portid, seq, dev,
504662306a36Sopenharmony_ci						filter_mask, NLM_F_MULTI);
504762306a36Sopenharmony_ci				if (err < 0 && err != -EOPNOTSUPP) {
504862306a36Sopenharmony_ci					if (likely(skb->len))
504962306a36Sopenharmony_ci						break;
505062306a36Sopenharmony_ci
505162306a36Sopenharmony_ci					goto out_err;
505262306a36Sopenharmony_ci				}
505362306a36Sopenharmony_ci			}
505462306a36Sopenharmony_ci			idx++;
505562306a36Sopenharmony_ci		}
505662306a36Sopenharmony_ci
505762306a36Sopenharmony_ci		if (ops->ndo_bridge_getlink) {
505862306a36Sopenharmony_ci			if (idx >= cb->args[0]) {
505962306a36Sopenharmony_ci				err = ops->ndo_bridge_getlink(skb, portid,
506062306a36Sopenharmony_ci							      seq, dev,
506162306a36Sopenharmony_ci							      filter_mask,
506262306a36Sopenharmony_ci							      NLM_F_MULTI);
506362306a36Sopenharmony_ci				if (err < 0 && err != -EOPNOTSUPP) {
506462306a36Sopenharmony_ci					if (likely(skb->len))
506562306a36Sopenharmony_ci						break;
506662306a36Sopenharmony_ci
506762306a36Sopenharmony_ci					goto out_err;
506862306a36Sopenharmony_ci				}
506962306a36Sopenharmony_ci			}
507062306a36Sopenharmony_ci			idx++;
507162306a36Sopenharmony_ci		}
507262306a36Sopenharmony_ci	}
507362306a36Sopenharmony_ci	err = skb->len;
507462306a36Sopenharmony_ciout_err:
507562306a36Sopenharmony_ci	rcu_read_unlock();
507662306a36Sopenharmony_ci	cb->args[0] = idx;
507762306a36Sopenharmony_ci
507862306a36Sopenharmony_ci	return err;
507962306a36Sopenharmony_ci}
508062306a36Sopenharmony_ci
508162306a36Sopenharmony_cistatic inline size_t bridge_nlmsg_size(void)
508262306a36Sopenharmony_ci{
508362306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifinfomsg))
508462306a36Sopenharmony_ci		+ nla_total_size(IFNAMSIZ)	/* IFLA_IFNAME */
508562306a36Sopenharmony_ci		+ nla_total_size(MAX_ADDR_LEN)	/* IFLA_ADDRESS */
508662306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_MASTER */
508762306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_MTU */
508862306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_LINK */
508962306a36Sopenharmony_ci		+ nla_total_size(sizeof(u32))	/* IFLA_OPERSTATE */
509062306a36Sopenharmony_ci		+ nla_total_size(sizeof(u8))	/* IFLA_PROTINFO */
509162306a36Sopenharmony_ci		+ nla_total_size(sizeof(struct nlattr))	/* IFLA_AF_SPEC */
509262306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16))	/* IFLA_BRIDGE_FLAGS */
509362306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16));	/* IFLA_BRIDGE_MODE */
509462306a36Sopenharmony_ci}
509562306a36Sopenharmony_ci
509662306a36Sopenharmony_cistatic int rtnl_bridge_notify(struct net_device *dev)
509762306a36Sopenharmony_ci{
509862306a36Sopenharmony_ci	struct net *net = dev_net(dev);
509962306a36Sopenharmony_ci	struct sk_buff *skb;
510062306a36Sopenharmony_ci	int err = -EOPNOTSUPP;
510162306a36Sopenharmony_ci
510262306a36Sopenharmony_ci	if (!dev->netdev_ops->ndo_bridge_getlink)
510362306a36Sopenharmony_ci		return 0;
510462306a36Sopenharmony_ci
510562306a36Sopenharmony_ci	skb = nlmsg_new(bridge_nlmsg_size(), GFP_ATOMIC);
510662306a36Sopenharmony_ci	if (!skb) {
510762306a36Sopenharmony_ci		err = -ENOMEM;
510862306a36Sopenharmony_ci		goto errout;
510962306a36Sopenharmony_ci	}
511062306a36Sopenharmony_ci
511162306a36Sopenharmony_ci	err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev, 0, 0);
511262306a36Sopenharmony_ci	if (err < 0)
511362306a36Sopenharmony_ci		goto errout;
511462306a36Sopenharmony_ci
511562306a36Sopenharmony_ci	/* Notification info is only filled for bridge ports, not the bridge
511662306a36Sopenharmony_ci	 * device itself. Therefore, a zero notification length is valid and
511762306a36Sopenharmony_ci	 * should not result in an error.
511862306a36Sopenharmony_ci	 */
511962306a36Sopenharmony_ci	if (!skb->len)
512062306a36Sopenharmony_ci		goto errout;
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
512362306a36Sopenharmony_ci	return 0;
512462306a36Sopenharmony_cierrout:
512562306a36Sopenharmony_ci	WARN_ON(err == -EMSGSIZE);
512662306a36Sopenharmony_ci	kfree_skb(skb);
512762306a36Sopenharmony_ci	if (err)
512862306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_LINK, err);
512962306a36Sopenharmony_ci	return err;
513062306a36Sopenharmony_ci}
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_cistatic int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
513362306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
513462306a36Sopenharmony_ci{
513562306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
513662306a36Sopenharmony_ci	struct ifinfomsg *ifm;
513762306a36Sopenharmony_ci	struct net_device *dev;
513862306a36Sopenharmony_ci	struct nlattr *br_spec, *attr, *br_flags_attr = NULL;
513962306a36Sopenharmony_ci	int rem, err = -EOPNOTSUPP;
514062306a36Sopenharmony_ci	u16 flags = 0;
514162306a36Sopenharmony_ci
514262306a36Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(*ifm))
514362306a36Sopenharmony_ci		return -EINVAL;
514462306a36Sopenharmony_ci
514562306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
514662306a36Sopenharmony_ci	if (ifm->ifi_family != AF_BRIDGE)
514762306a36Sopenharmony_ci		return -EPFNOSUPPORT;
514862306a36Sopenharmony_ci
514962306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ifm->ifi_index);
515062306a36Sopenharmony_ci	if (!dev) {
515162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "unknown ifindex");
515262306a36Sopenharmony_ci		return -ENODEV;
515362306a36Sopenharmony_ci	}
515462306a36Sopenharmony_ci
515562306a36Sopenharmony_ci	br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
515662306a36Sopenharmony_ci	if (br_spec) {
515762306a36Sopenharmony_ci		nla_for_each_nested(attr, br_spec, rem) {
515862306a36Sopenharmony_ci			if (nla_type(attr) == IFLA_BRIDGE_FLAGS && !br_flags_attr) {
515962306a36Sopenharmony_ci				if (nla_len(attr) < sizeof(flags))
516062306a36Sopenharmony_ci					return -EINVAL;
516162306a36Sopenharmony_ci
516262306a36Sopenharmony_ci				br_flags_attr = attr;
516362306a36Sopenharmony_ci				flags = nla_get_u16(attr);
516462306a36Sopenharmony_ci			}
516562306a36Sopenharmony_ci
516662306a36Sopenharmony_ci			if (nla_type(attr) == IFLA_BRIDGE_MODE) {
516762306a36Sopenharmony_ci				if (nla_len(attr) < sizeof(u16))
516862306a36Sopenharmony_ci					return -EINVAL;
516962306a36Sopenharmony_ci			}
517062306a36Sopenharmony_ci		}
517162306a36Sopenharmony_ci	}
517262306a36Sopenharmony_ci
517362306a36Sopenharmony_ci	if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
517462306a36Sopenharmony_ci		struct net_device *br_dev = netdev_master_upper_dev_get(dev);
517562306a36Sopenharmony_ci
517662306a36Sopenharmony_ci		if (!br_dev || !br_dev->netdev_ops->ndo_bridge_setlink) {
517762306a36Sopenharmony_ci			err = -EOPNOTSUPP;
517862306a36Sopenharmony_ci			goto out;
517962306a36Sopenharmony_ci		}
518062306a36Sopenharmony_ci
518162306a36Sopenharmony_ci		err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh, flags,
518262306a36Sopenharmony_ci							     extack);
518362306a36Sopenharmony_ci		if (err)
518462306a36Sopenharmony_ci			goto out;
518562306a36Sopenharmony_ci
518662306a36Sopenharmony_ci		flags &= ~BRIDGE_FLAGS_MASTER;
518762306a36Sopenharmony_ci	}
518862306a36Sopenharmony_ci
518962306a36Sopenharmony_ci	if ((flags & BRIDGE_FLAGS_SELF)) {
519062306a36Sopenharmony_ci		if (!dev->netdev_ops->ndo_bridge_setlink)
519162306a36Sopenharmony_ci			err = -EOPNOTSUPP;
519262306a36Sopenharmony_ci		else
519362306a36Sopenharmony_ci			err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh,
519462306a36Sopenharmony_ci								  flags,
519562306a36Sopenharmony_ci								  extack);
519662306a36Sopenharmony_ci		if (!err) {
519762306a36Sopenharmony_ci			flags &= ~BRIDGE_FLAGS_SELF;
519862306a36Sopenharmony_ci
519962306a36Sopenharmony_ci			/* Generate event to notify upper layer of bridge
520062306a36Sopenharmony_ci			 * change
520162306a36Sopenharmony_ci			 */
520262306a36Sopenharmony_ci			err = rtnl_bridge_notify(dev);
520362306a36Sopenharmony_ci		}
520462306a36Sopenharmony_ci	}
520562306a36Sopenharmony_ci
520662306a36Sopenharmony_ci	if (br_flags_attr)
520762306a36Sopenharmony_ci		memcpy(nla_data(br_flags_attr), &flags, sizeof(flags));
520862306a36Sopenharmony_ciout:
520962306a36Sopenharmony_ci	return err;
521062306a36Sopenharmony_ci}
521162306a36Sopenharmony_ci
521262306a36Sopenharmony_cistatic int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
521362306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
521462306a36Sopenharmony_ci{
521562306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
521662306a36Sopenharmony_ci	struct ifinfomsg *ifm;
521762306a36Sopenharmony_ci	struct net_device *dev;
521862306a36Sopenharmony_ci	struct nlattr *br_spec, *attr = NULL;
521962306a36Sopenharmony_ci	int rem, err = -EOPNOTSUPP;
522062306a36Sopenharmony_ci	u16 flags = 0;
522162306a36Sopenharmony_ci	bool have_flags = false;
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(*ifm))
522462306a36Sopenharmony_ci		return -EINVAL;
522562306a36Sopenharmony_ci
522662306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
522762306a36Sopenharmony_ci	if (ifm->ifi_family != AF_BRIDGE)
522862306a36Sopenharmony_ci		return -EPFNOSUPPORT;
522962306a36Sopenharmony_ci
523062306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ifm->ifi_index);
523162306a36Sopenharmony_ci	if (!dev) {
523262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "unknown ifindex");
523362306a36Sopenharmony_ci		return -ENODEV;
523462306a36Sopenharmony_ci	}
523562306a36Sopenharmony_ci
523662306a36Sopenharmony_ci	br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
523762306a36Sopenharmony_ci	if (br_spec) {
523862306a36Sopenharmony_ci		nla_for_each_nested(attr, br_spec, rem) {
523962306a36Sopenharmony_ci			if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
524062306a36Sopenharmony_ci				if (nla_len(attr) < sizeof(flags))
524162306a36Sopenharmony_ci					return -EINVAL;
524262306a36Sopenharmony_ci
524362306a36Sopenharmony_ci				have_flags = true;
524462306a36Sopenharmony_ci				flags = nla_get_u16(attr);
524562306a36Sopenharmony_ci				break;
524662306a36Sopenharmony_ci			}
524762306a36Sopenharmony_ci		}
524862306a36Sopenharmony_ci	}
524962306a36Sopenharmony_ci
525062306a36Sopenharmony_ci	if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
525162306a36Sopenharmony_ci		struct net_device *br_dev = netdev_master_upper_dev_get(dev);
525262306a36Sopenharmony_ci
525362306a36Sopenharmony_ci		if (!br_dev || !br_dev->netdev_ops->ndo_bridge_dellink) {
525462306a36Sopenharmony_ci			err = -EOPNOTSUPP;
525562306a36Sopenharmony_ci			goto out;
525662306a36Sopenharmony_ci		}
525762306a36Sopenharmony_ci
525862306a36Sopenharmony_ci		err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh, flags);
525962306a36Sopenharmony_ci		if (err)
526062306a36Sopenharmony_ci			goto out;
526162306a36Sopenharmony_ci
526262306a36Sopenharmony_ci		flags &= ~BRIDGE_FLAGS_MASTER;
526362306a36Sopenharmony_ci	}
526462306a36Sopenharmony_ci
526562306a36Sopenharmony_ci	if ((flags & BRIDGE_FLAGS_SELF)) {
526662306a36Sopenharmony_ci		if (!dev->netdev_ops->ndo_bridge_dellink)
526762306a36Sopenharmony_ci			err = -EOPNOTSUPP;
526862306a36Sopenharmony_ci		else
526962306a36Sopenharmony_ci			err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh,
527062306a36Sopenharmony_ci								  flags);
527162306a36Sopenharmony_ci
527262306a36Sopenharmony_ci		if (!err) {
527362306a36Sopenharmony_ci			flags &= ~BRIDGE_FLAGS_SELF;
527462306a36Sopenharmony_ci
527562306a36Sopenharmony_ci			/* Generate event to notify upper layer of bridge
527662306a36Sopenharmony_ci			 * change
527762306a36Sopenharmony_ci			 */
527862306a36Sopenharmony_ci			err = rtnl_bridge_notify(dev);
527962306a36Sopenharmony_ci		}
528062306a36Sopenharmony_ci	}
528162306a36Sopenharmony_ci
528262306a36Sopenharmony_ci	if (have_flags)
528362306a36Sopenharmony_ci		memcpy(nla_data(attr), &flags, sizeof(flags));
528462306a36Sopenharmony_ciout:
528562306a36Sopenharmony_ci	return err;
528662306a36Sopenharmony_ci}
528762306a36Sopenharmony_ci
528862306a36Sopenharmony_cistatic bool stats_attr_valid(unsigned int mask, int attrid, int idxattr)
528962306a36Sopenharmony_ci{
529062306a36Sopenharmony_ci	return (mask & IFLA_STATS_FILTER_BIT(attrid)) &&
529162306a36Sopenharmony_ci	       (!idxattr || idxattr == attrid);
529262306a36Sopenharmony_ci}
529362306a36Sopenharmony_ci
529462306a36Sopenharmony_cistatic bool
529562306a36Sopenharmony_cirtnl_offload_xstats_have_ndo(const struct net_device *dev, int attr_id)
529662306a36Sopenharmony_ci{
529762306a36Sopenharmony_ci	return dev->netdev_ops &&
529862306a36Sopenharmony_ci	       dev->netdev_ops->ndo_has_offload_stats &&
529962306a36Sopenharmony_ci	       dev->netdev_ops->ndo_get_offload_stats &&
530062306a36Sopenharmony_ci	       dev->netdev_ops->ndo_has_offload_stats(dev, attr_id);
530162306a36Sopenharmony_ci}
530262306a36Sopenharmony_ci
530362306a36Sopenharmony_cistatic unsigned int
530462306a36Sopenharmony_cirtnl_offload_xstats_get_size_ndo(const struct net_device *dev, int attr_id)
530562306a36Sopenharmony_ci{
530662306a36Sopenharmony_ci	return rtnl_offload_xstats_have_ndo(dev, attr_id) ?
530762306a36Sopenharmony_ci	       sizeof(struct rtnl_link_stats64) : 0;
530862306a36Sopenharmony_ci}
530962306a36Sopenharmony_ci
531062306a36Sopenharmony_cistatic int
531162306a36Sopenharmony_cirtnl_offload_xstats_fill_ndo(struct net_device *dev, int attr_id,
531262306a36Sopenharmony_ci			     struct sk_buff *skb)
531362306a36Sopenharmony_ci{
531462306a36Sopenharmony_ci	unsigned int size = rtnl_offload_xstats_get_size_ndo(dev, attr_id);
531562306a36Sopenharmony_ci	struct nlattr *attr = NULL;
531662306a36Sopenharmony_ci	void *attr_data;
531762306a36Sopenharmony_ci	int err;
531862306a36Sopenharmony_ci
531962306a36Sopenharmony_ci	if (!size)
532062306a36Sopenharmony_ci		return -ENODATA;
532162306a36Sopenharmony_ci
532262306a36Sopenharmony_ci	attr = nla_reserve_64bit(skb, attr_id, size,
532362306a36Sopenharmony_ci				 IFLA_OFFLOAD_XSTATS_UNSPEC);
532462306a36Sopenharmony_ci	if (!attr)
532562306a36Sopenharmony_ci		return -EMSGSIZE;
532662306a36Sopenharmony_ci
532762306a36Sopenharmony_ci	attr_data = nla_data(attr);
532862306a36Sopenharmony_ci	memset(attr_data, 0, size);
532962306a36Sopenharmony_ci
533062306a36Sopenharmony_ci	err = dev->netdev_ops->ndo_get_offload_stats(attr_id, dev, attr_data);
533162306a36Sopenharmony_ci	if (err)
533262306a36Sopenharmony_ci		return err;
533362306a36Sopenharmony_ci
533462306a36Sopenharmony_ci	return 0;
533562306a36Sopenharmony_ci}
533662306a36Sopenharmony_ci
533762306a36Sopenharmony_cistatic unsigned int
533862306a36Sopenharmony_cirtnl_offload_xstats_get_size_stats(const struct net_device *dev,
533962306a36Sopenharmony_ci				   enum netdev_offload_xstats_type type)
534062306a36Sopenharmony_ci{
534162306a36Sopenharmony_ci	bool enabled = netdev_offload_xstats_enabled(dev, type);
534262306a36Sopenharmony_ci
534362306a36Sopenharmony_ci	return enabled ? sizeof(struct rtnl_hw_stats64) : 0;
534462306a36Sopenharmony_ci}
534562306a36Sopenharmony_ci
534662306a36Sopenharmony_cistruct rtnl_offload_xstats_request_used {
534762306a36Sopenharmony_ci	bool request;
534862306a36Sopenharmony_ci	bool used;
534962306a36Sopenharmony_ci};
535062306a36Sopenharmony_ci
535162306a36Sopenharmony_cistatic int
535262306a36Sopenharmony_cirtnl_offload_xstats_get_stats(struct net_device *dev,
535362306a36Sopenharmony_ci			      enum netdev_offload_xstats_type type,
535462306a36Sopenharmony_ci			      struct rtnl_offload_xstats_request_used *ru,
535562306a36Sopenharmony_ci			      struct rtnl_hw_stats64 *stats,
535662306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
535762306a36Sopenharmony_ci{
535862306a36Sopenharmony_ci	bool request;
535962306a36Sopenharmony_ci	bool used;
536062306a36Sopenharmony_ci	int err;
536162306a36Sopenharmony_ci
536262306a36Sopenharmony_ci	request = netdev_offload_xstats_enabled(dev, type);
536362306a36Sopenharmony_ci	if (!request) {
536462306a36Sopenharmony_ci		used = false;
536562306a36Sopenharmony_ci		goto out;
536662306a36Sopenharmony_ci	}
536762306a36Sopenharmony_ci
536862306a36Sopenharmony_ci	err = netdev_offload_xstats_get(dev, type, stats, &used, extack);
536962306a36Sopenharmony_ci	if (err)
537062306a36Sopenharmony_ci		return err;
537162306a36Sopenharmony_ci
537262306a36Sopenharmony_ciout:
537362306a36Sopenharmony_ci	if (ru) {
537462306a36Sopenharmony_ci		ru->request = request;
537562306a36Sopenharmony_ci		ru->used = used;
537662306a36Sopenharmony_ci	}
537762306a36Sopenharmony_ci	return 0;
537862306a36Sopenharmony_ci}
537962306a36Sopenharmony_ci
538062306a36Sopenharmony_cistatic int
538162306a36Sopenharmony_cirtnl_offload_xstats_fill_hw_s_info_one(struct sk_buff *skb, int attr_id,
538262306a36Sopenharmony_ci				       struct rtnl_offload_xstats_request_used *ru)
538362306a36Sopenharmony_ci{
538462306a36Sopenharmony_ci	struct nlattr *nest;
538562306a36Sopenharmony_ci
538662306a36Sopenharmony_ci	nest = nla_nest_start(skb, attr_id);
538762306a36Sopenharmony_ci	if (!nest)
538862306a36Sopenharmony_ci		return -EMSGSIZE;
538962306a36Sopenharmony_ci
539062306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST, ru->request))
539162306a36Sopenharmony_ci		goto nla_put_failure;
539262306a36Sopenharmony_ci
539362306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED, ru->used))
539462306a36Sopenharmony_ci		goto nla_put_failure;
539562306a36Sopenharmony_ci
539662306a36Sopenharmony_ci	nla_nest_end(skb, nest);
539762306a36Sopenharmony_ci	return 0;
539862306a36Sopenharmony_ci
539962306a36Sopenharmony_cinla_put_failure:
540062306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
540162306a36Sopenharmony_ci	return -EMSGSIZE;
540262306a36Sopenharmony_ci}
540362306a36Sopenharmony_ci
540462306a36Sopenharmony_cistatic int
540562306a36Sopenharmony_cirtnl_offload_xstats_fill_hw_s_info(struct sk_buff *skb, struct net_device *dev,
540662306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
540762306a36Sopenharmony_ci{
540862306a36Sopenharmony_ci	enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
540962306a36Sopenharmony_ci	struct rtnl_offload_xstats_request_used ru_l3;
541062306a36Sopenharmony_ci	struct nlattr *nest;
541162306a36Sopenharmony_ci	int err;
541262306a36Sopenharmony_ci
541362306a36Sopenharmony_ci	err = rtnl_offload_xstats_get_stats(dev, t_l3, &ru_l3, NULL, extack);
541462306a36Sopenharmony_ci	if (err)
541562306a36Sopenharmony_ci		return err;
541662306a36Sopenharmony_ci
541762306a36Sopenharmony_ci	nest = nla_nest_start(skb, IFLA_OFFLOAD_XSTATS_HW_S_INFO);
541862306a36Sopenharmony_ci	if (!nest)
541962306a36Sopenharmony_ci		return -EMSGSIZE;
542062306a36Sopenharmony_ci
542162306a36Sopenharmony_ci	if (rtnl_offload_xstats_fill_hw_s_info_one(skb,
542262306a36Sopenharmony_ci						   IFLA_OFFLOAD_XSTATS_L3_STATS,
542362306a36Sopenharmony_ci						   &ru_l3))
542462306a36Sopenharmony_ci		goto nla_put_failure;
542562306a36Sopenharmony_ci
542662306a36Sopenharmony_ci	nla_nest_end(skb, nest);
542762306a36Sopenharmony_ci	return 0;
542862306a36Sopenharmony_ci
542962306a36Sopenharmony_cinla_put_failure:
543062306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
543162306a36Sopenharmony_ci	return -EMSGSIZE;
543262306a36Sopenharmony_ci}
543362306a36Sopenharmony_ci
543462306a36Sopenharmony_cistatic int rtnl_offload_xstats_fill(struct sk_buff *skb, struct net_device *dev,
543562306a36Sopenharmony_ci				    int *prividx, u32 off_filter_mask,
543662306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
543762306a36Sopenharmony_ci{
543862306a36Sopenharmony_ci	enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
543962306a36Sopenharmony_ci	int attr_id_hw_s_info = IFLA_OFFLOAD_XSTATS_HW_S_INFO;
544062306a36Sopenharmony_ci	int attr_id_l3_stats = IFLA_OFFLOAD_XSTATS_L3_STATS;
544162306a36Sopenharmony_ci	int attr_id_cpu_hit = IFLA_OFFLOAD_XSTATS_CPU_HIT;
544262306a36Sopenharmony_ci	bool have_data = false;
544362306a36Sopenharmony_ci	int err;
544462306a36Sopenharmony_ci
544562306a36Sopenharmony_ci	if (*prividx <= attr_id_cpu_hit &&
544662306a36Sopenharmony_ci	    (off_filter_mask &
544762306a36Sopenharmony_ci	     IFLA_STATS_FILTER_BIT(attr_id_cpu_hit))) {
544862306a36Sopenharmony_ci		err = rtnl_offload_xstats_fill_ndo(dev, attr_id_cpu_hit, skb);
544962306a36Sopenharmony_ci		if (!err) {
545062306a36Sopenharmony_ci			have_data = true;
545162306a36Sopenharmony_ci		} else if (err != -ENODATA) {
545262306a36Sopenharmony_ci			*prividx = attr_id_cpu_hit;
545362306a36Sopenharmony_ci			return err;
545462306a36Sopenharmony_ci		}
545562306a36Sopenharmony_ci	}
545662306a36Sopenharmony_ci
545762306a36Sopenharmony_ci	if (*prividx <= attr_id_hw_s_info &&
545862306a36Sopenharmony_ci	    (off_filter_mask & IFLA_STATS_FILTER_BIT(attr_id_hw_s_info))) {
545962306a36Sopenharmony_ci		*prividx = attr_id_hw_s_info;
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci		err = rtnl_offload_xstats_fill_hw_s_info(skb, dev, extack);
546262306a36Sopenharmony_ci		if (err)
546362306a36Sopenharmony_ci			return err;
546462306a36Sopenharmony_ci
546562306a36Sopenharmony_ci		have_data = true;
546662306a36Sopenharmony_ci		*prividx = 0;
546762306a36Sopenharmony_ci	}
546862306a36Sopenharmony_ci
546962306a36Sopenharmony_ci	if (*prividx <= attr_id_l3_stats &&
547062306a36Sopenharmony_ci	    (off_filter_mask & IFLA_STATS_FILTER_BIT(attr_id_l3_stats))) {
547162306a36Sopenharmony_ci		unsigned int size_l3;
547262306a36Sopenharmony_ci		struct nlattr *attr;
547362306a36Sopenharmony_ci
547462306a36Sopenharmony_ci		*prividx = attr_id_l3_stats;
547562306a36Sopenharmony_ci
547662306a36Sopenharmony_ci		size_l3 = rtnl_offload_xstats_get_size_stats(dev, t_l3);
547762306a36Sopenharmony_ci		if (!size_l3)
547862306a36Sopenharmony_ci			goto skip_l3_stats;
547962306a36Sopenharmony_ci		attr = nla_reserve_64bit(skb, attr_id_l3_stats, size_l3,
548062306a36Sopenharmony_ci					 IFLA_OFFLOAD_XSTATS_UNSPEC);
548162306a36Sopenharmony_ci		if (!attr)
548262306a36Sopenharmony_ci			return -EMSGSIZE;
548362306a36Sopenharmony_ci
548462306a36Sopenharmony_ci		err = rtnl_offload_xstats_get_stats(dev, t_l3, NULL,
548562306a36Sopenharmony_ci						    nla_data(attr), extack);
548662306a36Sopenharmony_ci		if (err)
548762306a36Sopenharmony_ci			return err;
548862306a36Sopenharmony_ci
548962306a36Sopenharmony_ci		have_data = true;
549062306a36Sopenharmony_ciskip_l3_stats:
549162306a36Sopenharmony_ci		*prividx = 0;
549262306a36Sopenharmony_ci	}
549362306a36Sopenharmony_ci
549462306a36Sopenharmony_ci	if (!have_data)
549562306a36Sopenharmony_ci		return -ENODATA;
549662306a36Sopenharmony_ci
549762306a36Sopenharmony_ci	*prividx = 0;
549862306a36Sopenharmony_ci	return 0;
549962306a36Sopenharmony_ci}
550062306a36Sopenharmony_ci
550162306a36Sopenharmony_cistatic unsigned int
550262306a36Sopenharmony_cirtnl_offload_xstats_get_size_hw_s_info_one(const struct net_device *dev,
550362306a36Sopenharmony_ci					   enum netdev_offload_xstats_type type)
550462306a36Sopenharmony_ci{
550562306a36Sopenharmony_ci	return nla_total_size(0) +
550662306a36Sopenharmony_ci		/* IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST */
550762306a36Sopenharmony_ci		nla_total_size(sizeof(u8)) +
550862306a36Sopenharmony_ci		/* IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED */
550962306a36Sopenharmony_ci		nla_total_size(sizeof(u8)) +
551062306a36Sopenharmony_ci		0;
551162306a36Sopenharmony_ci}
551262306a36Sopenharmony_ci
551362306a36Sopenharmony_cistatic unsigned int
551462306a36Sopenharmony_cirtnl_offload_xstats_get_size_hw_s_info(const struct net_device *dev)
551562306a36Sopenharmony_ci{
551662306a36Sopenharmony_ci	enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
551762306a36Sopenharmony_ci
551862306a36Sopenharmony_ci	return nla_total_size(0) +
551962306a36Sopenharmony_ci		/* IFLA_OFFLOAD_XSTATS_L3_STATS */
552062306a36Sopenharmony_ci		rtnl_offload_xstats_get_size_hw_s_info_one(dev, t_l3) +
552162306a36Sopenharmony_ci		0;
552262306a36Sopenharmony_ci}
552362306a36Sopenharmony_ci
552462306a36Sopenharmony_cistatic int rtnl_offload_xstats_get_size(const struct net_device *dev,
552562306a36Sopenharmony_ci					u32 off_filter_mask)
552662306a36Sopenharmony_ci{
552762306a36Sopenharmony_ci	enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
552862306a36Sopenharmony_ci	int attr_id_cpu_hit = IFLA_OFFLOAD_XSTATS_CPU_HIT;
552962306a36Sopenharmony_ci	int nla_size = 0;
553062306a36Sopenharmony_ci	int size;
553162306a36Sopenharmony_ci
553262306a36Sopenharmony_ci	if (off_filter_mask &
553362306a36Sopenharmony_ci	    IFLA_STATS_FILTER_BIT(attr_id_cpu_hit)) {
553462306a36Sopenharmony_ci		size = rtnl_offload_xstats_get_size_ndo(dev, attr_id_cpu_hit);
553562306a36Sopenharmony_ci		nla_size += nla_total_size_64bit(size);
553662306a36Sopenharmony_ci	}
553762306a36Sopenharmony_ci
553862306a36Sopenharmony_ci	if (off_filter_mask &
553962306a36Sopenharmony_ci	    IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO))
554062306a36Sopenharmony_ci		nla_size += rtnl_offload_xstats_get_size_hw_s_info(dev);
554162306a36Sopenharmony_ci
554262306a36Sopenharmony_ci	if (off_filter_mask &
554362306a36Sopenharmony_ci	    IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_L3_STATS)) {
554462306a36Sopenharmony_ci		size = rtnl_offload_xstats_get_size_stats(dev, t_l3);
554562306a36Sopenharmony_ci		nla_size += nla_total_size_64bit(size);
554662306a36Sopenharmony_ci	}
554762306a36Sopenharmony_ci
554862306a36Sopenharmony_ci	if (nla_size != 0)
554962306a36Sopenharmony_ci		nla_size += nla_total_size(0);
555062306a36Sopenharmony_ci
555162306a36Sopenharmony_ci	return nla_size;
555262306a36Sopenharmony_ci}
555362306a36Sopenharmony_ci
555462306a36Sopenharmony_cistruct rtnl_stats_dump_filters {
555562306a36Sopenharmony_ci	/* mask[0] filters outer attributes. Then individual nests have their
555662306a36Sopenharmony_ci	 * filtering mask at the index of the nested attribute.
555762306a36Sopenharmony_ci	 */
555862306a36Sopenharmony_ci	u32 mask[IFLA_STATS_MAX + 1];
555962306a36Sopenharmony_ci};
556062306a36Sopenharmony_ci
556162306a36Sopenharmony_cistatic int rtnl_fill_statsinfo(struct sk_buff *skb, struct net_device *dev,
556262306a36Sopenharmony_ci			       int type, u32 pid, u32 seq, u32 change,
556362306a36Sopenharmony_ci			       unsigned int flags,
556462306a36Sopenharmony_ci			       const struct rtnl_stats_dump_filters *filters,
556562306a36Sopenharmony_ci			       int *idxattr, int *prividx,
556662306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
556762306a36Sopenharmony_ci{
556862306a36Sopenharmony_ci	unsigned int filter_mask = filters->mask[0];
556962306a36Sopenharmony_ci	struct if_stats_msg *ifsm;
557062306a36Sopenharmony_ci	struct nlmsghdr *nlh;
557162306a36Sopenharmony_ci	struct nlattr *attr;
557262306a36Sopenharmony_ci	int s_prividx = *prividx;
557362306a36Sopenharmony_ci	int err;
557462306a36Sopenharmony_ci
557562306a36Sopenharmony_ci	ASSERT_RTNL();
557662306a36Sopenharmony_ci
557762306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifsm), flags);
557862306a36Sopenharmony_ci	if (!nlh)
557962306a36Sopenharmony_ci		return -EMSGSIZE;
558062306a36Sopenharmony_ci
558162306a36Sopenharmony_ci	ifsm = nlmsg_data(nlh);
558262306a36Sopenharmony_ci	ifsm->family = PF_UNSPEC;
558362306a36Sopenharmony_ci	ifsm->pad1 = 0;
558462306a36Sopenharmony_ci	ifsm->pad2 = 0;
558562306a36Sopenharmony_ci	ifsm->ifindex = dev->ifindex;
558662306a36Sopenharmony_ci	ifsm->filter_mask = filter_mask;
558762306a36Sopenharmony_ci
558862306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_64, *idxattr)) {
558962306a36Sopenharmony_ci		struct rtnl_link_stats64 *sp;
559062306a36Sopenharmony_ci
559162306a36Sopenharmony_ci		attr = nla_reserve_64bit(skb, IFLA_STATS_LINK_64,
559262306a36Sopenharmony_ci					 sizeof(struct rtnl_link_stats64),
559362306a36Sopenharmony_ci					 IFLA_STATS_UNSPEC);
559462306a36Sopenharmony_ci		if (!attr) {
559562306a36Sopenharmony_ci			err = -EMSGSIZE;
559662306a36Sopenharmony_ci			goto nla_put_failure;
559762306a36Sopenharmony_ci		}
559862306a36Sopenharmony_ci
559962306a36Sopenharmony_ci		sp = nla_data(attr);
560062306a36Sopenharmony_ci		dev_get_stats(dev, sp);
560162306a36Sopenharmony_ci	}
560262306a36Sopenharmony_ci
560362306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, *idxattr)) {
560462306a36Sopenharmony_ci		const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
560562306a36Sopenharmony_ci
560662306a36Sopenharmony_ci		if (ops && ops->fill_linkxstats) {
560762306a36Sopenharmony_ci			*idxattr = IFLA_STATS_LINK_XSTATS;
560862306a36Sopenharmony_ci			attr = nla_nest_start_noflag(skb,
560962306a36Sopenharmony_ci						     IFLA_STATS_LINK_XSTATS);
561062306a36Sopenharmony_ci			if (!attr) {
561162306a36Sopenharmony_ci				err = -EMSGSIZE;
561262306a36Sopenharmony_ci				goto nla_put_failure;
561362306a36Sopenharmony_ci			}
561462306a36Sopenharmony_ci
561562306a36Sopenharmony_ci			err = ops->fill_linkxstats(skb, dev, prividx, *idxattr);
561662306a36Sopenharmony_ci			nla_nest_end(skb, attr);
561762306a36Sopenharmony_ci			if (err)
561862306a36Sopenharmony_ci				goto nla_put_failure;
561962306a36Sopenharmony_ci			*idxattr = 0;
562062306a36Sopenharmony_ci		}
562162306a36Sopenharmony_ci	}
562262306a36Sopenharmony_ci
562362306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS_SLAVE,
562462306a36Sopenharmony_ci			     *idxattr)) {
562562306a36Sopenharmony_ci		const struct rtnl_link_ops *ops = NULL;
562662306a36Sopenharmony_ci		const struct net_device *master;
562762306a36Sopenharmony_ci
562862306a36Sopenharmony_ci		master = netdev_master_upper_dev_get(dev);
562962306a36Sopenharmony_ci		if (master)
563062306a36Sopenharmony_ci			ops = master->rtnl_link_ops;
563162306a36Sopenharmony_ci		if (ops && ops->fill_linkxstats) {
563262306a36Sopenharmony_ci			*idxattr = IFLA_STATS_LINK_XSTATS_SLAVE;
563362306a36Sopenharmony_ci			attr = nla_nest_start_noflag(skb,
563462306a36Sopenharmony_ci						     IFLA_STATS_LINK_XSTATS_SLAVE);
563562306a36Sopenharmony_ci			if (!attr) {
563662306a36Sopenharmony_ci				err = -EMSGSIZE;
563762306a36Sopenharmony_ci				goto nla_put_failure;
563862306a36Sopenharmony_ci			}
563962306a36Sopenharmony_ci
564062306a36Sopenharmony_ci			err = ops->fill_linkxstats(skb, dev, prividx, *idxattr);
564162306a36Sopenharmony_ci			nla_nest_end(skb, attr);
564262306a36Sopenharmony_ci			if (err)
564362306a36Sopenharmony_ci				goto nla_put_failure;
564462306a36Sopenharmony_ci			*idxattr = 0;
564562306a36Sopenharmony_ci		}
564662306a36Sopenharmony_ci	}
564762306a36Sopenharmony_ci
564862306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_OFFLOAD_XSTATS,
564962306a36Sopenharmony_ci			     *idxattr)) {
565062306a36Sopenharmony_ci		u32 off_filter_mask;
565162306a36Sopenharmony_ci
565262306a36Sopenharmony_ci		off_filter_mask = filters->mask[IFLA_STATS_LINK_OFFLOAD_XSTATS];
565362306a36Sopenharmony_ci		*idxattr = IFLA_STATS_LINK_OFFLOAD_XSTATS;
565462306a36Sopenharmony_ci		attr = nla_nest_start_noflag(skb,
565562306a36Sopenharmony_ci					     IFLA_STATS_LINK_OFFLOAD_XSTATS);
565662306a36Sopenharmony_ci		if (!attr) {
565762306a36Sopenharmony_ci			err = -EMSGSIZE;
565862306a36Sopenharmony_ci			goto nla_put_failure;
565962306a36Sopenharmony_ci		}
566062306a36Sopenharmony_ci
566162306a36Sopenharmony_ci		err = rtnl_offload_xstats_fill(skb, dev, prividx,
566262306a36Sopenharmony_ci					       off_filter_mask, extack);
566362306a36Sopenharmony_ci		if (err == -ENODATA)
566462306a36Sopenharmony_ci			nla_nest_cancel(skb, attr);
566562306a36Sopenharmony_ci		else
566662306a36Sopenharmony_ci			nla_nest_end(skb, attr);
566762306a36Sopenharmony_ci
566862306a36Sopenharmony_ci		if (err && err != -ENODATA)
566962306a36Sopenharmony_ci			goto nla_put_failure;
567062306a36Sopenharmony_ci		*idxattr = 0;
567162306a36Sopenharmony_ci	}
567262306a36Sopenharmony_ci
567362306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_AF_SPEC, *idxattr)) {
567462306a36Sopenharmony_ci		struct rtnl_af_ops *af_ops;
567562306a36Sopenharmony_ci
567662306a36Sopenharmony_ci		*idxattr = IFLA_STATS_AF_SPEC;
567762306a36Sopenharmony_ci		attr = nla_nest_start_noflag(skb, IFLA_STATS_AF_SPEC);
567862306a36Sopenharmony_ci		if (!attr) {
567962306a36Sopenharmony_ci			err = -EMSGSIZE;
568062306a36Sopenharmony_ci			goto nla_put_failure;
568162306a36Sopenharmony_ci		}
568262306a36Sopenharmony_ci
568362306a36Sopenharmony_ci		rcu_read_lock();
568462306a36Sopenharmony_ci		list_for_each_entry_rcu(af_ops, &rtnl_af_ops, list) {
568562306a36Sopenharmony_ci			if (af_ops->fill_stats_af) {
568662306a36Sopenharmony_ci				struct nlattr *af;
568762306a36Sopenharmony_ci
568862306a36Sopenharmony_ci				af = nla_nest_start_noflag(skb,
568962306a36Sopenharmony_ci							   af_ops->family);
569062306a36Sopenharmony_ci				if (!af) {
569162306a36Sopenharmony_ci					rcu_read_unlock();
569262306a36Sopenharmony_ci					err = -EMSGSIZE;
569362306a36Sopenharmony_ci					goto nla_put_failure;
569462306a36Sopenharmony_ci				}
569562306a36Sopenharmony_ci				err = af_ops->fill_stats_af(skb, dev);
569662306a36Sopenharmony_ci
569762306a36Sopenharmony_ci				if (err == -ENODATA) {
569862306a36Sopenharmony_ci					nla_nest_cancel(skb, af);
569962306a36Sopenharmony_ci				} else if (err < 0) {
570062306a36Sopenharmony_ci					rcu_read_unlock();
570162306a36Sopenharmony_ci					goto nla_put_failure;
570262306a36Sopenharmony_ci				}
570362306a36Sopenharmony_ci
570462306a36Sopenharmony_ci				nla_nest_end(skb, af);
570562306a36Sopenharmony_ci			}
570662306a36Sopenharmony_ci		}
570762306a36Sopenharmony_ci		rcu_read_unlock();
570862306a36Sopenharmony_ci
570962306a36Sopenharmony_ci		nla_nest_end(skb, attr);
571062306a36Sopenharmony_ci
571162306a36Sopenharmony_ci		*idxattr = 0;
571262306a36Sopenharmony_ci	}
571362306a36Sopenharmony_ci
571462306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
571562306a36Sopenharmony_ci
571662306a36Sopenharmony_ci	return 0;
571762306a36Sopenharmony_ci
571862306a36Sopenharmony_cinla_put_failure:
571962306a36Sopenharmony_ci	/* not a multi message or no progress mean a real error */
572062306a36Sopenharmony_ci	if (!(flags & NLM_F_MULTI) || s_prividx == *prividx)
572162306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
572262306a36Sopenharmony_ci	else
572362306a36Sopenharmony_ci		nlmsg_end(skb, nlh);
572462306a36Sopenharmony_ci
572562306a36Sopenharmony_ci	return err;
572662306a36Sopenharmony_ci}
572762306a36Sopenharmony_ci
572862306a36Sopenharmony_cistatic size_t if_nlmsg_stats_size(const struct net_device *dev,
572962306a36Sopenharmony_ci				  const struct rtnl_stats_dump_filters *filters)
573062306a36Sopenharmony_ci{
573162306a36Sopenharmony_ci	size_t size = NLMSG_ALIGN(sizeof(struct if_stats_msg));
573262306a36Sopenharmony_ci	unsigned int filter_mask = filters->mask[0];
573362306a36Sopenharmony_ci
573462306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_64, 0))
573562306a36Sopenharmony_ci		size += nla_total_size_64bit(sizeof(struct rtnl_link_stats64));
573662306a36Sopenharmony_ci
573762306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS, 0)) {
573862306a36Sopenharmony_ci		const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
573962306a36Sopenharmony_ci		int attr = IFLA_STATS_LINK_XSTATS;
574062306a36Sopenharmony_ci
574162306a36Sopenharmony_ci		if (ops && ops->get_linkxstats_size) {
574262306a36Sopenharmony_ci			size += nla_total_size(ops->get_linkxstats_size(dev,
574362306a36Sopenharmony_ci									attr));
574462306a36Sopenharmony_ci			/* for IFLA_STATS_LINK_XSTATS */
574562306a36Sopenharmony_ci			size += nla_total_size(0);
574662306a36Sopenharmony_ci		}
574762306a36Sopenharmony_ci	}
574862306a36Sopenharmony_ci
574962306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_XSTATS_SLAVE, 0)) {
575062306a36Sopenharmony_ci		struct net_device *_dev = (struct net_device *)dev;
575162306a36Sopenharmony_ci		const struct rtnl_link_ops *ops = NULL;
575262306a36Sopenharmony_ci		const struct net_device *master;
575362306a36Sopenharmony_ci
575462306a36Sopenharmony_ci		/* netdev_master_upper_dev_get can't take const */
575562306a36Sopenharmony_ci		master = netdev_master_upper_dev_get(_dev);
575662306a36Sopenharmony_ci		if (master)
575762306a36Sopenharmony_ci			ops = master->rtnl_link_ops;
575862306a36Sopenharmony_ci		if (ops && ops->get_linkxstats_size) {
575962306a36Sopenharmony_ci			int attr = IFLA_STATS_LINK_XSTATS_SLAVE;
576062306a36Sopenharmony_ci
576162306a36Sopenharmony_ci			size += nla_total_size(ops->get_linkxstats_size(dev,
576262306a36Sopenharmony_ci									attr));
576362306a36Sopenharmony_ci			/* for IFLA_STATS_LINK_XSTATS_SLAVE */
576462306a36Sopenharmony_ci			size += nla_total_size(0);
576562306a36Sopenharmony_ci		}
576662306a36Sopenharmony_ci	}
576762306a36Sopenharmony_ci
576862306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_LINK_OFFLOAD_XSTATS, 0)) {
576962306a36Sopenharmony_ci		u32 off_filter_mask;
577062306a36Sopenharmony_ci
577162306a36Sopenharmony_ci		off_filter_mask = filters->mask[IFLA_STATS_LINK_OFFLOAD_XSTATS];
577262306a36Sopenharmony_ci		size += rtnl_offload_xstats_get_size(dev, off_filter_mask);
577362306a36Sopenharmony_ci	}
577462306a36Sopenharmony_ci
577562306a36Sopenharmony_ci	if (stats_attr_valid(filter_mask, IFLA_STATS_AF_SPEC, 0)) {
577662306a36Sopenharmony_ci		struct rtnl_af_ops *af_ops;
577762306a36Sopenharmony_ci
577862306a36Sopenharmony_ci		/* for IFLA_STATS_AF_SPEC */
577962306a36Sopenharmony_ci		size += nla_total_size(0);
578062306a36Sopenharmony_ci
578162306a36Sopenharmony_ci		rcu_read_lock();
578262306a36Sopenharmony_ci		list_for_each_entry_rcu(af_ops, &rtnl_af_ops, list) {
578362306a36Sopenharmony_ci			if (af_ops->get_stats_af_size) {
578462306a36Sopenharmony_ci				size += nla_total_size(
578562306a36Sopenharmony_ci					af_ops->get_stats_af_size(dev));
578662306a36Sopenharmony_ci
578762306a36Sopenharmony_ci				/* for AF_* */
578862306a36Sopenharmony_ci				size += nla_total_size(0);
578962306a36Sopenharmony_ci			}
579062306a36Sopenharmony_ci		}
579162306a36Sopenharmony_ci		rcu_read_unlock();
579262306a36Sopenharmony_ci	}
579362306a36Sopenharmony_ci
579462306a36Sopenharmony_ci	return size;
579562306a36Sopenharmony_ci}
579662306a36Sopenharmony_ci
579762306a36Sopenharmony_ci#define RTNL_STATS_OFFLOAD_XSTATS_VALID ((1 << __IFLA_OFFLOAD_XSTATS_MAX) - 1)
579862306a36Sopenharmony_ci
579962306a36Sopenharmony_cistatic const struct nla_policy
580062306a36Sopenharmony_cirtnl_stats_get_policy_filters[IFLA_STATS_MAX + 1] = {
580162306a36Sopenharmony_ci	[IFLA_STATS_LINK_OFFLOAD_XSTATS] =
580262306a36Sopenharmony_ci		    NLA_POLICY_MASK(NLA_U32, RTNL_STATS_OFFLOAD_XSTATS_VALID),
580362306a36Sopenharmony_ci};
580462306a36Sopenharmony_ci
580562306a36Sopenharmony_cistatic const struct nla_policy
580662306a36Sopenharmony_cirtnl_stats_get_policy[IFLA_STATS_GETSET_MAX + 1] = {
580762306a36Sopenharmony_ci	[IFLA_STATS_GET_FILTERS] =
580862306a36Sopenharmony_ci		    NLA_POLICY_NESTED(rtnl_stats_get_policy_filters),
580962306a36Sopenharmony_ci};
581062306a36Sopenharmony_ci
581162306a36Sopenharmony_cistatic const struct nla_policy
581262306a36Sopenharmony_ciifla_stats_set_policy[IFLA_STATS_GETSET_MAX + 1] = {
581362306a36Sopenharmony_ci	[IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS] = NLA_POLICY_MAX(NLA_U8, 1),
581462306a36Sopenharmony_ci};
581562306a36Sopenharmony_ci
581662306a36Sopenharmony_cistatic int rtnl_stats_get_parse_filters(struct nlattr *ifla_filters,
581762306a36Sopenharmony_ci					struct rtnl_stats_dump_filters *filters,
581862306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
581962306a36Sopenharmony_ci{
582062306a36Sopenharmony_ci	struct nlattr *tb[IFLA_STATS_MAX + 1];
582162306a36Sopenharmony_ci	int err;
582262306a36Sopenharmony_ci	int at;
582362306a36Sopenharmony_ci
582462306a36Sopenharmony_ci	err = nla_parse_nested(tb, IFLA_STATS_MAX, ifla_filters,
582562306a36Sopenharmony_ci			       rtnl_stats_get_policy_filters, extack);
582662306a36Sopenharmony_ci	if (err < 0)
582762306a36Sopenharmony_ci		return err;
582862306a36Sopenharmony_ci
582962306a36Sopenharmony_ci	for (at = 1; at <= IFLA_STATS_MAX; at++) {
583062306a36Sopenharmony_ci		if (tb[at]) {
583162306a36Sopenharmony_ci			if (!(filters->mask[0] & IFLA_STATS_FILTER_BIT(at))) {
583262306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Filtered attribute not enabled in filter_mask");
583362306a36Sopenharmony_ci				return -EINVAL;
583462306a36Sopenharmony_ci			}
583562306a36Sopenharmony_ci			filters->mask[at] = nla_get_u32(tb[at]);
583662306a36Sopenharmony_ci		}
583762306a36Sopenharmony_ci	}
583862306a36Sopenharmony_ci
583962306a36Sopenharmony_ci	return 0;
584062306a36Sopenharmony_ci}
584162306a36Sopenharmony_ci
584262306a36Sopenharmony_cistatic int rtnl_stats_get_parse(const struct nlmsghdr *nlh,
584362306a36Sopenharmony_ci				u32 filter_mask,
584462306a36Sopenharmony_ci				struct rtnl_stats_dump_filters *filters,
584562306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
584662306a36Sopenharmony_ci{
584762306a36Sopenharmony_ci	struct nlattr *tb[IFLA_STATS_GETSET_MAX + 1];
584862306a36Sopenharmony_ci	int err;
584962306a36Sopenharmony_ci	int i;
585062306a36Sopenharmony_ci
585162306a36Sopenharmony_ci	filters->mask[0] = filter_mask;
585262306a36Sopenharmony_ci	for (i = 1; i < ARRAY_SIZE(filters->mask); i++)
585362306a36Sopenharmony_ci		filters->mask[i] = -1U;
585462306a36Sopenharmony_ci
585562306a36Sopenharmony_ci	err = nlmsg_parse(nlh, sizeof(struct if_stats_msg), tb,
585662306a36Sopenharmony_ci			  IFLA_STATS_GETSET_MAX, rtnl_stats_get_policy, extack);
585762306a36Sopenharmony_ci	if (err < 0)
585862306a36Sopenharmony_ci		return err;
585962306a36Sopenharmony_ci
586062306a36Sopenharmony_ci	if (tb[IFLA_STATS_GET_FILTERS]) {
586162306a36Sopenharmony_ci		err = rtnl_stats_get_parse_filters(tb[IFLA_STATS_GET_FILTERS],
586262306a36Sopenharmony_ci						   filters, extack);
586362306a36Sopenharmony_ci		if (err)
586462306a36Sopenharmony_ci			return err;
586562306a36Sopenharmony_ci	}
586662306a36Sopenharmony_ci
586762306a36Sopenharmony_ci	return 0;
586862306a36Sopenharmony_ci}
586962306a36Sopenharmony_ci
587062306a36Sopenharmony_cistatic int rtnl_valid_stats_req(const struct nlmsghdr *nlh, bool strict_check,
587162306a36Sopenharmony_ci				bool is_dump, struct netlink_ext_ack *extack)
587262306a36Sopenharmony_ci{
587362306a36Sopenharmony_ci	struct if_stats_msg *ifsm;
587462306a36Sopenharmony_ci
587562306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifsm))) {
587662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid header for stats dump");
587762306a36Sopenharmony_ci		return -EINVAL;
587862306a36Sopenharmony_ci	}
587962306a36Sopenharmony_ci
588062306a36Sopenharmony_ci	if (!strict_check)
588162306a36Sopenharmony_ci		return 0;
588262306a36Sopenharmony_ci
588362306a36Sopenharmony_ci	ifsm = nlmsg_data(nlh);
588462306a36Sopenharmony_ci
588562306a36Sopenharmony_ci	/* only requests using strict checks can pass data to influence
588662306a36Sopenharmony_ci	 * the dump. The legacy exception is filter_mask.
588762306a36Sopenharmony_ci	 */
588862306a36Sopenharmony_ci	if (ifsm->pad1 || ifsm->pad2 || (is_dump && ifsm->ifindex)) {
588962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid values in header for stats dump request");
589062306a36Sopenharmony_ci		return -EINVAL;
589162306a36Sopenharmony_ci	}
589262306a36Sopenharmony_ci	if (ifsm->filter_mask >= IFLA_STATS_FILTER_BIT(IFLA_STATS_MAX + 1)) {
589362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid stats requested through filter mask");
589462306a36Sopenharmony_ci		return -EINVAL;
589562306a36Sopenharmony_ci	}
589662306a36Sopenharmony_ci
589762306a36Sopenharmony_ci	return 0;
589862306a36Sopenharmony_ci}
589962306a36Sopenharmony_ci
590062306a36Sopenharmony_cistatic int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh,
590162306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
590262306a36Sopenharmony_ci{
590362306a36Sopenharmony_ci	struct rtnl_stats_dump_filters filters;
590462306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
590562306a36Sopenharmony_ci	struct net_device *dev = NULL;
590662306a36Sopenharmony_ci	int idxattr = 0, prividx = 0;
590762306a36Sopenharmony_ci	struct if_stats_msg *ifsm;
590862306a36Sopenharmony_ci	struct sk_buff *nskb;
590962306a36Sopenharmony_ci	int err;
591062306a36Sopenharmony_ci
591162306a36Sopenharmony_ci	err = rtnl_valid_stats_req(nlh, netlink_strict_get_check(skb),
591262306a36Sopenharmony_ci				   false, extack);
591362306a36Sopenharmony_ci	if (err)
591462306a36Sopenharmony_ci		return err;
591562306a36Sopenharmony_ci
591662306a36Sopenharmony_ci	ifsm = nlmsg_data(nlh);
591762306a36Sopenharmony_ci	if (ifsm->ifindex > 0)
591862306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ifsm->ifindex);
591962306a36Sopenharmony_ci	else
592062306a36Sopenharmony_ci		return -EINVAL;
592162306a36Sopenharmony_ci
592262306a36Sopenharmony_ci	if (!dev)
592362306a36Sopenharmony_ci		return -ENODEV;
592462306a36Sopenharmony_ci
592562306a36Sopenharmony_ci	if (!ifsm->filter_mask) {
592662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter mask must be set for stats get");
592762306a36Sopenharmony_ci		return -EINVAL;
592862306a36Sopenharmony_ci	}
592962306a36Sopenharmony_ci
593062306a36Sopenharmony_ci	err = rtnl_stats_get_parse(nlh, ifsm->filter_mask, &filters, extack);
593162306a36Sopenharmony_ci	if (err)
593262306a36Sopenharmony_ci		return err;
593362306a36Sopenharmony_ci
593462306a36Sopenharmony_ci	nskb = nlmsg_new(if_nlmsg_stats_size(dev, &filters), GFP_KERNEL);
593562306a36Sopenharmony_ci	if (!nskb)
593662306a36Sopenharmony_ci		return -ENOBUFS;
593762306a36Sopenharmony_ci
593862306a36Sopenharmony_ci	err = rtnl_fill_statsinfo(nskb, dev, RTM_NEWSTATS,
593962306a36Sopenharmony_ci				  NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
594062306a36Sopenharmony_ci				  0, &filters, &idxattr, &prividx, extack);
594162306a36Sopenharmony_ci	if (err < 0) {
594262306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in if_nlmsg_stats_size */
594362306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
594462306a36Sopenharmony_ci		kfree_skb(nskb);
594562306a36Sopenharmony_ci	} else {
594662306a36Sopenharmony_ci		err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid);
594762306a36Sopenharmony_ci	}
594862306a36Sopenharmony_ci
594962306a36Sopenharmony_ci	return err;
595062306a36Sopenharmony_ci}
595162306a36Sopenharmony_ci
595262306a36Sopenharmony_cistatic int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb)
595362306a36Sopenharmony_ci{
595462306a36Sopenharmony_ci	struct netlink_ext_ack *extack = cb->extack;
595562306a36Sopenharmony_ci	int h, s_h, err, s_idx, s_idxattr, s_prividx;
595662306a36Sopenharmony_ci	struct rtnl_stats_dump_filters filters;
595762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
595862306a36Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
595962306a36Sopenharmony_ci	struct if_stats_msg *ifsm;
596062306a36Sopenharmony_ci	struct hlist_head *head;
596162306a36Sopenharmony_ci	struct net_device *dev;
596262306a36Sopenharmony_ci	int idx = 0;
596362306a36Sopenharmony_ci
596462306a36Sopenharmony_ci	s_h = cb->args[0];
596562306a36Sopenharmony_ci	s_idx = cb->args[1];
596662306a36Sopenharmony_ci	s_idxattr = cb->args[2];
596762306a36Sopenharmony_ci	s_prividx = cb->args[3];
596862306a36Sopenharmony_ci
596962306a36Sopenharmony_ci	cb->seq = net->dev_base_seq;
597062306a36Sopenharmony_ci
597162306a36Sopenharmony_ci	err = rtnl_valid_stats_req(cb->nlh, cb->strict_check, true, extack);
597262306a36Sopenharmony_ci	if (err)
597362306a36Sopenharmony_ci		return err;
597462306a36Sopenharmony_ci
597562306a36Sopenharmony_ci	ifsm = nlmsg_data(cb->nlh);
597662306a36Sopenharmony_ci	if (!ifsm->filter_mask) {
597762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter mask must be set for stats dump");
597862306a36Sopenharmony_ci		return -EINVAL;
597962306a36Sopenharmony_ci	}
598062306a36Sopenharmony_ci
598162306a36Sopenharmony_ci	err = rtnl_stats_get_parse(cb->nlh, ifsm->filter_mask, &filters,
598262306a36Sopenharmony_ci				   extack);
598362306a36Sopenharmony_ci	if (err)
598462306a36Sopenharmony_ci		return err;
598562306a36Sopenharmony_ci
598662306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
598762306a36Sopenharmony_ci		idx = 0;
598862306a36Sopenharmony_ci		head = &net->dev_index_head[h];
598962306a36Sopenharmony_ci		hlist_for_each_entry(dev, head, index_hlist) {
599062306a36Sopenharmony_ci			if (idx < s_idx)
599162306a36Sopenharmony_ci				goto cont;
599262306a36Sopenharmony_ci			err = rtnl_fill_statsinfo(skb, dev, RTM_NEWSTATS,
599362306a36Sopenharmony_ci						  NETLINK_CB(cb->skb).portid,
599462306a36Sopenharmony_ci						  cb->nlh->nlmsg_seq, 0,
599562306a36Sopenharmony_ci						  flags, &filters,
599662306a36Sopenharmony_ci						  &s_idxattr, &s_prividx,
599762306a36Sopenharmony_ci						  extack);
599862306a36Sopenharmony_ci			/* If we ran out of room on the first message,
599962306a36Sopenharmony_ci			 * we're in trouble
600062306a36Sopenharmony_ci			 */
600162306a36Sopenharmony_ci			WARN_ON((err == -EMSGSIZE) && (skb->len == 0));
600262306a36Sopenharmony_ci
600362306a36Sopenharmony_ci			if (err < 0)
600462306a36Sopenharmony_ci				goto out;
600562306a36Sopenharmony_ci			s_prividx = 0;
600662306a36Sopenharmony_ci			s_idxattr = 0;
600762306a36Sopenharmony_ci			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
600862306a36Sopenharmony_cicont:
600962306a36Sopenharmony_ci			idx++;
601062306a36Sopenharmony_ci		}
601162306a36Sopenharmony_ci	}
601262306a36Sopenharmony_ciout:
601362306a36Sopenharmony_ci	cb->args[3] = s_prividx;
601462306a36Sopenharmony_ci	cb->args[2] = s_idxattr;
601562306a36Sopenharmony_ci	cb->args[1] = idx;
601662306a36Sopenharmony_ci	cb->args[0] = h;
601762306a36Sopenharmony_ci
601862306a36Sopenharmony_ci	return skb->len;
601962306a36Sopenharmony_ci}
602062306a36Sopenharmony_ci
602162306a36Sopenharmony_civoid rtnl_offload_xstats_notify(struct net_device *dev)
602262306a36Sopenharmony_ci{
602362306a36Sopenharmony_ci	struct rtnl_stats_dump_filters response_filters = {};
602462306a36Sopenharmony_ci	struct net *net = dev_net(dev);
602562306a36Sopenharmony_ci	int idxattr = 0, prividx = 0;
602662306a36Sopenharmony_ci	struct sk_buff *skb;
602762306a36Sopenharmony_ci	int err = -ENOBUFS;
602862306a36Sopenharmony_ci
602962306a36Sopenharmony_ci	ASSERT_RTNL();
603062306a36Sopenharmony_ci
603162306a36Sopenharmony_ci	response_filters.mask[0] |=
603262306a36Sopenharmony_ci		IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_OFFLOAD_XSTATS);
603362306a36Sopenharmony_ci	response_filters.mask[IFLA_STATS_LINK_OFFLOAD_XSTATS] |=
603462306a36Sopenharmony_ci		IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO);
603562306a36Sopenharmony_ci
603662306a36Sopenharmony_ci	skb = nlmsg_new(if_nlmsg_stats_size(dev, &response_filters),
603762306a36Sopenharmony_ci			GFP_KERNEL);
603862306a36Sopenharmony_ci	if (!skb)
603962306a36Sopenharmony_ci		goto errout;
604062306a36Sopenharmony_ci
604162306a36Sopenharmony_ci	err = rtnl_fill_statsinfo(skb, dev, RTM_NEWSTATS, 0, 0, 0, 0,
604262306a36Sopenharmony_ci				  &response_filters, &idxattr, &prividx, NULL);
604362306a36Sopenharmony_ci	if (err < 0) {
604462306a36Sopenharmony_ci		kfree_skb(skb);
604562306a36Sopenharmony_ci		goto errout;
604662306a36Sopenharmony_ci	}
604762306a36Sopenharmony_ci
604862306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_STATS, NULL, GFP_KERNEL);
604962306a36Sopenharmony_ci	return;
605062306a36Sopenharmony_ci
605162306a36Sopenharmony_cierrout:
605262306a36Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_STATS, err);
605362306a36Sopenharmony_ci}
605462306a36Sopenharmony_ciEXPORT_SYMBOL(rtnl_offload_xstats_notify);
605562306a36Sopenharmony_ci
605662306a36Sopenharmony_cistatic int rtnl_stats_set(struct sk_buff *skb, struct nlmsghdr *nlh,
605762306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
605862306a36Sopenharmony_ci{
605962306a36Sopenharmony_ci	enum netdev_offload_xstats_type t_l3 = NETDEV_OFFLOAD_XSTATS_TYPE_L3;
606062306a36Sopenharmony_ci	struct rtnl_stats_dump_filters response_filters = {};
606162306a36Sopenharmony_ci	struct nlattr *tb[IFLA_STATS_GETSET_MAX + 1];
606262306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
606362306a36Sopenharmony_ci	struct net_device *dev = NULL;
606462306a36Sopenharmony_ci	struct if_stats_msg *ifsm;
606562306a36Sopenharmony_ci	bool notify = false;
606662306a36Sopenharmony_ci	int err;
606762306a36Sopenharmony_ci
606862306a36Sopenharmony_ci	err = rtnl_valid_stats_req(nlh, netlink_strict_get_check(skb),
606962306a36Sopenharmony_ci				   false, extack);
607062306a36Sopenharmony_ci	if (err)
607162306a36Sopenharmony_ci		return err;
607262306a36Sopenharmony_ci
607362306a36Sopenharmony_ci	ifsm = nlmsg_data(nlh);
607462306a36Sopenharmony_ci	if (ifsm->family != AF_UNSPEC) {
607562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Address family should be AF_UNSPEC");
607662306a36Sopenharmony_ci		return -EINVAL;
607762306a36Sopenharmony_ci	}
607862306a36Sopenharmony_ci
607962306a36Sopenharmony_ci	if (ifsm->ifindex > 0)
608062306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ifsm->ifindex);
608162306a36Sopenharmony_ci	else
608262306a36Sopenharmony_ci		return -EINVAL;
608362306a36Sopenharmony_ci
608462306a36Sopenharmony_ci	if (!dev)
608562306a36Sopenharmony_ci		return -ENODEV;
608662306a36Sopenharmony_ci
608762306a36Sopenharmony_ci	if (ifsm->filter_mask) {
608862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filter mask must be 0 for stats set");
608962306a36Sopenharmony_ci		return -EINVAL;
609062306a36Sopenharmony_ci	}
609162306a36Sopenharmony_ci
609262306a36Sopenharmony_ci	err = nlmsg_parse(nlh, sizeof(*ifsm), tb, IFLA_STATS_GETSET_MAX,
609362306a36Sopenharmony_ci			  ifla_stats_set_policy, extack);
609462306a36Sopenharmony_ci	if (err < 0)
609562306a36Sopenharmony_ci		return err;
609662306a36Sopenharmony_ci
609762306a36Sopenharmony_ci	if (tb[IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS]) {
609862306a36Sopenharmony_ci		u8 req = nla_get_u8(tb[IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS]);
609962306a36Sopenharmony_ci
610062306a36Sopenharmony_ci		if (req)
610162306a36Sopenharmony_ci			err = netdev_offload_xstats_enable(dev, t_l3, extack);
610262306a36Sopenharmony_ci		else
610362306a36Sopenharmony_ci			err = netdev_offload_xstats_disable(dev, t_l3);
610462306a36Sopenharmony_ci
610562306a36Sopenharmony_ci		if (!err)
610662306a36Sopenharmony_ci			notify = true;
610762306a36Sopenharmony_ci		else if (err != -EALREADY)
610862306a36Sopenharmony_ci			return err;
610962306a36Sopenharmony_ci
611062306a36Sopenharmony_ci		response_filters.mask[0] |=
611162306a36Sopenharmony_ci			IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_OFFLOAD_XSTATS);
611262306a36Sopenharmony_ci		response_filters.mask[IFLA_STATS_LINK_OFFLOAD_XSTATS] |=
611362306a36Sopenharmony_ci			IFLA_STATS_FILTER_BIT(IFLA_OFFLOAD_XSTATS_HW_S_INFO);
611462306a36Sopenharmony_ci	}
611562306a36Sopenharmony_ci
611662306a36Sopenharmony_ci	if (notify)
611762306a36Sopenharmony_ci		rtnl_offload_xstats_notify(dev);
611862306a36Sopenharmony_ci
611962306a36Sopenharmony_ci	return 0;
612062306a36Sopenharmony_ci}
612162306a36Sopenharmony_ci
612262306a36Sopenharmony_cistatic int rtnl_mdb_valid_dump_req(const struct nlmsghdr *nlh,
612362306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
612462306a36Sopenharmony_ci{
612562306a36Sopenharmony_ci	struct br_port_msg *bpm;
612662306a36Sopenharmony_ci
612762306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*bpm))) {
612862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid header for mdb dump request");
612962306a36Sopenharmony_ci		return -EINVAL;
613062306a36Sopenharmony_ci	}
613162306a36Sopenharmony_ci
613262306a36Sopenharmony_ci	bpm = nlmsg_data(nlh);
613362306a36Sopenharmony_ci	if (bpm->ifindex) {
613462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Filtering by device index is not supported for mdb dump request");
613562306a36Sopenharmony_ci		return -EINVAL;
613662306a36Sopenharmony_ci	}
613762306a36Sopenharmony_ci	if (nlmsg_attrlen(nlh, sizeof(*bpm))) {
613862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid data after header in mdb dump request");
613962306a36Sopenharmony_ci		return -EINVAL;
614062306a36Sopenharmony_ci	}
614162306a36Sopenharmony_ci
614262306a36Sopenharmony_ci	return 0;
614362306a36Sopenharmony_ci}
614462306a36Sopenharmony_ci
614562306a36Sopenharmony_cistruct rtnl_mdb_dump_ctx {
614662306a36Sopenharmony_ci	long idx;
614762306a36Sopenharmony_ci};
614862306a36Sopenharmony_ci
614962306a36Sopenharmony_cistatic int rtnl_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
615062306a36Sopenharmony_ci{
615162306a36Sopenharmony_ci	struct rtnl_mdb_dump_ctx *ctx = (void *)cb->ctx;
615262306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
615362306a36Sopenharmony_ci	struct net_device *dev;
615462306a36Sopenharmony_ci	int idx, s_idx;
615562306a36Sopenharmony_ci	int err;
615662306a36Sopenharmony_ci
615762306a36Sopenharmony_ci	NL_ASSERT_DUMP_CTX_FITS(struct rtnl_mdb_dump_ctx);
615862306a36Sopenharmony_ci
615962306a36Sopenharmony_ci	if (cb->strict_check) {
616062306a36Sopenharmony_ci		err = rtnl_mdb_valid_dump_req(cb->nlh, cb->extack);
616162306a36Sopenharmony_ci		if (err)
616262306a36Sopenharmony_ci			return err;
616362306a36Sopenharmony_ci	}
616462306a36Sopenharmony_ci
616562306a36Sopenharmony_ci	s_idx = ctx->idx;
616662306a36Sopenharmony_ci	idx = 0;
616762306a36Sopenharmony_ci
616862306a36Sopenharmony_ci	for_each_netdev(net, dev) {
616962306a36Sopenharmony_ci		if (idx < s_idx)
617062306a36Sopenharmony_ci			goto skip;
617162306a36Sopenharmony_ci		if (!dev->netdev_ops->ndo_mdb_dump)
617262306a36Sopenharmony_ci			goto skip;
617362306a36Sopenharmony_ci
617462306a36Sopenharmony_ci		err = dev->netdev_ops->ndo_mdb_dump(dev, skb, cb);
617562306a36Sopenharmony_ci		if (err == -EMSGSIZE)
617662306a36Sopenharmony_ci			goto out;
617762306a36Sopenharmony_ci		/* Moving on to next device, reset markers and sequence
617862306a36Sopenharmony_ci		 * counters since they are all maintained per-device.
617962306a36Sopenharmony_ci		 */
618062306a36Sopenharmony_ci		memset(cb->ctx, 0, sizeof(cb->ctx));
618162306a36Sopenharmony_ci		cb->prev_seq = 0;
618262306a36Sopenharmony_ci		cb->seq = 0;
618362306a36Sopenharmony_ciskip:
618462306a36Sopenharmony_ci		idx++;
618562306a36Sopenharmony_ci	}
618662306a36Sopenharmony_ci
618762306a36Sopenharmony_ciout:
618862306a36Sopenharmony_ci	ctx->idx = idx;
618962306a36Sopenharmony_ci	return skb->len;
619062306a36Sopenharmony_ci}
619162306a36Sopenharmony_ci
619262306a36Sopenharmony_cistatic int rtnl_validate_mdb_entry(const struct nlattr *attr,
619362306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
619462306a36Sopenharmony_ci{
619562306a36Sopenharmony_ci	struct br_mdb_entry *entry = nla_data(attr);
619662306a36Sopenharmony_ci
619762306a36Sopenharmony_ci	if (nla_len(attr) != sizeof(struct br_mdb_entry)) {
619862306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, attr, "Invalid attribute length");
619962306a36Sopenharmony_ci		return -EINVAL;
620062306a36Sopenharmony_ci	}
620162306a36Sopenharmony_ci
620262306a36Sopenharmony_ci	if (entry->ifindex == 0) {
620362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Zero entry ifindex is not allowed");
620462306a36Sopenharmony_ci		return -EINVAL;
620562306a36Sopenharmony_ci	}
620662306a36Sopenharmony_ci
620762306a36Sopenharmony_ci	if (entry->addr.proto == htons(ETH_P_IP)) {
620862306a36Sopenharmony_ci		if (!ipv4_is_multicast(entry->addr.u.ip4) &&
620962306a36Sopenharmony_ci		    !ipv4_is_zeronet(entry->addr.u.ip4)) {
621062306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "IPv4 entry group address is not multicast or 0.0.0.0");
621162306a36Sopenharmony_ci			return -EINVAL;
621262306a36Sopenharmony_ci		}
621362306a36Sopenharmony_ci		if (ipv4_is_local_multicast(entry->addr.u.ip4)) {
621462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "IPv4 entry group address is local multicast");
621562306a36Sopenharmony_ci			return -EINVAL;
621662306a36Sopenharmony_ci		}
621762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
621862306a36Sopenharmony_ci	} else if (entry->addr.proto == htons(ETH_P_IPV6)) {
621962306a36Sopenharmony_ci		if (ipv6_addr_is_ll_all_nodes(&entry->addr.u.ip6)) {
622062306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "IPv6 entry group address is link-local all nodes");
622162306a36Sopenharmony_ci			return -EINVAL;
622262306a36Sopenharmony_ci		}
622362306a36Sopenharmony_ci#endif
622462306a36Sopenharmony_ci	} else if (entry->addr.proto == 0) {
622562306a36Sopenharmony_ci		/* L2 mdb */
622662306a36Sopenharmony_ci		if (!is_multicast_ether_addr(entry->addr.u.mac_addr)) {
622762306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "L2 entry group is not multicast");
622862306a36Sopenharmony_ci			return -EINVAL;
622962306a36Sopenharmony_ci		}
623062306a36Sopenharmony_ci	} else {
623162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Unknown entry protocol");
623262306a36Sopenharmony_ci		return -EINVAL;
623362306a36Sopenharmony_ci	}
623462306a36Sopenharmony_ci
623562306a36Sopenharmony_ci	if (entry->state != MDB_PERMANENT && entry->state != MDB_TEMPORARY) {
623662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Unknown entry state");
623762306a36Sopenharmony_ci		return -EINVAL;
623862306a36Sopenharmony_ci	}
623962306a36Sopenharmony_ci	if (entry->vid >= VLAN_VID_MASK) {
624062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid entry VLAN id");
624162306a36Sopenharmony_ci		return -EINVAL;
624262306a36Sopenharmony_ci	}
624362306a36Sopenharmony_ci
624462306a36Sopenharmony_ci	return 0;
624562306a36Sopenharmony_ci}
624662306a36Sopenharmony_ci
624762306a36Sopenharmony_cistatic const struct nla_policy mdba_policy[MDBA_SET_ENTRY_MAX + 1] = {
624862306a36Sopenharmony_ci	[MDBA_SET_ENTRY_UNSPEC] = { .strict_start_type = MDBA_SET_ENTRY_ATTRS + 1 },
624962306a36Sopenharmony_ci	[MDBA_SET_ENTRY] = NLA_POLICY_VALIDATE_FN(NLA_BINARY,
625062306a36Sopenharmony_ci						  rtnl_validate_mdb_entry,
625162306a36Sopenharmony_ci						  sizeof(struct br_mdb_entry)),
625262306a36Sopenharmony_ci	[MDBA_SET_ENTRY_ATTRS] = { .type = NLA_NESTED },
625362306a36Sopenharmony_ci};
625462306a36Sopenharmony_ci
625562306a36Sopenharmony_cistatic int rtnl_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh,
625662306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
625762306a36Sopenharmony_ci{
625862306a36Sopenharmony_ci	struct nlattr *tb[MDBA_SET_ENTRY_MAX + 1];
625962306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
626062306a36Sopenharmony_ci	struct br_port_msg *bpm;
626162306a36Sopenharmony_ci	struct net_device *dev;
626262306a36Sopenharmony_ci	int err;
626362306a36Sopenharmony_ci
626462306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
626562306a36Sopenharmony_ci				     MDBA_SET_ENTRY_MAX, mdba_policy, extack);
626662306a36Sopenharmony_ci	if (err)
626762306a36Sopenharmony_ci		return err;
626862306a36Sopenharmony_ci
626962306a36Sopenharmony_ci	bpm = nlmsg_data(nlh);
627062306a36Sopenharmony_ci	if (!bpm->ifindex) {
627162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid ifindex");
627262306a36Sopenharmony_ci		return -EINVAL;
627362306a36Sopenharmony_ci	}
627462306a36Sopenharmony_ci
627562306a36Sopenharmony_ci	dev = __dev_get_by_index(net, bpm->ifindex);
627662306a36Sopenharmony_ci	if (!dev) {
627762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device doesn't exist");
627862306a36Sopenharmony_ci		return -ENODEV;
627962306a36Sopenharmony_ci	}
628062306a36Sopenharmony_ci
628162306a36Sopenharmony_ci	if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY)) {
628262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Missing MDBA_SET_ENTRY attribute");
628362306a36Sopenharmony_ci		return -EINVAL;
628462306a36Sopenharmony_ci	}
628562306a36Sopenharmony_ci
628662306a36Sopenharmony_ci	if (!dev->netdev_ops->ndo_mdb_add) {
628762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device does not support MDB operations");
628862306a36Sopenharmony_ci		return -EOPNOTSUPP;
628962306a36Sopenharmony_ci	}
629062306a36Sopenharmony_ci
629162306a36Sopenharmony_ci	return dev->netdev_ops->ndo_mdb_add(dev, tb, nlh->nlmsg_flags, extack);
629262306a36Sopenharmony_ci}
629362306a36Sopenharmony_ci
629462306a36Sopenharmony_cistatic int rtnl_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
629562306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
629662306a36Sopenharmony_ci{
629762306a36Sopenharmony_ci	struct nlattr *tb[MDBA_SET_ENTRY_MAX + 1];
629862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
629962306a36Sopenharmony_ci	struct br_port_msg *bpm;
630062306a36Sopenharmony_ci	struct net_device *dev;
630162306a36Sopenharmony_ci	int err;
630262306a36Sopenharmony_ci
630362306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*bpm), tb,
630462306a36Sopenharmony_ci				     MDBA_SET_ENTRY_MAX, mdba_policy, extack);
630562306a36Sopenharmony_ci	if (err)
630662306a36Sopenharmony_ci		return err;
630762306a36Sopenharmony_ci
630862306a36Sopenharmony_ci	bpm = nlmsg_data(nlh);
630962306a36Sopenharmony_ci	if (!bpm->ifindex) {
631062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid ifindex");
631162306a36Sopenharmony_ci		return -EINVAL;
631262306a36Sopenharmony_ci	}
631362306a36Sopenharmony_ci
631462306a36Sopenharmony_ci	dev = __dev_get_by_index(net, bpm->ifindex);
631562306a36Sopenharmony_ci	if (!dev) {
631662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device doesn't exist");
631762306a36Sopenharmony_ci		return -ENODEV;
631862306a36Sopenharmony_ci	}
631962306a36Sopenharmony_ci
632062306a36Sopenharmony_ci	if (NL_REQ_ATTR_CHECK(extack, NULL, tb, MDBA_SET_ENTRY)) {
632162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Missing MDBA_SET_ENTRY attribute");
632262306a36Sopenharmony_ci		return -EINVAL;
632362306a36Sopenharmony_ci	}
632462306a36Sopenharmony_ci
632562306a36Sopenharmony_ci	if (!dev->netdev_ops->ndo_mdb_del) {
632662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device does not support MDB operations");
632762306a36Sopenharmony_ci		return -EOPNOTSUPP;
632862306a36Sopenharmony_ci	}
632962306a36Sopenharmony_ci
633062306a36Sopenharmony_ci	return dev->netdev_ops->ndo_mdb_del(dev, tb, extack);
633162306a36Sopenharmony_ci}
633262306a36Sopenharmony_ci
633362306a36Sopenharmony_ci/* Process one rtnetlink message. */
633462306a36Sopenharmony_ci
633562306a36Sopenharmony_cistatic int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
633662306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
633762306a36Sopenharmony_ci{
633862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
633962306a36Sopenharmony_ci	struct rtnl_link *link;
634062306a36Sopenharmony_ci	enum rtnl_kinds kind;
634162306a36Sopenharmony_ci	struct module *owner;
634262306a36Sopenharmony_ci	int err = -EOPNOTSUPP;
634362306a36Sopenharmony_ci	rtnl_doit_func doit;
634462306a36Sopenharmony_ci	unsigned int flags;
634562306a36Sopenharmony_ci	int family;
634662306a36Sopenharmony_ci	int type;
634762306a36Sopenharmony_ci
634862306a36Sopenharmony_ci	type = nlh->nlmsg_type;
634962306a36Sopenharmony_ci	if (type > RTM_MAX)
635062306a36Sopenharmony_ci		return -EOPNOTSUPP;
635162306a36Sopenharmony_ci
635262306a36Sopenharmony_ci	type -= RTM_BASE;
635362306a36Sopenharmony_ci
635462306a36Sopenharmony_ci	/* All the messages must have at least 1 byte length */
635562306a36Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(struct rtgenmsg))
635662306a36Sopenharmony_ci		return 0;
635762306a36Sopenharmony_ci
635862306a36Sopenharmony_ci	family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
635962306a36Sopenharmony_ci	kind = rtnl_msgtype_kind(type);
636062306a36Sopenharmony_ci
636162306a36Sopenharmony_ci	if (kind != RTNL_KIND_GET && !netlink_net_capable(skb, CAP_NET_ADMIN))
636262306a36Sopenharmony_ci		return -EPERM;
636362306a36Sopenharmony_ci
636462306a36Sopenharmony_ci	rcu_read_lock();
636562306a36Sopenharmony_ci	if (kind == RTNL_KIND_GET && (nlh->nlmsg_flags & NLM_F_DUMP)) {
636662306a36Sopenharmony_ci		struct sock *rtnl;
636762306a36Sopenharmony_ci		rtnl_dumpit_func dumpit;
636862306a36Sopenharmony_ci		u32 min_dump_alloc = 0;
636962306a36Sopenharmony_ci
637062306a36Sopenharmony_ci		link = rtnl_get_link(family, type);
637162306a36Sopenharmony_ci		if (!link || !link->dumpit) {
637262306a36Sopenharmony_ci			family = PF_UNSPEC;
637362306a36Sopenharmony_ci			link = rtnl_get_link(family, type);
637462306a36Sopenharmony_ci			if (!link || !link->dumpit)
637562306a36Sopenharmony_ci				goto err_unlock;
637662306a36Sopenharmony_ci		}
637762306a36Sopenharmony_ci		owner = link->owner;
637862306a36Sopenharmony_ci		dumpit = link->dumpit;
637962306a36Sopenharmony_ci
638062306a36Sopenharmony_ci		if (type == RTM_GETLINK - RTM_BASE)
638162306a36Sopenharmony_ci			min_dump_alloc = rtnl_calcit(skb, nlh);
638262306a36Sopenharmony_ci
638362306a36Sopenharmony_ci		err = 0;
638462306a36Sopenharmony_ci		/* need to do this before rcu_read_unlock() */
638562306a36Sopenharmony_ci		if (!try_module_get(owner))
638662306a36Sopenharmony_ci			err = -EPROTONOSUPPORT;
638762306a36Sopenharmony_ci
638862306a36Sopenharmony_ci		rcu_read_unlock();
638962306a36Sopenharmony_ci
639062306a36Sopenharmony_ci		rtnl = net->rtnl;
639162306a36Sopenharmony_ci		if (err == 0) {
639262306a36Sopenharmony_ci			struct netlink_dump_control c = {
639362306a36Sopenharmony_ci				.dump		= dumpit,
639462306a36Sopenharmony_ci				.min_dump_alloc	= min_dump_alloc,
639562306a36Sopenharmony_ci				.module		= owner,
639662306a36Sopenharmony_ci			};
639762306a36Sopenharmony_ci			err = netlink_dump_start(rtnl, skb, nlh, &c);
639862306a36Sopenharmony_ci			/* netlink_dump_start() will keep a reference on
639962306a36Sopenharmony_ci			 * module if dump is still in progress.
640062306a36Sopenharmony_ci			 */
640162306a36Sopenharmony_ci			module_put(owner);
640262306a36Sopenharmony_ci		}
640362306a36Sopenharmony_ci		return err;
640462306a36Sopenharmony_ci	}
640562306a36Sopenharmony_ci
640662306a36Sopenharmony_ci	link = rtnl_get_link(family, type);
640762306a36Sopenharmony_ci	if (!link || !link->doit) {
640862306a36Sopenharmony_ci		family = PF_UNSPEC;
640962306a36Sopenharmony_ci		link = rtnl_get_link(PF_UNSPEC, type);
641062306a36Sopenharmony_ci		if (!link || !link->doit)
641162306a36Sopenharmony_ci			goto out_unlock;
641262306a36Sopenharmony_ci	}
641362306a36Sopenharmony_ci
641462306a36Sopenharmony_ci	owner = link->owner;
641562306a36Sopenharmony_ci	if (!try_module_get(owner)) {
641662306a36Sopenharmony_ci		err = -EPROTONOSUPPORT;
641762306a36Sopenharmony_ci		goto out_unlock;
641862306a36Sopenharmony_ci	}
641962306a36Sopenharmony_ci
642062306a36Sopenharmony_ci	flags = link->flags;
642162306a36Sopenharmony_ci	if (kind == RTNL_KIND_DEL && (nlh->nlmsg_flags & NLM_F_BULK) &&
642262306a36Sopenharmony_ci	    !(flags & RTNL_FLAG_BULK_DEL_SUPPORTED)) {
642362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Bulk delete is not supported");
642462306a36Sopenharmony_ci		module_put(owner);
642562306a36Sopenharmony_ci		goto err_unlock;
642662306a36Sopenharmony_ci	}
642762306a36Sopenharmony_ci
642862306a36Sopenharmony_ci	if (flags & RTNL_FLAG_DOIT_UNLOCKED) {
642962306a36Sopenharmony_ci		doit = link->doit;
643062306a36Sopenharmony_ci		rcu_read_unlock();
643162306a36Sopenharmony_ci		if (doit)
643262306a36Sopenharmony_ci			err = doit(skb, nlh, extack);
643362306a36Sopenharmony_ci		module_put(owner);
643462306a36Sopenharmony_ci		return err;
643562306a36Sopenharmony_ci	}
643662306a36Sopenharmony_ci	rcu_read_unlock();
643762306a36Sopenharmony_ci
643862306a36Sopenharmony_ci	rtnl_lock();
643962306a36Sopenharmony_ci	link = rtnl_get_link(family, type);
644062306a36Sopenharmony_ci	if (link && link->doit)
644162306a36Sopenharmony_ci		err = link->doit(skb, nlh, extack);
644262306a36Sopenharmony_ci	rtnl_unlock();
644362306a36Sopenharmony_ci
644462306a36Sopenharmony_ci	module_put(owner);
644562306a36Sopenharmony_ci
644662306a36Sopenharmony_ci	return err;
644762306a36Sopenharmony_ci
644862306a36Sopenharmony_ciout_unlock:
644962306a36Sopenharmony_ci	rcu_read_unlock();
645062306a36Sopenharmony_ci	return err;
645162306a36Sopenharmony_ci
645262306a36Sopenharmony_cierr_unlock:
645362306a36Sopenharmony_ci	rcu_read_unlock();
645462306a36Sopenharmony_ci	return -EOPNOTSUPP;
645562306a36Sopenharmony_ci}
645662306a36Sopenharmony_ci
645762306a36Sopenharmony_cistatic void rtnetlink_rcv(struct sk_buff *skb)
645862306a36Sopenharmony_ci{
645962306a36Sopenharmony_ci	netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
646062306a36Sopenharmony_ci}
646162306a36Sopenharmony_ci
646262306a36Sopenharmony_cistatic int rtnetlink_bind(struct net *net, int group)
646362306a36Sopenharmony_ci{
646462306a36Sopenharmony_ci	switch (group) {
646562306a36Sopenharmony_ci	case RTNLGRP_IPV4_MROUTE_R:
646662306a36Sopenharmony_ci	case RTNLGRP_IPV6_MROUTE_R:
646762306a36Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
646862306a36Sopenharmony_ci			return -EPERM;
646962306a36Sopenharmony_ci		break;
647062306a36Sopenharmony_ci	}
647162306a36Sopenharmony_ci	return 0;
647262306a36Sopenharmony_ci}
647362306a36Sopenharmony_ci
647462306a36Sopenharmony_cistatic int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
647562306a36Sopenharmony_ci{
647662306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
647762306a36Sopenharmony_ci
647862306a36Sopenharmony_ci	switch (event) {
647962306a36Sopenharmony_ci	case NETDEV_REBOOT:
648062306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
648162306a36Sopenharmony_ci	case NETDEV_CHANGEADDR:
648262306a36Sopenharmony_ci	case NETDEV_CHANGENAME:
648362306a36Sopenharmony_ci	case NETDEV_FEAT_CHANGE:
648462306a36Sopenharmony_ci	case NETDEV_BONDING_FAILOVER:
648562306a36Sopenharmony_ci	case NETDEV_POST_TYPE_CHANGE:
648662306a36Sopenharmony_ci	case NETDEV_NOTIFY_PEERS:
648762306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
648862306a36Sopenharmony_ci	case NETDEV_RESEND_IGMP:
648962306a36Sopenharmony_ci	case NETDEV_CHANGEINFODATA:
649062306a36Sopenharmony_ci	case NETDEV_CHANGELOWERSTATE:
649162306a36Sopenharmony_ci	case NETDEV_CHANGE_TX_QUEUE_LEN:
649262306a36Sopenharmony_ci		rtmsg_ifinfo_event(RTM_NEWLINK, dev, 0, rtnl_get_event(event),
649362306a36Sopenharmony_ci				   GFP_KERNEL, NULL, 0, 0, NULL);
649462306a36Sopenharmony_ci		break;
649562306a36Sopenharmony_ci	default:
649662306a36Sopenharmony_ci		break;
649762306a36Sopenharmony_ci	}
649862306a36Sopenharmony_ci	return NOTIFY_DONE;
649962306a36Sopenharmony_ci}
650062306a36Sopenharmony_ci
650162306a36Sopenharmony_cistatic struct notifier_block rtnetlink_dev_notifier = {
650262306a36Sopenharmony_ci	.notifier_call	= rtnetlink_event,
650362306a36Sopenharmony_ci};
650462306a36Sopenharmony_ci
650562306a36Sopenharmony_ci
650662306a36Sopenharmony_cistatic int __net_init rtnetlink_net_init(struct net *net)
650762306a36Sopenharmony_ci{
650862306a36Sopenharmony_ci	struct sock *sk;
650962306a36Sopenharmony_ci	struct netlink_kernel_cfg cfg = {
651062306a36Sopenharmony_ci		.groups		= RTNLGRP_MAX,
651162306a36Sopenharmony_ci		.input		= rtnetlink_rcv,
651262306a36Sopenharmony_ci		.cb_mutex	= &rtnl_mutex,
651362306a36Sopenharmony_ci		.flags		= NL_CFG_F_NONROOT_RECV,
651462306a36Sopenharmony_ci		.bind		= rtnetlink_bind,
651562306a36Sopenharmony_ci	};
651662306a36Sopenharmony_ci
651762306a36Sopenharmony_ci	sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);
651862306a36Sopenharmony_ci	if (!sk)
651962306a36Sopenharmony_ci		return -ENOMEM;
652062306a36Sopenharmony_ci	net->rtnl = sk;
652162306a36Sopenharmony_ci	return 0;
652262306a36Sopenharmony_ci}
652362306a36Sopenharmony_ci
652462306a36Sopenharmony_cistatic void __net_exit rtnetlink_net_exit(struct net *net)
652562306a36Sopenharmony_ci{
652662306a36Sopenharmony_ci	netlink_kernel_release(net->rtnl);
652762306a36Sopenharmony_ci	net->rtnl = NULL;
652862306a36Sopenharmony_ci}
652962306a36Sopenharmony_ci
653062306a36Sopenharmony_cistatic struct pernet_operations rtnetlink_net_ops = {
653162306a36Sopenharmony_ci	.init = rtnetlink_net_init,
653262306a36Sopenharmony_ci	.exit = rtnetlink_net_exit,
653362306a36Sopenharmony_ci};
653462306a36Sopenharmony_ci
653562306a36Sopenharmony_civoid __init rtnetlink_init(void)
653662306a36Sopenharmony_ci{
653762306a36Sopenharmony_ci	if (register_pernet_subsys(&rtnetlink_net_ops))
653862306a36Sopenharmony_ci		panic("rtnetlink_init: cannot initialize rtnetlink\n");
653962306a36Sopenharmony_ci
654062306a36Sopenharmony_ci	register_netdevice_notifier(&rtnetlink_dev_notifier);
654162306a36Sopenharmony_ci
654262306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink,
654362306a36Sopenharmony_ci		      rtnl_dump_ifinfo, 0);
654462306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, 0);
654562306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, 0);
654662306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, 0);
654762306a36Sopenharmony_ci
654862306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, 0);
654962306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, 0);
655062306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETNETCONF, NULL, rtnl_dump_all, 0);
655162306a36Sopenharmony_ci
655262306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_NEWLINKPROP, rtnl_newlinkprop, NULL, 0);
655362306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_DELLINKPROP, rtnl_dellinkprop, NULL, 0);
655462306a36Sopenharmony_ci
655562306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, 0);
655662306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL,
655762306a36Sopenharmony_ci		      RTNL_FLAG_BULK_DEL_SUPPORTED);
655862306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_GETNEIGH, rtnl_fdb_get, rtnl_fdb_dump, 0);
655962306a36Sopenharmony_ci
656062306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, 0);
656162306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, 0);
656262306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, 0);
656362306a36Sopenharmony_ci
656462306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETSTATS, rtnl_stats_get, rtnl_stats_dump,
656562306a36Sopenharmony_ci		      0);
656662306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_SETSTATS, rtnl_stats_set, NULL, 0);
656762306a36Sopenharmony_ci
656862306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, rtnl_mdb_dump, 0);
656962306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_NEWMDB, rtnl_mdb_add, NULL, 0);
657062306a36Sopenharmony_ci	rtnl_register(PF_BRIDGE, RTM_DELMDB, rtnl_mdb_del, NULL, 0);
657162306a36Sopenharmony_ci}
6572