Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/net/core/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 172 kB image not shown  

Quelle  rtnetlink.c   Sprache: C

 
// 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






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.