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