// SPDX-License-Identifier: GPL-2.0-or-later
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* Routing netlink socket interface: protocol independent part.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
* Fixes:
* Vitaly E. Lavrov RTA_OK arithmetic was wrong.
*/
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/capability.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/security.h>
#include <linux/mutex.h>
#include <linux/if_addr.h>
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <linux/bpf.h>
#include <linux/uaccess.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/arp.h>
#include <net/route.h>
#include <net/udp.h>
#include <net/tcp.h>
#include <net/sock.h>
#include <net/pkt_sched.h>
#include <net/fib_rules.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/netdev_lock.h>
#include <net/devlink.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/addrconf.h>
#endif
#include <linux/dpll.h>
#include "dev.h"
#define RTNL_MAX_TYPE 50
#define RTNL_SLAVE_MAX_TYPE 44
struct rtnl_link {
rtnl_doit_func doit;
rtnl_dumpit_func dumpit;
struct module *owner;
unsigned int flags;
struct rcu_head rcu;
};
static DEFINE_MUTEX(rtnl_mutex);
void rtnl_lock(void )
{
mutex_lock(&rtnl_mutex);
}
EXPORT_SYMBOL(rtnl_lock);
int rtnl_lock_interruptible(void )
{
return mutex_lock_interruptible(&rtnl_mutex);
}
int rtnl_lock_killable(void )
{
return mutex_lock_killable(&rtnl_mutex);
}
static struct sk_buff *defer_kfree_skb_list;
void rtnl_kfree_skbs(struct sk_buff *head, struct sk_buff *tail)
{
if (head && tail) {
tail->next = defer_kfree_skb_list;
defer_kfree_skb_list = head;
}
}
EXPORT_SYMBOL(rtnl_kfree_skbs);
void __rtnl_unlock(void )
{
struct sk_buff *head = defer_kfree_skb_list;
defer_kfree_skb_list = NULL;
/* Ensure that we didn't actually add any TODO item when __rtnl_unlock()
* is used. In some places, e.g. in cfg80211, we have code that will do
* something like
* rtnl_lock()
* wiphy_lock()
* ...
* rtnl_unlock()
*
* and because netdev_run_todo() acquires the RTNL for items on the list
* we could cause a situation such as this:
* Thread 1 Thread 2
* rtnl_lock()
* unregister_netdevice()
* __rtnl_unlock()
* rtnl_lock()
* wiphy_lock()
* rtnl_unlock()
* netdev_run_todo()
* __rtnl_unlock()
*
* // list not empty now
* // because of thread 2
* rtnl_lock()
* while (!list_empty(...))
* rtnl_lock()
* wiphy_lock()
* **** DEADLOCK ****
*
* However, usage of __rtnl_unlock() is rare, and so we can ensure that
* it's not used in cases where something is added to do the list.
*/
WARN_ON(!list_empty(&net_todo_list));
mutex_unlock(&rtnl_mutex);
while (head) {
struct sk_buff *next = head->next;
kfree_skb(head);
cond_resched();
head = next;
}
}
void rtnl_unlock(void )
{
/* This fellow will unlock it for us. */
netdev_run_todo();
}
EXPORT_SYMBOL(rtnl_unlock);
int rtnl_trylock(void )
{
return mutex_trylock(&rtnl_mutex);
}
EXPORT_SYMBOL(rtnl_trylock);
int rtnl_is_locked(void )
{
return mutex_is_locked(&rtnl_mutex);
}
EXPORT_SYMBOL(rtnl_is_locked);
bool refcount_dec_and_rtnl_lock(refcount_t *r)
{
return refcount_dec_and_mutex_lock(r, &rtnl_mutex);
}
EXPORT_SYMBOL(refcount_dec_and_rtnl_lock);
#ifdef CONFIG_PROVE_LOCKING
bool lockdep_rtnl_is_held(void )
{
return lockdep_is_held(&rtnl_mutex);
}
EXPORT_SYMBOL(lockdep_rtnl_is_held);
#endif /* #ifdef CONFIG_PROVE_LOCKING */
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
void __rtnl_net_lock(struct net *net)
{
ASSERT_RTNL();
mutex_lock(&net->rtnl_mutex);
}
EXPORT_SYMBOL(__rtnl_net_lock);
void __rtnl_net_unlock(struct net *net)
{
ASSERT_RTNL();
mutex_unlock(&net->rtnl_mutex);
}
EXPORT_SYMBOL(__rtnl_net_unlock);
void rtnl_net_lock(struct net *net)
{
rtnl_lock();
__rtnl_net_lock(net);
}
EXPORT_SYMBOL(rtnl_net_lock);
void rtnl_net_unlock(struct net *net)
{
__rtnl_net_unlock(net);
rtnl_unlock();
}
EXPORT_SYMBOL(rtnl_net_unlock);
int rtnl_net_trylock(struct net *net)
{
int ret = rtnl_trylock();
if (ret)
__rtnl_net_lock(net);
return ret;
}
EXPORT_SYMBOL(rtnl_net_trylock);
int rtnl_net_lock_killable(struct net *net)
{
int ret = rtnl_lock_killable();
if (!ret)
__rtnl_net_lock(net);
return ret;
}
static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b)
{
if (net_eq(net_a, net_b))
return 0;
/* always init_net first */
if (net_eq(net_a, &init_net))
return -1;
if (net_eq(net_b, &init_net))
return 1;
/* otherwise lock in ascending order */
return net_a < net_b ? -1 : 1;
}
int rtnl_net_lock_cmp_fn(const struct lockdep_map *a, const struct lockdep_map *b)
{
const struct net *net_a, *net_b;
net_a = container_of(a, struct net, rtnl_mutex.dep_map);
net_b = container_of(b, struct net, rtnl_mutex.dep_map);
return rtnl_net_cmp_locks(net_a, net_b);
}
bool rtnl_net_is_locked(struct net *net)
{
return rtnl_is_locked() && mutex_is_locked(&net->rtnl_mutex);
}
EXPORT_SYMBOL(rtnl_net_is_locked);
bool lockdep_rtnl_net_is_held(struct net *net)
{
return lockdep_rtnl_is_held() && lockdep_is_held(&net->rtnl_mutex);
}
EXPORT_SYMBOL(lockdep_rtnl_net_is_held);
#else
static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b)
{
/* No need to swap */
return -1;
}
#endif
struct rtnl_nets {
/* ->newlink() needs to freeze 3 netns at most;
* 2 for the new device, 1 for its peer.
*/
struct net *net[3];
unsigned char len;
};
static void rtnl_nets_init(struct rtnl_nets *rtnl_nets)
{
memset(rtnl_nets, 0, sizeof (*rtnl_nets));
}
static void rtnl_nets_destroy(struct rtnl_nets *rtnl_nets)
{
int i;
for (i = 0; i < rtnl_nets->len; i++) {
put_net(rtnl_nets->net[i]);
rtnl_nets->net[i] = NULL;
}
rtnl_nets->len = 0;
}
/**
* rtnl_nets_add - Add netns to be locked before ->newlink().
*
* @rtnl_nets: rtnl_nets pointer passed to ->get_peer_net().
* @net: netns pointer with an extra refcnt held.
*
* The extra refcnt is released in rtnl_nets_destroy().
*/
static void rtnl_nets_add(struct rtnl_nets *rtnl_nets, struct net *net)
{
int i;
DEBUG_NET_WARN_ON_ONCE(rtnl_nets->len == ARRAY_SIZE(rtnl_nets->net));
for (i = 0; i < rtnl_nets->len; i++) {
switch (rtnl_net_cmp_locks(rtnl_nets->net[i], net)) {
case 0:
put_net(net);
return ;
case 1:
swap(rtnl_nets->net[i], net);
}
}
rtnl_nets->net[i] = net;
rtnl_nets->len++;
}
static void rtnl_nets_lock(struct rtnl_nets *rtnl_nets)
{
int i;
rtnl_lock();
for (i = 0; i < rtnl_nets->len; i++)
__rtnl_net_lock(rtnl_nets->net[i]);
}
static void rtnl_nets_unlock(struct rtnl_nets *rtnl_nets)
{
int i;
for (i = 0; i < rtnl_nets->len; i++)
__rtnl_net_unlock(rtnl_nets->net[i]);
rtnl_unlock();
}
static struct rtnl_link __rcu *__rcu *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];
static inline int rtm_msgindex(int msgtype)
{
int msgindex = msgtype - RTM_BASE;
/*
* msgindex < 0 implies someone tried to register a netlink
* control code. msgindex >= RTM_NR_MSGTYPES may indicate that
* the message type has not been added to linux/rtnetlink.h
*/
BUG_ON(msgindex < 0 || msgindex >= RTM_NR_MSGTYPES);
return msgindex;
}
static struct rtnl_link *rtnl_get_link(int protocol, int msgtype)
{
struct rtnl_link __rcu **tab;
if (protocol >= ARRAY_SIZE(rtnl_msg_handlers))
protocol = PF_UNSPEC;
tab = rcu_dereference_rtnl(rtnl_msg_handlers[protocol]);
if (!tab)
tab = rcu_dereference_rtnl(rtnl_msg_handlers[PF_UNSPEC]);
return rcu_dereference_rtnl(tab[msgtype]);
}
static int rtnl_register_internal(struct module *owner,
int protocol, int msgtype,
rtnl_doit_func doit, rtnl_dumpit_func dumpit,
unsigned int flags)
{
struct rtnl_link *link, *old;
struct rtnl_link __rcu **tab;
int msgindex;
int ret = -ENOBUFS;
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
msgindex = rtm_msgindex(msgtype);
rtnl_lock();
tab = rtnl_dereference(rtnl_msg_handlers[protocol]);
if (tab == NULL) {
tab = kcalloc(RTM_NR_MSGTYPES, sizeof (void *), GFP_KERNEL);
if (!tab)
goto unlock;
/* ensures we see the 0 stores */
rcu_assign_pointer(rtnl_msg_handlers[protocol], tab);
}
old = rtnl_dereference(tab[msgindex]);
if (old) {
link = kmemdup(old, sizeof (*old), GFP_KERNEL);
if (!link)
goto unlock;
} else {
link = kzalloc(sizeof (*link), GFP_KERNEL);
if (!link)
goto unlock;
}
WARN_ON(link->owner && link->owner != owner);
link->owner = owner;
WARN_ON(doit && link->doit && link->doit != doit);
if (doit)
link->doit = doit;
WARN_ON(dumpit && link->dumpit && link->dumpit != dumpit);
if (dumpit)
link->dumpit = dumpit;
WARN_ON(rtnl_msgtype_kind(msgtype) != RTNL_KIND_DEL &&
(flags & RTNL_FLAG_BULK_DEL_SUPPORTED));
link->flags |= flags;
/* publish protocol:msgtype */
rcu_assign_pointer(tab[msgindex], link);
ret = 0;
if (old)
kfree_rcu(old, rcu);
unlock:
rtnl_unlock();
return ret;
}
/**
* rtnl_unregister - Unregister a rtnetlink message type
* @protocol: Protocol family or PF_UNSPEC
* @msgtype: rtnetlink message type
*
* Returns 0 on success or a negative error code.
*/
static int rtnl_unregister(int protocol, int msgtype)
{
struct rtnl_link __rcu **tab;
struct rtnl_link *link;
int msgindex;
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
msgindex = rtm_msgindex(msgtype);
rtnl_lock();
tab = rtnl_dereference(rtnl_msg_handlers[protocol]);
if (!tab) {
rtnl_unlock();
return -ENOENT;
}
link = rcu_replace_pointer_rtnl(tab[msgindex], NULL);
rtnl_unlock();
kfree_rcu(link, rcu);
return 0;
}
/**
* rtnl_unregister_all - Unregister all rtnetlink message type of a protocol
* @protocol : Protocol family or PF_UNSPEC
*
* Identical to calling rtnl_unregster() for all registered message types
* of a certain protocol family.
*/
void rtnl_unregister_all(int protocol)
{
struct rtnl_link __rcu **tab;
struct rtnl_link *link;
int msgindex;
BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX);
rtnl_lock();
tab = rcu_replace_pointer_rtnl(rtnl_msg_handlers[protocol], NULL);
if (!tab) {
rtnl_unlock();
return ;
}
for (msgindex = 0; msgindex < RTM_NR_MSGTYPES; msgindex++) {
link = rcu_replace_pointer_rtnl(tab[msgindex], NULL);
kfree_rcu(link, rcu);
}
rtnl_unlock();
synchronize_net();
kfree(tab);
}
EXPORT_SYMBOL_GPL(rtnl_unregister_all);
/**
* __rtnl_register_many - Register rtnetlink message types
* @handlers: Array of struct rtnl_msg_handlers
* @n: The length of @handlers
*
* Registers the specified function pointers (at least one of them has
* to be non-NULL) to be called whenever a request message for the
* specified protocol family and message type is received.
*
* The special protocol family PF_UNSPEC may be used to define fallback
* function pointers for the case when no entry for the specific protocol
* family exists.
*
* When one element of @handlers fails to register,
* 1) built-in: panics.
* 2) modules : the previous successful registrations are unwinded
* and an error is returned.
*
* Use rtnl_register_many().
*/
int __rtnl_register_many(const struct rtnl_msg_handler *handlers, int n)
{
const struct rtnl_msg_handler *handler;
int i, err;
for (i = 0, handler = handlers; i < n; i++, handler++) {
err = rtnl_register_internal(handler->owner, handler->protocol,
handler->msgtype, handler->doit,
handler->dumpit, handler->flags);
if (err) {
if (!handler->owner)
panic("Unable to register rtnetlink message "
"handlers, %pS\n" , handlers);
__rtnl_unregister_many(handlers, i);
break ;
}
}
return err;
}
EXPORT_SYMBOL_GPL(__rtnl_register_many);
void __rtnl_unregister_many(const struct rtnl_msg_handler *handlers, int n)
{
const struct rtnl_msg_handler *handler;
int i;
for (i = n - 1, handler = handlers + n - 1; i >= 0; i--, handler--)
rtnl_unregister(handler->protocol, handler->msgtype);
}
EXPORT_SYMBOL_GPL(__rtnl_unregister_many);
static DEFINE_MUTEX(link_ops_mutex);
static LIST_HEAD(link_ops);
static struct rtnl_link_ops *rtnl_link_ops_get(const char *kind, int *srcu_index)
{
struct rtnl_link_ops *ops;
rcu_read_lock();
list_for_each_entry_rcu(ops, &link_ops, list) {
if (!strcmp(ops->kind, kind)) {
*srcu_index = srcu_read_lock(&ops->srcu);
goto unlock;
}
}
ops = NULL;
unlock:
rcu_read_unlock();
return ops;
}
static void rtnl_link_ops_put(struct rtnl_link_ops *ops, int srcu_index)
{
srcu_read_unlock(&ops->srcu, srcu_index);
}
/**
* rtnl_link_register - Register rtnl_link_ops with rtnetlink.
* @ops: struct rtnl_link_ops * to register
*
* Returns 0 on success or a negative error code.
*/
int rtnl_link_register(struct rtnl_link_ops *ops)
{
struct rtnl_link_ops *tmp;
int err;
/* Sanity-check max sizes to avoid stack buffer overflow. */
if (WARN_ON(ops->maxtype > RTNL_MAX_TYPE ||
ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE))
return -EINVAL;
/* The check for alloc/setup is here because if ops
* does not have that filled up, it is not possible
* to use the ops for creating device. So do not
* fill up dellink as well. That disables rtnl_dellink.
*/
if ((ops->alloc || ops->setup) && !ops->dellink)
ops->dellink = unregister_netdevice_queue;
err = init_srcu_struct(&ops->srcu);
if (err)
return err;
mutex_lock(&link_ops_mutex);
list_for_each_entry(tmp, &link_ops, list) {
if (!strcmp(ops->kind, tmp->kind)) {
err = -EEXIST;
goto unlock;
}
}
list_add_tail_rcu(&ops->list, &link_ops);
unlock:
mutex_unlock(&link_ops_mutex);
return err;
}
EXPORT_SYMBOL_GPL(rtnl_link_register);
static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops)
{
struct net_device *dev;
LIST_HEAD(list_kill);
for_each_netdev(net, dev) {
if (dev->rtnl_link_ops == ops)
ops->dellink(dev, &list_kill);
}
unregister_netdevice_many(&list_kill);
}
/* Return with the rtnl_lock held when there are no network
* devices unregistering in any network namespace.
*/
static void rtnl_lock_unregistering_all(void )
{
DEFINE_WAIT_FUNC(wait, woken_wake_function);
add_wait_queue(&netdev_unregistering_wq, &wait);
for (;;) {
rtnl_lock();
/* We held write locked pernet_ops_rwsem, and parallel
* setup_net() and cleanup_net() are not possible.
*/
if (!atomic_read(&dev_unreg_count))
break ;
__rtnl_unlock();
wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
remove_wait_queue(&netdev_unregistering_wq, &wait);
}
/**
* rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink.
* @ops: struct rtnl_link_ops * to unregister
*/
void rtnl_link_unregister(struct rtnl_link_ops *ops)
{
struct net *net;
mutex_lock(&link_ops_mutex);
list_del_rcu(&ops->list);
mutex_unlock(&link_ops_mutex);
synchronize_srcu(&ops->srcu);
cleanup_srcu_struct(&ops->srcu);
/* Close the race with setup_net() and cleanup_net() */
down_write(&pernet_ops_rwsem);
rtnl_lock_unregistering_all();
for_each_net(net)
__rtnl_kill_links(net, ops);
rtnl_unlock();
up_write(&pernet_ops_rwsem);
}
EXPORT_SYMBOL_GPL(rtnl_link_unregister);
static size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev)
{
struct net_device *master_dev;
const struct rtnl_link_ops *ops;
size_t size = 0;
rcu_read_lock();
master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
if (!master_dev)
goto out;
ops = master_dev->rtnl_link_ops;
if (!ops || !ops->get_slave_size)
goto out;
/* IFLA_INFO_SLAVE_DATA + nested data */
size = nla_total_size(sizeof (struct nlattr)) +
ops->get_slave_size(master_dev, dev);
out:
rcu_read_unlock();
return size;
}
static size_t rtnl_link_get_size(const struct net_device *dev)
{
const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
size_t size;
if (!ops)
return 0;
size = nla_total_size(sizeof (struct nlattr)) + /* IFLA_LINKINFO */
nla_total_size(strlen(ops->kind) + 1); /* IFLA_INFO_KIND */
if (ops->get_size)
/* IFLA_INFO_DATA + nested data */
size += nla_total_size(sizeof (struct nlattr)) +
ops->get_size(dev);
if (ops->get_xstats_size)
/* IFLA_INFO_XSTATS */
size += nla_total_size(ops->get_xstats_size(dev));
size += rtnl_link_get_slave_info_data_size(dev);
return size;
}
static LIST_HEAD(rtnl_af_ops);
static struct rtnl_af_ops *rtnl_af_lookup(const int family, int *srcu_index)
{
struct rtnl_af_ops *ops;
ASSERT_RTNL();
rcu_read_lock();
list_for_each_entry_rcu(ops, &rtnl_af_ops, list) {
if (ops->family == family) {
*srcu_index = srcu_read_lock(&ops->srcu);
goto unlock;
}
}
ops = NULL;
unlock:
rcu_read_unlock();
return ops;
}
static void rtnl_af_put(struct rtnl_af_ops *ops, int srcu_index)
{
srcu_read_unlock(&ops->srcu, srcu_index);
}
/**
* rtnl_af_register - Register rtnl_af_ops with rtnetlink.
* @ops: struct rtnl_af_ops * to register
*
* Return: 0 on success or a negative error code.
*/
int rtnl_af_register(struct rtnl_af_ops *ops)
{
int err = init_srcu_struct(&ops->srcu);
if (err)
return err;
rtnl_lock();
list_add_tail_rcu(&ops->list, &rtnl_af_ops);
rtnl_unlock();
return 0;
}
EXPORT_SYMBOL_GPL(rtnl_af_register);
/**
* rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink.
* @ops: struct rtnl_af_ops * to unregister
*/
void rtnl_af_unregister(struct rtnl_af_ops *ops)
{
rtnl_lock();
list_del_rcu(&ops->list);
rtnl_unlock();
synchronize_rcu();
synchronize_srcu(&ops->srcu);
cleanup_srcu_struct(&ops->srcu);
}
EXPORT_SYMBOL_GPL(rtnl_af_unregister);
static size_t rtnl_link_get_af_size(const struct net_device *dev,
u32 ext_filter_mask)
{
struct rtnl_af_ops *af_ops;
size_t size;
/* IFLA_AF_SPEC */
size = nla_total_size(sizeof (struct nlattr));
rcu_read_lock();
list_for_each_entry_rcu(af_ops, &rtnl_af_ops, list) {
if (af_ops->get_link_af_size) {
/* AF_* + nested data */
size += nla_total_size(sizeof (struct nlattr)) +
af_ops->get_link_af_size(dev, ext_filter_mask);
}
}
rcu_read_unlock();
return size;
}
static bool rtnl_have_link_slave_info(const struct net_device *dev)
{
struct net_device *master_dev;
bool ret = false ;
rcu_read_lock();
master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev);
if (master_dev && master_dev->rtnl_link_ops)
ret = true ;
rcu_read_unlock();
return ret;
}
static int rtnl_link_slave_info_fill(struct sk_buff *skb,
const struct net_device *dev)
{
struct net_device *master_dev;
const struct rtnl_link_ops *ops;
struct nlattr *slave_data;
int err;
master_dev = netdev_master_upper_dev_get((struct net_device *) dev);
if (!master_dev)
return 0;
ops = master_dev->rtnl_link_ops;
if (!ops)
return 0;
if (nla_put_string(skb, IFLA_INFO_SLAVE_KIND, ops->kind) < 0)
return -EMSGSIZE;
if (ops->fill_slave_info) {
slave_data = nla_nest_start_noflag(skb, IFLA_INFO_SLAVE_DATA);
if (!slave_data)
return -EMSGSIZE;
err = ops->fill_slave_info(skb, master_dev, dev);
if (err < 0)
goto err_cancel_slave_data;
nla_nest_end(skb, slave_data);
}
return 0;
err_cancel_slave_data:
nla_nest_cancel(skb, slave_data);
return err;
}
static int rtnl_link_info_fill(struct sk_buff *skb,
const struct net_device *dev)
{
const struct rtnl_link_ops *ops = dev->rtnl_link_ops;
struct nlattr *data;
int err;
if (!ops)
return 0;
if (nla_put_string(skb, IFLA_INFO_KIND, ops->kind) < 0)
return -EMSGSIZE;
if (ops->fill_xstats) {
err = ops->fill_xstats(skb, dev);
if (err < 0)
return err;
}
if (ops->fill_info) {
data = nla_nest_start_noflag(skb, IFLA_INFO_DATA);
if (data == NULL)
return -EMSGSIZE;
err = ops->fill_info(skb, dev);
if (err < 0)
goto err_cancel_data;
nla_nest_end(skb, data);
}
return 0;
err_cancel_data:
nla_nest_cancel(skb, data);
return err;
}
static int rtnl_link_fill(struct sk_buff *skb, const struct net_device *dev)
{
struct nlattr *linkinfo;
int err = -EMSGSIZE;
linkinfo = nla_nest_start_noflag(skb, IFLA_LINKINFO);
if (linkinfo == NULL)
goto out;
err = rtnl_link_info_fill(skb, dev);
if (err < 0)
goto err_cancel_link;
err = rtnl_link_slave_info_fill(skb, dev);
if (err < 0)
goto err_cancel_link;
nla_nest_end(skb, linkinfo);
return 0;
err_cancel_link:
nla_nest_cancel(skb, linkinfo);
out:
return err;
}
int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned int group, int echo)
{
struct sock *rtnl = net->rtnl;
return nlmsg_notify(rtnl, skb, pid, group, echo, GFP_KERNEL);
}
int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid)
{
struct sock *rtnl = net->rtnl;
return nlmsg_unicast(rtnl, skb, pid);
}
EXPORT_SYMBOL(rtnl_unicast);
void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
const struct nlmsghdr *nlh, gfp_t flags)
{
struct sock *rtnl = net->rtnl;
nlmsg_notify(rtnl, skb, pid, group, nlmsg_report(nlh), flags);
}
EXPORT_SYMBOL(rtnl_notify);
void rtnl_set_sk_err(struct net *net, u32 group, int error)
{
struct sock *rtnl = net->rtnl;
netlink_set_err(rtnl, 0, group, error);
}
EXPORT_SYMBOL(rtnl_set_sk_err);
int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics)
{
struct nlattr *mx;
int i, valid = 0;
/* nothing is dumped for dst_default_metrics, so just skip the loop */
if (metrics == dst_default_metrics.metrics)
return 0;
mx = nla_nest_start_noflag(skb, RTA_METRICS);
if (mx == NULL)
return -ENOBUFS;
for (i = 0; i < RTAX_MAX; i++) {
if (metrics[i]) {
if (i == RTAX_CC_ALGO - 1) {
char tmp[TCP_CA_NAME_MAX], *name;
name = tcp_ca_get_name_by_key(metrics[i], tmp);
if (!name)
continue ;
if (nla_put_string(skb, i + 1, name))
goto nla_put_failure;
} else if (i == RTAX_FEATURES - 1) {
u32 user_features = metrics[i] & RTAX_FEATURE_MASK;
if (!user_features)
continue ;
BUILD_BUG_ON(RTAX_FEATURE_MASK & DST_FEATURE_MASK);
if (nla_put_u32(skb, i + 1, user_features))
goto nla_put_failure;
} else {
if (nla_put_u32(skb, i + 1, metrics[i]))
goto nla_put_failure;
}
valid++;
}
}
if (!valid) {
nla_nest_cancel(skb, mx);
return 0;
}
return nla_nest_end(skb, mx);
nla_put_failure:
nla_nest_cancel(skb, mx);
return -EMSGSIZE;
}
EXPORT_SYMBOL(rtnetlink_put_metrics);
int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id,
long expires, u32 error)
{
struct rta_cacheinfo ci = {
.rta_error = error,
.rta_id = id,
};
unsigned long delta;
if (dst) {
delta = jiffies - READ_ONCE(dst->lastuse);
ci.rta_lastuse = jiffies_delta_to_clock_t(delta);
ci.rta_used = dst->__use;
ci.rta_clntref = rcuref_read(&dst->__rcuref);
}
if (expires) {
unsigned long clock;
clock = jiffies_to_clock_t(abs(expires));
clock = min_t(unsigned long , clock, INT_MAX);
ci.rta_expires = (expires > 0) ? clock : -clock;
}
return nla_put(skb, RTA_CACHEINFO, sizeof (ci), &ci);
}
EXPORT_SYMBOL_GPL(rtnl_put_cacheinfo);
void netif_set_operstate(struct net_device *dev, int newstate)
{
unsigned int old = READ_ONCE(dev->operstate);
do {
if (old == newstate)
return ;
} while (!try_cmpxchg(&dev->operstate, &old, newstate));
netif_state_change(dev);
}
EXPORT_SYMBOL(netif_set_operstate);
static void set_operstate(struct net_device *dev, unsigned char transition)
{
unsigned char operstate = READ_ONCE(dev->operstate);
switch (transition) {
case IF_OPER_UP:
if ((operstate == IF_OPER_DORMANT ||
operstate == IF_OPER_TESTING ||
operstate == IF_OPER_UNKNOWN) &&
!netif_dormant(dev) && !netif_testing(dev))
operstate = IF_OPER_UP;
break ;
case IF_OPER_TESTING:
if (netif_oper_up(dev))
operstate = IF_OPER_TESTING;
break ;
case IF_OPER_DORMANT:
if (netif_oper_up(dev))
operstate = IF_OPER_DORMANT;
break ;
}
netif_set_operstate(dev, operstate);
}
static unsigned int rtnl_dev_get_flags(const struct net_device *dev)
{
return (dev->flags & ~(IFF_PROMISC | IFF_ALLMULTI)) |
(dev->gflags & (IFF_PROMISC | IFF_ALLMULTI));
}
static unsigned int rtnl_dev_combine_flags(const struct net_device *dev,
const struct ifinfomsg *ifm)
{
unsigned int flags = ifm->ifi_flags;
/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
if (ifm->ifi_change)
flags = (flags & ifm->ifi_change) |
(rtnl_dev_get_flags(dev) & ~ifm->ifi_change);
return flags;
}
static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
const struct rtnl_link_stats64 *b)
{
a->rx_packets = b->rx_packets;
a->tx_packets = b->tx_packets;
a->rx_bytes = b->rx_bytes;
a->tx_bytes = b->tx_bytes;
a->rx_errors = b->rx_errors;
a->tx_errors = b->tx_errors;
a->rx_dropped = b->rx_dropped;
a->tx_dropped = b->tx_dropped;
a->multicast = b->multicast;
a->collisions = b->collisions;
a->rx_length_errors = b->rx_length_errors;
a->rx_over_errors = b->rx_over_errors;
a->rx_crc_errors = b->rx_crc_errors;
a->rx_frame_errors = b->rx_frame_errors;
a->rx_fifo_errors = b->rx_fifo_errors;
a->rx_missed_errors = b->rx_missed_errors;
a->tx_aborted_errors = b->tx_aborted_errors;
a->tx_carrier_errors = b->tx_carrier_errors;
a->tx_fifo_errors = b->tx_fifo_errors;
a->tx_heartbeat_errors = b->tx_heartbeat_errors;
a->tx_window_errors = b->tx_window_errors;
a->rx_compressed = b->rx_compressed;
a->tx_compressed = b->tx_compressed;
a->rx_nohandler = b->rx_nohandler;
}
/* All VF info */
static inline int rtnl_vfinfo_size(const struct net_device *dev,
u32 ext_filter_mask)
{
if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF)) {
int num_vfs = dev_num_vf(dev->dev.parent);
size_t size = nla_total_size(0);
size += num_vfs *
(nla_total_size(0) +
nla_total_size(sizeof (struct ifla_vf_mac)) +
nla_total_size(sizeof (struct ifla_vf_broadcast)) +
nla_total_size(sizeof (struct ifla_vf_vlan)) +
nla_total_size(0) + /* nest IFLA_VF_VLAN_LIST */
nla_total_size(MAX_VLAN_LIST_LEN *
sizeof (struct ifla_vf_vlan_info)) +
nla_total_size(sizeof (struct ifla_vf_spoofchk)) +
nla_total_size(sizeof (struct ifla_vf_tx_rate)) +
nla_total_size(sizeof (struct ifla_vf_rate)) +
nla_total_size(sizeof (struct ifla_vf_link_state)) +
nla_total_size(sizeof (struct ifla_vf_rss_query_en)) +
nla_total_size(sizeof (struct ifla_vf_trust)));
if (~ext_filter_mask & RTEXT_FILTER_SKIP_STATS) {
size += num_vfs *
(nla_total_size(0) + /* nest IFLA_VF_STATS */
/* IFLA_VF_STATS_RX_PACKETS */
nla_total_size_64bit(sizeof (__u64)) +
/* IFLA_VF_STATS_TX_PACKETS */
nla_total_size_64bit(sizeof (__u64)) +
/* IFLA_VF_STATS_RX_BYTES */
nla_total_size_64bit(sizeof (__u64)) +
/* IFLA_VF_STATS_TX_BYTES */
nla_total_size_64bit(sizeof (__u64)) +
/* IFLA_VF_STATS_BROADCAST */
nla_total_size_64bit(sizeof (__u64)) +
/* IFLA_VF_STATS_MULTICAST */
nla_total_size_64bit(sizeof (__u64)) +
/* IFLA_VF_STATS_RX_DROPPED */
nla_total_size_64bit(sizeof (__u64)) +
/* IFLA_VF_STATS_TX_DROPPED */
nla_total_size_64bit(sizeof (__u64)));
}
if (dev->netdev_ops->ndo_get_vf_guid)
size += num_vfs * 2 *
nla_total_size(sizeof (struct ifla_vf_guid));
return size;
} else
return 0;
}
static size_t rtnl_port_size(const struct net_device *dev,
u32 ext_filter_mask)
{
size_t port_size = nla_total_size(4) /* PORT_VF */
+ nla_total_size(PORT_PROFILE_MAX) /* PORT_PROFILE */
+ nla_total_size(PORT_UUID_MAX) /* PORT_INSTANCE_UUID */
+ nla_total_size(PORT_UUID_MAX) /* PORT_HOST_UUID */
+ nla_total_size(1) /* PROT_VDP_REQUEST */
+ nla_total_size(2); /* PORT_VDP_RESPONSE */
size_t vf_ports_size = nla_total_size(sizeof (struct nlattr));
size_t vf_port_size = nla_total_size(sizeof (struct nlattr))
+ port_size;
size_t port_self_size = nla_total_size(sizeof (struct nlattr))
+ port_size;
if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
!(ext_filter_mask & RTEXT_FILTER_VF))
return 0;
if (dev_num_vf(dev->dev.parent))
return port_self_size + vf_ports_size +
vf_port_size * dev_num_vf(dev->dev.parent);
else
return port_self_size;
}
static size_t rtnl_xdp_size(void )
{
size_t xdp_size = nla_total_size(0) + /* nest IFLA_XDP */
nla_total_size(1) + /* XDP_ATTACHED */
nla_total_size(4) + /* XDP_PROG_ID (or 1st mode) */
nla_total_size(4); /* XDP_<mode>_PROG_ID */
return xdp_size;
}
static size_t rtnl_prop_list_size(const struct net_device *dev)
{
struct netdev_name_node *name_node;
unsigned int cnt = 0;
rcu_read_lock();
list_for_each_entry_rcu(name_node, &dev->name_node->list, list)
cnt++;
rcu_read_unlock();
if (!cnt)
return 0;
return nla_total_size(0) + cnt * nla_total_size(ALTIFNAMSIZ);
}
static size_t rtnl_proto_down_size(const struct net_device *dev)
{
size_t size = nla_total_size(1);
/* Assume dev->proto_down_reason is not zero. */
size += nla_total_size(0) + nla_total_size(4);
return size;
}
static size_t rtnl_devlink_port_size(const struct net_device *dev)
{
size_t size = nla_total_size(0); /* nest IFLA_DEVLINK_PORT */
if (dev->devlink_port)
size += devlink_nl_port_handle_size(dev->devlink_port);
return size;
}
static size_t rtnl_dpll_pin_size(const struct net_device *dev)
{
size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */
size += dpll_netdev_pin_handle_size(dev);
return size;
}
static noinline size_t if_nlmsg_size(const struct net_device *dev,
u32 ext_filter_mask)
{
return NLMSG_ALIGN(sizeof (struct ifinfomsg))
+ nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
+ nla_total_size(IFALIASZ) /* IFLA_IFALIAS */
+ nla_total_size(IFNAMSIZ) /* IFLA_QDISC */
+ nla_total_size_64bit(sizeof (struct rtnl_link_ifmap))
+ nla_total_size(sizeof (struct rtnl_link_stats))
+ nla_total_size_64bit(sizeof (struct rtnl_link_stats64))
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_BROADCAST */
+ nla_total_size(4) /* IFLA_TXQLEN */
+ nla_total_size(4) /* IFLA_WEIGHT */
+ nla_total_size(4) /* IFLA_MTU */
+ nla_total_size(4) /* IFLA_LINK */
+ nla_total_size(4) /* IFLA_MASTER */
+ nla_total_size(1) /* IFLA_CARRIER */
+ nla_total_size(4) /* IFLA_PROMISCUITY */
+ nla_total_size(4) /* IFLA_ALLMULTI */
+ nla_total_size(4) /* IFLA_NUM_TX_QUEUES */
+ nla_total_size(4) /* IFLA_NUM_RX_QUEUES */
+ nla_total_size(4) /* IFLA_GSO_MAX_SEGS */
+ nla_total_size(4) /* IFLA_GSO_MAX_SIZE */
+ nla_total_size(4) /* IFLA_GRO_MAX_SIZE */
+ nla_total_size(4) /* IFLA_GSO_IPV4_MAX_SIZE */
+ nla_total_size(4) /* IFLA_GRO_IPV4_MAX_SIZE */
+ nla_total_size(4) /* IFLA_TSO_MAX_SIZE */
+ nla_total_size(4) /* IFLA_TSO_MAX_SEGS */
+ nla_total_size(1) /* IFLA_OPERSTATE */
+ nla_total_size(1) /* IFLA_LINKMODE */
+ nla_total_size(1) /* IFLA_NETNS_IMMUTABLE */
+ nla_total_size(4) /* IFLA_CARRIER_CHANGES */
+ nla_total_size(4) /* IFLA_LINK_NETNSID */
+ nla_total_size(4) /* IFLA_GROUP */
+ nla_total_size(ext_filter_mask
& RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */
+ rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */
+ rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
+ rtnl_link_get_size(dev) /* IFLA_LINKINFO */
+ rtnl_link_get_af_size(dev, ext_filter_mask) /* IFLA_AF_SPEC */
+ nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */
+ nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */
+ nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */
+ rtnl_xdp_size() /* IFLA_XDP */
+ nla_total_size(4) /* IFLA_EVENT */
+ nla_total_size(4) /* IFLA_NEW_NETNSID */
+ nla_total_size(4) /* IFLA_NEW_IFINDEX */
+ rtnl_proto_down_size(dev) /* proto down */
+ nla_total_size(4) /* IFLA_TARGET_NETNSID */
+ nla_total_size(4) /* IFLA_CARRIER_UP_COUNT */
+ nla_total_size(4) /* IFLA_CARRIER_DOWN_COUNT */
+ nla_total_size(4) /* IFLA_MIN_MTU */
+ nla_total_size(4) /* IFLA_MAX_MTU */
+ rtnl_prop_list_size(dev)
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */
+ rtnl_devlink_port_size(dev)
+ rtnl_dpll_pin_size(dev)
+ nla_total_size(8) /* IFLA_MAX_PACING_OFFLOAD_HORIZON */
+ 0;
}
static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev)
{
struct nlattr *vf_ports;
struct nlattr *vf_port;
int vf;
int err;
vf_ports = nla_nest_start_noflag(skb, IFLA_VF_PORTS);
if (!vf_ports)
return -EMSGSIZE;
for (vf = 0; vf < dev_num_vf(dev->dev.parent); vf++) {
vf_port = nla_nest_start_noflag(skb, IFLA_VF_PORT);
if (!vf_port)
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_PORT_VF, vf))
goto nla_put_failure;
err = dev->netdev_ops->ndo_get_vf_port(dev, vf, skb);
if (err == -EMSGSIZE)
goto nla_put_failure;
if (err) {
nla_nest_cancel(skb, vf_port);
continue ;
}
nla_nest_end(skb, vf_port);
}
nla_nest_end(skb, vf_ports);
return 0;
nla_put_failure:
nla_nest_cancel(skb, vf_ports);
return -EMSGSIZE;
}
static int rtnl_port_self_fill(struct sk_buff *skb, struct net_device *dev)
{
struct nlattr *port_self;
int err;
port_self = nla_nest_start_noflag(skb, IFLA_PORT_SELF);
if (!port_self)
return -EMSGSIZE;
err = dev->netdev_ops->ndo_get_vf_port(dev, PORT_SELF_VF, skb);
if (err) {
nla_nest_cancel(skb, port_self);
return (err == -EMSGSIZE) ? err : 0;
}
nla_nest_end(skb, port_self);
return 0;
}
static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev,
u32 ext_filter_mask)
{
int err;
if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent ||
!(ext_filter_mask & RTEXT_FILTER_VF))
return 0;
err = rtnl_port_self_fill(skb, dev);
if (err)
return err;
if (dev_num_vf(dev->dev.parent)) {
err = rtnl_vf_ports_fill(skb, dev);
if (err)
return err;
}
return 0;
}
static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev)
{
int err;
struct netdev_phys_item_id ppid;
err = dev_get_phys_port_id(dev, &ppid);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
if (nla_put(skb, IFLA_PHYS_PORT_ID, ppid.id_len, ppid.id))
return -EMSGSIZE;
return 0;
}
static int rtnl_phys_port_name_fill(struct sk_buff *skb, struct net_device *dev)
{
char name[IFNAMSIZ];
int err;
err = dev_get_phys_port_name(dev, name, sizeof (name));
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
if (nla_put_string(skb, IFLA_PHYS_PORT_NAME, name))
return -EMSGSIZE;
return 0;
}
static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
{
struct netdev_phys_item_id ppid = { };
int err;
err = netif_get_port_parent_id(dev, &ppid, false );
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
if (nla_put(skb, IFLA_PHYS_SWITCH_ID, ppid.id_len, ppid.id))
return -EMSGSIZE;
return 0;
}
static noinline_for_stack int rtnl_fill_stats(struct sk_buff *skb,
struct net_device *dev)
{
struct rtnl_link_stats64 *sp;
struct nlattr *attr;
attr = nla_reserve_64bit(skb, IFLA_STATS64,
sizeof (struct rtnl_link_stats64), IFLA_PAD);
if (!attr)
return -EMSGSIZE;
sp = nla_data(attr);
dev_get_stats(dev, sp);
attr = nla_reserve(skb, IFLA_STATS,
sizeof (struct rtnl_link_stats));
if (!attr)
return -EMSGSIZE;
copy_rtnl_link_stats(nla_data(attr), sp);
return 0;
}
static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
struct net_device *dev,
int vfs_num,
u32 ext_filter_mask)
{
struct ifla_vf_rss_query_en vf_rss_query_en;
struct nlattr *vf, *vfstats, *vfvlanlist;
struct ifla_vf_link_state vf_linkstate;
struct ifla_vf_vlan_info vf_vlan_info;
struct ifla_vf_spoofchk vf_spoofchk;
struct ifla_vf_tx_rate vf_tx_rate;
struct ifla_vf_stats vf_stats;
struct ifla_vf_trust vf_trust;
struct ifla_vf_vlan vf_vlan;
struct ifla_vf_rate vf_rate;
struct ifla_vf_mac vf_mac;
struct ifla_vf_broadcast vf_broadcast;
struct ifla_vf_info ivi;
struct ifla_vf_guid node_guid;
struct ifla_vf_guid port_guid;
memset(&ivi, 0, sizeof (ivi));
/* Not all SR-IOV capable drivers support the
* spoofcheck and "RSS query enable" query. Preset to
* -1 so the user space tool can detect that the driver
* didn't report anything.
*/
ivi.spoofchk = -1;
ivi.rss_query_en = -1;
ivi.trusted = -1;
/* The default value for VF link state is "auto"
* IFLA_VF_LINK_STATE_AUTO which equals zero
*/
ivi.linkstate = 0;
/* VLAN Protocol by default is 802.1Q */
ivi.vlan_proto = htons(ETH_P_8021Q);
if (dev->netdev_ops->ndo_get_vf_config(dev, vfs_num, &ivi))
return 0;
memset(&vf_vlan_info, 0, sizeof (vf_vlan_info));
memset(&node_guid, 0, sizeof (node_guid));
memset(&port_guid, 0, sizeof (port_guid));
vf_mac.vf =
vf_vlan.vf =
vf_vlan_info.vf =
vf_rate.vf =
vf_tx_rate.vf =
vf_spoofchk.vf =
vf_linkstate.vf =
vf_rss_query_en.vf =
vf_trust.vf =
node_guid.vf =
port_guid.vf = ivi.vf;
memcpy(vf_mac.mac, ivi.mac, sizeof (ivi.mac));
memcpy(vf_broadcast.broadcast, dev->broadcast, dev->addr_len);
vf_vlan.vlan = ivi.vlan;
vf_vlan.qos = ivi.qos;
vf_vlan_info.vlan = ivi.vlan;
vf_vlan_info.qos = ivi.qos;
vf_vlan_info.vlan_proto = ivi.vlan_proto;
vf_tx_rate.rate = ivi.max_tx_rate;
vf_rate.min_tx_rate = ivi.min_tx_rate;
vf_rate.max_tx_rate = ivi.max_tx_rate;
vf_spoofchk.setting = ivi.spoofchk;
vf_linkstate.link_state = ivi.linkstate;
vf_rss_query_en.setting = ivi.rss_query_en;
vf_trust.setting = ivi.trusted;
vf = nla_nest_start_noflag(skb, IFLA_VF_INFO);
if (!vf)
return -EMSGSIZE;
if (nla_put(skb, IFLA_VF_MAC, sizeof (vf_mac), &vf_mac) ||
nla_put(skb, IFLA_VF_BROADCAST, sizeof (vf_broadcast), &vf_broadcast) ||
nla_put(skb, IFLA_VF_VLAN, sizeof (vf_vlan), &vf_vlan) ||
nla_put(skb, IFLA_VF_RATE, sizeof (vf_rate),
&vf_rate) ||
nla_put(skb, IFLA_VF_TX_RATE, sizeof (vf_tx_rate),
&vf_tx_rate) ||
nla_put(skb, IFLA_VF_SPOOFCHK, sizeof (vf_spoofchk),
&vf_spoofchk) ||
nla_put(skb, IFLA_VF_LINK_STATE, sizeof (vf_linkstate),
&vf_linkstate) ||
nla_put(skb, IFLA_VF_RSS_QUERY_EN,
sizeof (vf_rss_query_en),
&vf_rss_query_en) ||
nla_put(skb, IFLA_VF_TRUST,
sizeof (vf_trust), &vf_trust))
goto nla_put_vf_failure;
if (dev->netdev_ops->ndo_get_vf_guid &&
!dev->netdev_ops->ndo_get_vf_guid(dev, vfs_num, &node_guid,
&port_guid)) {
if (nla_put(skb, IFLA_VF_IB_NODE_GUID, sizeof (node_guid),
&node_guid) ||
nla_put(skb, IFLA_VF_IB_PORT_GUID, sizeof (port_guid),
&port_guid))
goto nla_put_vf_failure;
}
vfvlanlist = nla_nest_start_noflag(skb, IFLA_VF_VLAN_LIST);
if (!vfvlanlist)
goto nla_put_vf_failure;
if (nla_put(skb, IFLA_VF_VLAN_INFO, sizeof (vf_vlan_info),
&vf_vlan_info)) {
nla_nest_cancel(skb, vfvlanlist);
goto nla_put_vf_failure;
}
nla_nest_end(skb, vfvlanlist);
if (~ext_filter_mask & RTEXT_FILTER_SKIP_STATS) {
memset(&vf_stats, 0, sizeof (vf_stats));
if (dev->netdev_ops->ndo_get_vf_stats)
dev->netdev_ops->ndo_get_vf_stats(dev, vfs_num,
&vf_stats);
vfstats = nla_nest_start_noflag(skb, IFLA_VF_STATS);
if (!vfstats)
goto nla_put_vf_failure;
if (nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_PACKETS,
vf_stats.rx_packets, IFLA_VF_STATS_PAD) ||
nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_PACKETS,
vf_stats.tx_packets, IFLA_VF_STATS_PAD) ||
nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_BYTES,
vf_stats.rx_bytes, IFLA_VF_STATS_PAD) ||
nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_BYTES,
vf_stats.tx_bytes, IFLA_VF_STATS_PAD) ||
nla_put_u64_64bit(skb, IFLA_VF_STATS_BROADCAST,
vf_stats.broadcast, IFLA_VF_STATS_PAD) ||
nla_put_u64_64bit(skb, IFLA_VF_STATS_MULTICAST,
vf_stats.multicast, IFLA_VF_STATS_PAD) ||
nla_put_u64_64bit(skb, IFLA_VF_STATS_RX_DROPPED,
vf_stats.rx_dropped, IFLA_VF_STATS_PAD) ||
nla_put_u64_64bit(skb, IFLA_VF_STATS_TX_DROPPED,
vf_stats.tx_dropped, IFLA_VF_STATS_PAD)) {
nla_nest_cancel(skb, vfstats);
goto nla_put_vf_failure;
}
nla_nest_end(skb, vfstats);
}
nla_nest_end(skb, vf);
return 0;
nla_put_vf_failure:
nla_nest_cancel(skb, vf);
return -EMSGSIZE;
}
static noinline_for_stack int rtnl_fill_vf(struct sk_buff *skb,
struct net_device *dev,
u32 ext_filter_mask)
{
struct nlattr *vfinfo;
int i, num_vfs;
if (!dev->dev.parent || ((ext_filter_mask & RTEXT_FILTER_VF) == 0))
return 0;
num_vfs = dev_num_vf(dev->dev.parent);
if (nla_put_u32(skb, IFLA_NUM_VF, num_vfs))
return -EMSGSIZE;
if (!dev->netdev_ops->ndo_get_vf_config)
return 0;
vfinfo = nla_nest_start_noflag(skb, IFLA_VFINFO_LIST);
if (!vfinfo)
return -EMSGSIZE;
for (i = 0; i < num_vfs; i++) {
if (rtnl_fill_vfinfo(skb, dev, i, ext_filter_mask)) {
nla_nest_cancel(skb, vfinfo);
return -EMSGSIZE;
}
}
nla_nest_end(skb, vfinfo);
return 0;
}
static int rtnl_fill_link_ifmap(struct sk_buff *skb,
const struct net_device *dev)
{
struct rtnl_link_ifmap map;
memset(&map, 0, sizeof (map));
map.mem_start = READ_ONCE(dev->mem_start);
map.mem_end = READ_ONCE(dev->mem_end);
map.base_addr = READ_ONCE(dev->base_addr);
map.irq = READ_ONCE(dev->irq);
map.dma = READ_ONCE(dev->dma);
map.port = READ_ONCE(dev->if_port);
if (nla_put_64bit(skb, IFLA_MAP, sizeof (map), &map, IFLA_PAD))
return -EMSGSIZE;
return 0;
}
static u32 rtnl_xdp_prog_skb(struct net_device *dev)
{
const struct bpf_prog *generic_xdp_prog;
u32 res = 0;
rcu_read_lock();
generic_xdp_prog = rcu_dereference(dev->xdp_prog);
if (generic_xdp_prog)
res = generic_xdp_prog->aux->id;
rcu_read_unlock();
return res;
}
static u32 rtnl_xdp_prog_drv(struct net_device *dev)
{
return dev_xdp_prog_id(dev, XDP_MODE_DRV);
}
static u32 rtnl_xdp_prog_hw(struct net_device *dev)
{
return dev_xdp_prog_id(dev, XDP_MODE_HW);
}
static int rtnl_xdp_report_one(struct sk_buff *skb, struct net_device *dev,
u32 *prog_id, u8 *mode, u8 tgt_mode, u32 attr,
u32 (*get_prog_id)(struct net_device *dev))
{
u32 curr_id;
int err;
curr_id = get_prog_id(dev);
if (!curr_id)
return 0;
*prog_id = curr_id;
err = nla_put_u32(skb, attr, curr_id);
if (err)
return err;
if (*mode != XDP_ATTACHED_NONE)
*mode = XDP_ATTACHED_MULTI;
else
*mode = tgt_mode;
return 0;
}
static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev)
{
struct nlattr *xdp;
u32 prog_id;
int err;
u8 mode;
xdp = nla_nest_start_noflag(skb, IFLA_XDP);
if (!xdp)
return -EMSGSIZE;
prog_id = 0;
mode = XDP_ATTACHED_NONE;
err = rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_SKB,
IFLA_XDP_SKB_PROG_ID, rtnl_xdp_prog_skb);
if (err)
goto err_cancel;
err = rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_DRV,
IFLA_XDP_DRV_PROG_ID, rtnl_xdp_prog_drv);
if (err)
goto err_cancel;
err = rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_HW,
IFLA_XDP_HW_PROG_ID, rtnl_xdp_prog_hw);
if (err)
goto err_cancel;
err = nla_put_u8(skb, IFLA_XDP_ATTACHED, mode);
if (err)
goto err_cancel;
if (prog_id && mode != XDP_ATTACHED_MULTI) {
err = nla_put_u32(skb, IFLA_XDP_PROG_ID, prog_id);
if (err)
goto err_cancel;
}
nla_nest_end(skb, xdp);
return 0;
err_cancel:
nla_nest_cancel(skb, xdp);
return err;
}
static u32 rtnl_get_event(unsigned long event)
{
u32 rtnl_event_type = IFLA_EVENT_NONE;
switch (event) {
case NETDEV_REBOOT:
rtnl_event_type = IFLA_EVENT_REBOOT;
break ;
case NETDEV_FEAT_CHANGE:
rtnl_event_type = IFLA_EVENT_FEATURES;
break ;
case NETDEV_BONDING_FAILOVER:
rtnl_event_type = IFLA_EVENT_BONDING_FAILOVER;
break ;
case NETDEV_NOTIFY_PEERS:
rtnl_event_type = IFLA_EVENT_NOTIFY_PEERS;
break ;
case NETDEV_RESEND_IGMP:
rtnl_event_type = IFLA_EVENT_IGMP_RESEND;
break ;
case NETDEV_CHANGEINFODATA:
rtnl_event_type = IFLA_EVENT_BONDING_OPTIONS;
break ;
default :
break ;
}
return rtnl_event_type;
}
static int put_master_ifindex(struct sk_buff *skb, struct net_device *dev)
{
const struct net_device *upper_dev;
int ret = 0;
rcu_read_lock();
upper_dev = netdev_master_upper_dev_get_rcu(dev);
if (upper_dev)
ret = nla_put_u32(skb, IFLA_MASTER,
READ_ONCE(upper_dev->ifindex));
rcu_read_unlock();
return ret;
}
static int nla_put_iflink(struct sk_buff *skb, const struct net_device *dev,
bool force)
{
int iflink = dev_get_iflink(dev);
if (force || READ_ONCE(dev->ifindex) != iflink)
return nla_put_u32(skb, IFLA_LINK, iflink);
return 0;
}
static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb,
struct net_device *dev)
{
char buf[IFALIASZ];
int ret;
ret = dev_get_alias(dev, buf, sizeof (buf));
return ret > 0 ? nla_put_string(skb, IFLA_IFALIAS, buf) : 0;
}
static int rtnl_fill_link_netnsid(struct sk_buff *skb,
const struct net_device *dev,
struct net *src_net, gfp_t gfp)
{
bool put_iflink = false ;
if (dev->rtnl_link_ops && dev->rtnl_link_ops->get_link_net) {
struct net *link_net = dev->rtnl_link_ops->get_link_net(dev);
if (!net_eq(dev_net(dev), link_net)) {
int id = peernet2id_alloc(src_net, link_net, gfp);
if (nla_put_s32(skb, IFLA_LINK_NETNSID, id))
return -EMSGSIZE;
put_iflink = true ;
}
}
return nla_put_iflink(skb, dev, put_iflink);
}
static int rtnl_fill_link_af(struct sk_buff *skb,
const struct net_device *dev,
u32 ext_filter_mask)
{
const struct rtnl_af_ops *af_ops;
struct nlattr *af_spec;
af_spec = nla_nest_start_noflag(skb, IFLA_AF_SPEC);
if (!af_spec)
return -EMSGSIZE;
list_for_each_entry_rcu(af_ops, &rtnl_af_ops, list) {
struct nlattr *af;
int err;
if (!af_ops->fill_link_af)
continue ;
af = nla_nest_start_noflag(skb, af_ops->family);
if (!af)
return -EMSGSIZE;
err = af_ops->fill_link_af(skb, dev, ext_filter_mask);
/*
* Caller may return ENODATA to indicate that there
* was no data to be dumped. This is not an error, it
* means we should trim the attribute header and
* continue.
*/
if (err == -ENODATA)
nla_nest_cancel(skb, af);
else if (err < 0)
return -EMSGSIZE;
nla_nest_end(skb, af);
}
nla_nest_end(skb, af_spec);
return 0;
}
static int rtnl_fill_alt_ifnames(struct sk_buff *skb,
const struct net_device *dev)
{
struct netdev_name_node *name_node;
int count = 0;
list_for_each_entry_rcu(name_node, &dev->name_node->list, list) {
if (nla_put_string(skb, IFLA_ALT_IFNAME, name_node->name))
return -EMSGSIZE;
count++;
}
return count;
}
/* RCU protected. */
static int rtnl_fill_prop_list(struct sk_buff *skb,
const struct net_device *dev)
{
struct nlattr *prop_list;
int ret;
prop_list = nla_nest_start(skb, IFLA_PROP_LIST);
if (!prop_list)
return -EMSGSIZE;
ret = rtnl_fill_alt_ifnames(skb, dev);
if (ret <= 0)
goto nest_cancel;
nla_nest_end(skb, prop_list);
return 0;
nest_cancel:
nla_nest_cancel(skb, prop_list);
return ret;
}
static int rtnl_fill_proto_down(struct sk_buff *skb,
const struct net_device *dev)
{
struct nlattr *pr;
u32 preason;
if (nla_put_u8(skb, IFLA_PROTO_DOWN, READ_ONCE(dev->proto_down)))
goto nla_put_failure;
preason = READ_ONCE(dev->proto_down_reason);
if (!preason)
return 0;
pr = nla_nest_start(skb, IFLA_PROTO_DOWN_REASON);
if (!pr)
return -EMSGSIZE;
if (nla_put_u32(skb, IFLA_PROTO_DOWN_REASON_VALUE, preason)) {
nla_nest_cancel(skb, pr);
goto nla_put_failure;
}
nla_nest_end(skb, pr);
return 0;
nla_put_failure:
return -EMSGSIZE;
}
static int rtnl_fill_devlink_port(struct sk_buff *skb,
const struct net_device *dev)
{
struct nlattr *devlink_port_nest;
int ret;
devlink_port_nest = nla_nest_start(skb, IFLA_DEVLINK_PORT);
if (!devlink_port_nest)
return -EMSGSIZE;
if (dev->devlink_port) {
ret = devlink_nl_port_handle_fill(skb, dev->devlink_port);
if (ret < 0)
goto nest_cancel;
}
nla_nest_end(skb, devlink_port_nest);
return 0;
nest_cancel:
nla_nest_cancel(skb, devlink_port_nest);
return ret;
}
static int rtnl_fill_dpll_pin(struct sk_buff *skb,
const struct net_device *dev)
{
struct nlattr *dpll_pin_nest;
int ret;
dpll_pin_nest = nla_nest_start(skb, IFLA_DPLL_PIN);
if (!dpll_pin_nest)
return -EMSGSIZE;
ret = dpll_netdev_add_pin_handle(skb, dev);
if (ret < 0)
goto nest_cancel;
nla_nest_end(skb, dpll_pin_nest);
return 0;
nest_cancel:
nla_nest_cancel(skb, dpll_pin_nest);
return ret;
}
static int rtnl_fill_ifinfo(struct sk_buff *skb,
struct net_device *dev, struct net *src_net,
int type, u32 pid, u32 seq, u32 change,
unsigned int flags, u32 ext_filter_mask,
u32 event, int *new_nsid, int new_ifindex,
int tgt_netnsid, gfp_t gfp)
{
char devname[IFNAMSIZ];
struct ifinfomsg *ifm;
struct nlmsghdr *nlh;
struct Qdisc *qdisc;
ASSERT_RTNL();
nlh = nlmsg_put(skb, pid, seq, type, sizeof (*ifm), flags);
if (nlh == NULL)
return -EMSGSIZE;
ifm = nlmsg_data(nlh);
ifm->ifi_family = AF_UNSPEC;
ifm->__ifi_pad = 0;
ifm->ifi_type = READ_ONCE(dev->type);
ifm->ifi_index = READ_ONCE(dev->ifindex);
ifm->ifi_flags = netif_get_flags(dev);
ifm->ifi_change = change;
if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_TARGET_NETNSID, tgt_netnsid))
goto nla_put_failure;
netdev_copy_name(dev, devname);
if (nla_put_string(skb, IFLA_IFNAME, devname))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_TXQLEN, READ_ONCE(dev->tx_queue_len)) ||
nla_put_u8(skb, IFLA_OPERSTATE,
netif_running(dev) ? READ_ONCE(dev->operstate) :
IF_OPER_DOWN) ||
nla_put_u8(skb, IFLA_LINKMODE, READ_ONCE(dev->link_mode)) ||
nla_put_u8(skb, IFLA_NETNS_IMMUTABLE, dev->netns_immutable) ||
nla_put_u32(skb, IFLA_MTU, READ_ONCE(dev->mtu)) ||
nla_put_u32(skb, IFLA_MIN_MTU, READ_ONCE(dev->min_mtu)) ||
nla_put_u32(skb, IFLA_MAX_MTU, READ_ONCE(dev->max_mtu)) ||
nla_put_u32(skb, IFLA_GROUP, READ_ONCE(dev->group)) ||
nla_put_u32(skb, IFLA_PROMISCUITY, READ_ONCE(dev->promiscuity)) ||
nla_put_u32(skb, IFLA_ALLMULTI, READ_ONCE(dev->allmulti)) ||
nla_put_u32(skb, IFLA_NUM_TX_QUEUES,
READ_ONCE(dev->num_tx_queues)) ||
nla_put_u32(skb, IFLA_GSO_MAX_SEGS,
READ_ONCE(dev->gso_max_segs)) ||
nla_put_u32(skb, IFLA_GSO_MAX_SIZE,
READ_ONCE(dev->gso_max_size)) ||
nla_put_u32(skb, IFLA_GRO_MAX_SIZE,
READ_ONCE(dev->gro_max_size)) ||
nla_put_u32(skb, IFLA_GSO_IPV4_MAX_SIZE,
READ_ONCE(dev->gso_ipv4_max_size)) ||
nla_put_u32(skb, IFLA_GRO_IPV4_MAX_SIZE,
READ_ONCE(dev->gro_ipv4_max_size)) ||
nla_put_u32(skb, IFLA_TSO_MAX_SIZE,
READ_ONCE(dev->tso_max_size)) ||
nla_put_u32(skb, IFLA_TSO_MAX_SEGS,
READ_ONCE(dev->tso_max_segs)) ||
nla_put_uint(skb, IFLA_MAX_PACING_OFFLOAD_HORIZON,
READ_ONCE(dev->max_pacing_offload_horizon)) ||
#ifdef CONFIG_RPS
nla_put_u32(skb, IFLA_NUM_RX_QUEUES,
READ_ONCE(dev->num_rx_queues)) ||
#endif
put_master_ifindex(skb, dev) ||
nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
nla_put_ifalias(skb, dev) ||
nla_put_u32(skb, IFLA_CARRIER_CHANGES,
atomic_read(&dev->carrier_up_count) +
atomic_read(&dev->carrier_down_count)) ||
nla_put_u32(skb, IFLA_CARRIER_UP_COUNT,
atomic_read(&dev->carrier_up_count)) ||
nla_put_u32(skb, IFLA_CARRIER_DOWN_COUNT,
atomic_read(&dev->carrier_down_count)))
goto nla_put_failure;
if (rtnl_fill_proto_down(skb, dev))
goto nla_put_failure;
if (event != IFLA_EVENT_NONE) {
if (nla_put_u32(skb, IFLA_EVENT, event))
goto nla_put_failure;
}
if (dev->addr_len) {
if (nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr) ||
nla_put(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast))
goto nla_put_failure;
}
if (rtnl_phys_port_id_fill(skb, dev))
goto nla_put_failure;
if (rtnl_phys_port_name_fill(skb, dev))
goto nla_put_failure;
if (rtnl_phys_switch_id_fill(skb, dev))
goto nla_put_failure;
if (rtnl_fill_stats(skb, dev))
goto nla_put_failure;
if (rtnl_fill_vf(skb, dev, ext_filter_mask))
goto nla_put_failure;
if (rtnl_port_fill(skb, dev, ext_filter_mask))
goto nla_put_failure;
if (rtnl_xdp_fill(skb, dev))
goto nla_put_failure;
if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) {
if (rtnl_link_fill(skb, dev) < 0)
goto nla_put_failure;
}
if (new_nsid &&
nla_put_s32(skb, IFLA_NEW_NETNSID, *new_nsid) < 0)
goto nla_put_failure;
if (new_ifindex &&
nla_put_s32(skb, IFLA_NEW_IFINDEX, new_ifindex) < 0)
goto nla_put_failure;
if (memchr_inv(dev->perm_addr, '\0' , dev->addr_len) &&
nla_put(skb, IFLA_PERM_ADDRESS, dev->addr_len, dev->perm_addr))
goto nla_put_failure;
rcu_read_lock();
if (rtnl_fill_link_netnsid(skb, dev, src_net, GFP_ATOMIC))
goto nla_put_failure_rcu;
qdisc = rcu_dereference(dev->qdisc);
if (qdisc && nla_put_string(skb, IFLA_QDISC, qdisc->ops->id))
goto nla_put_failure_rcu;
if (rtnl_fill_link_af(skb, dev, ext_filter_mask))
goto nla_put_failure_rcu;
if (rtnl_fill_link_ifmap(skb, dev))
goto nla_put_failure_rcu;
if (rtnl_fill_prop_list(skb, dev))
goto nla_put_failure_rcu;
rcu_read_unlock();
if (dev->dev.parent &&
nla_put_string(skb, IFLA_PARENT_DEV_NAME,
dev_name(dev->dev.parent)))
goto nla_put_failure;
if (dev->dev.parent && dev->dev.parent->bus &&
nla_put_string(skb, IFLA_PARENT_DEV_BUS_NAME,
dev->dev.parent->bus->name))
goto nla_put_failure;
if (rtnl_fill_devlink_port(skb, dev))
goto nla_put_failure;
if (rtnl_fill_dpll_pin(skb, dev))
goto nla_put_failure;
nlmsg_end(skb, nlh);
return 0;
nla_put_failure_rcu:
rcu_read_unlock();
nla_put_failure:
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
}
static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_UNSPEC] = { .strict_start_type = IFLA_DPLL_PIN },
[IFLA_IFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ-1 },
[IFLA_ADDRESS] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
[IFLA_BROADCAST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
[IFLA_MAP] = { .len = sizeof (struct rtnl_link_ifmap) },
[IFLA_MTU] = { .type = NLA_U32 },
[IFLA_LINK] = { .type = NLA_U32 },
[IFLA_MASTER] = { .type = NLA_U32 },
[IFLA_CARRIER] = { .type = NLA_U8 },
[IFLA_TXQLEN] = { .type = NLA_U32 },
[IFLA_WEIGHT] = { .type = NLA_U32 },
[IFLA_OPERSTATE] = { .type = NLA_U8 },
[IFLA_LINKMODE] = { .type = NLA_U8 },
[IFLA_LINKINFO] = { .type = NLA_NESTED },
[IFLA_NET_NS_PID] = { .type = NLA_U32 },
[IFLA_NET_NS_FD] = { .type = NLA_U32 },
/* IFLA_IFALIAS is a string, but policy is set to NLA_BINARY to
* allow 0-length string (needed to remove an alias).
*/
[IFLA_IFALIAS] = { .type = NLA_BINARY, .len = IFALIASZ - 1 },
[IFLA_VFINFO_LIST] = {. type = NLA_NESTED },
[IFLA_VF_PORTS] = { .type = NLA_NESTED },
[IFLA_PORT_SELF] = { .type = NLA_NESTED },
[IFLA_AF_SPEC] = { .type = NLA_NESTED },
[IFLA_EXT_MASK] = { .type = NLA_U32 },
[IFLA_PROMISCUITY] = { .type = NLA_U32 },
[IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 },
[IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 },
[IFLA_GSO_MAX_SEGS] = { .type = NLA_U32 },
[IFLA_GSO_MAX_SIZE] = NLA_POLICY_MIN(NLA_U32, MAX_TCP_HEADER + 1),
[IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
[IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */
[IFLA_PHYS_SWITCH_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
[IFLA_LINK_NETNSID] = { .type = NLA_S32 },
[IFLA_PROTO_DOWN] = { .type = NLA_U8 },
[IFLA_XDP] = { .type = NLA_NESTED },
[IFLA_EVENT] = { .type = NLA_U32 },
[IFLA_GROUP] = { .type = NLA_U32 },
[IFLA_TARGET_NETNSID] = { .type = NLA_S32 },
[IFLA_CARRIER_UP_COUNT] = { .type = NLA_U32 },
[IFLA_CARRIER_DOWN_COUNT] = { .type = NLA_U32 },
[IFLA_MIN_MTU] = { .type = NLA_U32 },
[IFLA_MAX_MTU] = { .type = NLA_U32 },
[IFLA_PROP_LIST] = { .type = NLA_NESTED },
[IFLA_ALT_IFNAME] = { .type = NLA_STRING,
.len = ALTIFNAMSIZ - 1 },
[IFLA_PERM_ADDRESS] = { .type = NLA_REJECT },
[IFLA_PROTO_DOWN_REASON] = { .type = NLA_NESTED },
[IFLA_NEW_IFINDEX] = NLA_POLICY_MIN(NLA_S32, 1),
[IFLA_PARENT_DEV_NAME] = { .type = NLA_NUL_STRING },
[IFLA_GRO_MAX_SIZE] = { .type = NLA_U32 },
[IFLA_TSO_MAX_SIZE] = { .type = NLA_REJECT },
[IFLA_TSO_MAX_SEGS] = { .type = NLA_REJECT },
[IFLA_ALLMULTI] = { .type = NLA_REJECT },
[IFLA_GSO_IPV4_MAX_SIZE] = NLA_POLICY_MIN(NLA_U32, MAX_TCP_HEADER + 1),
[IFLA_GRO_IPV4_MAX_SIZE] = { .type = NLA_U32 },
[IFLA_NETNS_IMMUTABLE] = { .type = NLA_REJECT },
};
static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
[IFLA_INFO_KIND] = { .type = NLA_STRING },
[IFLA_INFO_DATA] = { .type = NLA_NESTED },
[IFLA_INFO_SLAVE_KIND] = { .type = NLA_STRING },
[IFLA_INFO_SLAVE_DATA] = { .type = NLA_NESTED },
};
static const struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
[IFLA_VF_MAC] = { .len = sizeof (struct ifla_vf_mac) },
[IFLA_VF_BROADCAST] = { .type = NLA_REJECT },
[IFLA_VF_VLAN] = { .len = sizeof (struct ifla_vf_vlan) },
[IFLA_VF_VLAN_LIST] = { .type = NLA_NESTED },
[IFLA_VF_TX_RATE] = { .len = sizeof (struct ifla_vf_tx_rate) },
[IFLA_VF_SPOOFCHK] = { .len = sizeof (struct ifla_vf_spoofchk) },
[IFLA_VF_RATE] = { .len = sizeof (struct ifla_vf_rate) },
[IFLA_VF_LINK_STATE] = { .len = sizeof (struct ifla_vf_link_state) },
[IFLA_VF_RSS_QUERY_EN] = { .len = sizeof (struct ifla_vf_rss_query_en) },
[IFLA_VF_STATS] = { .type = NLA_NESTED },
[IFLA_VF_TRUST] = { .len = sizeof (struct ifla_vf_trust) },
[IFLA_VF_IB_NODE_GUID] = { .len = sizeof (struct ifla_vf_guid) },
[IFLA_VF_IB_PORT_GUID] = { .len = sizeof (struct ifla_vf_guid) },
};
static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
[IFLA_PORT_VF] = { .type = NLA_U32 },
[IFLA_PORT_PROFILE] = { .type = NLA_STRING,
.len = PORT_PROFILE_MAX },
[IFLA_PORT_INSTANCE_UUID] = { .type = NLA_BINARY,
.len = PORT_UUID_MAX },
[IFLA_PORT_HOST_UUID] = { .type = NLA_STRING,
.len = PORT_UUID_MAX },
[IFLA_PORT_REQUEST] = { .type = NLA_U8, },
[IFLA_PORT_RESPONSE] = { .type = NLA_U16, },
/* Unused, but we need to keep it here since user space could
* fill it. It's also broken with regard to NLA_BINARY use in
* combination with structs.
*/
[IFLA_PORT_VSI_TYPE] = { .type = NLA_BINARY,
.len = sizeof (struct ifla_port_vsi) },
};
static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = {
[IFLA_XDP_UNSPEC] = { .strict_start_type = IFLA_XDP_EXPECTED_FD },
[IFLA_XDP_FD] = { .type = NLA_S32 },
[IFLA_XDP_EXPECTED_FD] = { .type = NLA_S32 },
[IFLA_XDP_ATTACHED] = { .type = NLA_U8 },
[IFLA_XDP_FLAGS] = { .type = NLA_U32 },
[IFLA_XDP_PROG_ID] = { .type = NLA_U32 },
};
static struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla,
int *ops_srcu_index)
{
struct nlattr *linfo[IFLA_INFO_MAX + 1];
struct rtnl_link_ops *ops = NULL;
if (nla_parse_nested_deprecated(linfo, IFLA_INFO_MAX, nla, ifla_info_policy, NULL) < 0)
return NULL;
if (linfo[IFLA_INFO_KIND]) {
char kind[MODULE_NAME_LEN];
nla_strscpy(kind, linfo[IFLA_INFO_KIND], sizeof (kind));
ops = rtnl_link_ops_get(kind, ops_srcu_index);
}
return ops;
}
static bool link_master_filtered(struct net_device *dev, int master_idx)
{
struct net_device *master;
if (!master_idx)
return false ;
master = netdev_master_upper_dev_get(dev);
/* 0 is already used to denote IFLA_MASTER wasn't passed, therefore need
* another invalid value for ifindex to denote "no master".
*/
if (master_idx == -1)
return !!master;
if (!master || master->ifindex != master_idx)
return true ;
return false ;
}
static bool link_kind_filtered(const struct net_device *dev,
const struct rtnl_link_ops *kind_ops)
{
if (kind_ops && dev->rtnl_link_ops != kind_ops)
return true ;
return false ;
}
static bool link_dump_filtered(struct net_device *dev,
int master_idx,
const struct rtnl_link_ops *kind_ops)
{
if (link_master_filtered(dev, master_idx) ||
link_kind_filtered(dev, kind_ops))
return true ;
return false ;
}
/**
* rtnl_get_net_ns_capable - Get netns if sufficiently privileged.
* @sk: netlink socket
* @netnsid: network namespace identifier
*
* Returns the network namespace identified by netnsid on success or an error
* pointer on failure.
*/
struct net *rtnl_get_net_ns_capable(struct sock *sk, int netnsid)
{
struct net *net;
net = get_net_ns_by_id(sock_net(sk), netnsid);
if (!net)
return ERR_PTR(-EINVAL);
/* For now, the caller is required to have CAP_NET_ADMIN in
* the user namespace owning the target net ns.
*/
if (!sk_ns_capable(sk, net->user_ns, CAP_NET_ADMIN)) {
put_net(net);
return ERR_PTR(-EACCES);
}
return net;
}
EXPORT_SYMBOL_GPL(rtnl_get_net_ns_capable);
static int rtnl_valid_dump_ifinfo_req(const struct nlmsghdr *nlh,
bool strict_check, struct nlattr **tb,
struct netlink_ext_ack *extack)
{
int hdrlen;
if (strict_check) {
struct ifinfomsg *ifm;
ifm = nlmsg_payload(nlh, sizeof (*ifm));
if (!ifm) {
NL_SET_ERR_MSG(extack, "Invalid header for link dump" );
return -EINVAL;
}
if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
ifm->ifi_change) {
NL_SET_ERR_MSG(extack, "Invalid values in header for link dump request" );
return -EINVAL;
}
if (ifm->ifi_index) {
NL_SET_ERR_MSG(extack, "Filter by device index not supported for link dumps" );
return -EINVAL;
}
return nlmsg_parse_deprecated_strict(nlh, sizeof (*ifm), tb,
IFLA_MAX, ifla_policy,
extack);
}
/* A hack to preserve kernel<->userspace interface.
* The correct header is ifinfomsg. It is consistent with rtnl_getlink.
* However, before Linux v3.9 the code here assumed rtgenmsg and that's
* what iproute2 < v3.9.0 used.
* We can detect the old iproute2. Even including the IFLA_EXT_MASK
* attribute, its netlink message is shorter than struct ifinfomsg.
*/
hdrlen = nlmsg_len(nlh) < sizeof (struct ifinfomsg) ?
sizeof (struct rtgenmsg) : sizeof (struct ifinfomsg);
return nlmsg_parse_deprecated(nlh, hdrlen, tb, IFLA_MAX, ifla_policy,
extack);
}
static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
struct netlink_ext_ack *extack = cb->extack;
struct rtnl_link_ops *kind_ops = NULL;
const struct nlmsghdr *nlh = cb->nlh;
struct net *net = sock_net(skb->sk);
unsigned int flags = NLM_F_MULTI;
struct nlattr *tb[IFLA_MAX+1];
struct {
unsigned long ifindex;
} *ctx = (void *)cb->ctx;
struct net *tgt_net = net;
u32 ext_filter_mask = 0;
struct net_device *dev;
int ops_srcu_index;
int master_idx = 0;
int netnsid = -1;
int err, i;
err = rtnl_valid_dump_ifinfo_req(nlh, cb->strict_check, tb, extack);
if (err < 0) {
if (cb->strict_check)
return err;
goto walk_entries;
}
for (i = 0; i <= IFLA_MAX; ++i) {
if (!tb[i])
continue ;
/* new attributes should only be added with strict checking */
switch (i) {
case IFLA_TARGET_NETNSID:
netnsid = nla_get_s32(tb[i]);
tgt_net = rtnl_get_net_ns_capable(skb->sk, netnsid);
if (IS_ERR(tgt_net)) {
NL_SET_ERR_MSG(extack, "Invalid target network namespace id" );
err = PTR_ERR(tgt_net);
netnsid = -1;
goto out;
}
break ;
case IFLA_EXT_MASK:
ext_filter_mask = nla_get_u32(tb[i]);
break ;
case IFLA_MASTER:
master_idx = nla_get_u32(tb[i]);
break ;
case IFLA_LINKINFO:
kind_ops = linkinfo_to_kind_ops(tb[i], &ops_srcu_index);
break ;
default :
if (cb->strict_check) {
NL_SET_ERR_MSG(extack, "Unsupported attribute in link dump request" );
err = -EINVAL;
goto out;
}
}
}
if (master_idx || kind_ops)
flags |= NLM_F_DUMP_FILTERED;
walk_entries:
err = 0;
for_each_netdev_dump(tgt_net, dev, ctx->ifindex) {
if (link_dump_filtered(dev, master_idx, kind_ops))
continue ;
err = rtnl_fill_ifinfo(skb, dev, net, RTM_NEWLINK,
NETLINK_CB(cb->skb).portid,
nlh->nlmsg_seq, 0, flags,
ext_filter_mask, 0, NULL, 0,
netnsid, GFP_KERNEL);
if (err < 0)
break ;
}
cb->seq = tgt_net->dev_base_seq;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
out:
if (kind_ops)
rtnl_link_ops_put(kind_ops, ops_srcu_index);
if (netnsid >= 0)
put_net(tgt_net);
return err;
}
int rtnl_nla_parse_ifinfomsg(struct nlattr **tb, const struct nlattr *nla_peer,
struct netlink_ext_ack *exterr)
{
const struct ifinfomsg *ifmp;
const struct nlattr *attrs;
size_t len;
ifmp = nla_data(nla_peer);
attrs = nla_data(nla_peer) + sizeof (struct ifinfomsg);
len = nla_len(nla_peer) - sizeof (struct ifinfomsg);
if (ifmp->ifi_index < 0) {
NL_SET_ERR_MSG_ATTR(exterr, nla_peer,
"ifindex can't be negative" );
return -EINVAL;
}
return nla_parse_deprecated(tb, IFLA_MAX, attrs, len, ifla_policy,
exterr);
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=96 H=88 G=91
¤ Dauer der Verarbeitung: 0.26 Sekunden
¤
*© Formatika GbR, Deutschland