162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/netdevice.h> 462306a36Sopenharmony_ci#include <linux/notifier.h> 562306a36Sopenharmony_ci#include <linux/rtnetlink.h> 662306a36Sopenharmony_ci#include <net/net_namespace.h> 762306a36Sopenharmony_ci#include <net/sock.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "netdev-genl-gen.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic int 1262306a36Sopenharmony_cinetdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp, 1362306a36Sopenharmony_ci const struct genl_info *info) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci void *hdr; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci hdr = genlmsg_iput(rsp, info); 1862306a36Sopenharmony_ci if (!hdr) 1962306a36Sopenharmony_ci return -EMSGSIZE; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (nla_put_u32(rsp, NETDEV_A_DEV_IFINDEX, netdev->ifindex) || 2262306a36Sopenharmony_ci nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_FEATURES, 2362306a36Sopenharmony_ci netdev->xdp_features, NETDEV_A_DEV_PAD)) { 2462306a36Sopenharmony_ci genlmsg_cancel(rsp, hdr); 2562306a36Sopenharmony_ci return -EINVAL; 2662306a36Sopenharmony_ci } 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (netdev->xdp_features & NETDEV_XDP_ACT_XSK_ZEROCOPY) { 2962306a36Sopenharmony_ci if (nla_put_u32(rsp, NETDEV_A_DEV_XDP_ZC_MAX_SEGS, 3062306a36Sopenharmony_ci netdev->xdp_zc_max_segs)) { 3162306a36Sopenharmony_ci genlmsg_cancel(rsp, hdr); 3262306a36Sopenharmony_ci return -EINVAL; 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci genlmsg_end(rsp, hdr); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void 4262306a36Sopenharmony_cinetdev_genl_dev_notify(struct net_device *netdev, int cmd) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct genl_info info; 4562306a36Sopenharmony_ci struct sk_buff *ntf; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (!genl_has_listeners(&netdev_nl_family, dev_net(netdev), 4862306a36Sopenharmony_ci NETDEV_NLGRP_MGMT)) 4962306a36Sopenharmony_ci return; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci genl_info_init_ntf(&info, &netdev_nl_family, cmd); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); 5462306a36Sopenharmony_ci if (!ntf) 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (netdev_nl_dev_fill(netdev, ntf, &info)) { 5862306a36Sopenharmony_ci nlmsg_free(ntf); 5962306a36Sopenharmony_ci return; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci genlmsg_multicast_netns(&netdev_nl_family, dev_net(netdev), ntf, 6362306a36Sopenharmony_ci 0, NETDEV_NLGRP_MGMT, GFP_KERNEL); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciint netdev_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct net_device *netdev; 6962306a36Sopenharmony_ci struct sk_buff *rsp; 7062306a36Sopenharmony_ci u32 ifindex; 7162306a36Sopenharmony_ci int err; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_DEV_IFINDEX)) 7462306a36Sopenharmony_ci return -EINVAL; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); 7962306a36Sopenharmony_ci if (!rsp) 8062306a36Sopenharmony_ci return -ENOMEM; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci rtnl_lock(); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci netdev = __dev_get_by_index(genl_info_net(info), ifindex); 8562306a36Sopenharmony_ci if (netdev) 8662306a36Sopenharmony_ci err = netdev_nl_dev_fill(netdev, rsp, info); 8762306a36Sopenharmony_ci else 8862306a36Sopenharmony_ci err = -ENODEV; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci rtnl_unlock(); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (err) 9362306a36Sopenharmony_ci goto err_free_msg; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return genlmsg_reply(rsp, info); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cierr_free_msg: 9862306a36Sopenharmony_ci nlmsg_free(rsp); 9962306a36Sopenharmony_ci return err; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciint netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 10562306a36Sopenharmony_ci struct net_device *netdev; 10662306a36Sopenharmony_ci int err = 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci rtnl_lock(); 10962306a36Sopenharmony_ci for_each_netdev_dump(net, netdev, cb->args[0]) { 11062306a36Sopenharmony_ci err = netdev_nl_dev_fill(netdev, skb, genl_info_dump(cb)); 11162306a36Sopenharmony_ci if (err < 0) 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci rtnl_unlock(); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (err != -EMSGSIZE) 11762306a36Sopenharmony_ci return err; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return skb->len; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int netdev_genl_netdevice_event(struct notifier_block *nb, 12362306a36Sopenharmony_ci unsigned long event, void *ptr) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci switch (event) { 12862306a36Sopenharmony_ci case NETDEV_REGISTER: 12962306a36Sopenharmony_ci netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_ADD_NTF); 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci case NETDEV_UNREGISTER: 13262306a36Sopenharmony_ci netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF); 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci case NETDEV_XDP_FEAT_CHANGE: 13562306a36Sopenharmony_ci netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_CHANGE_NTF); 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return NOTIFY_OK; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic struct notifier_block netdev_genl_nb = { 14362306a36Sopenharmony_ci .notifier_call = netdev_genl_netdevice_event, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int __init netdev_genl_init(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int err; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci err = register_netdevice_notifier(&netdev_genl_nb); 15162306a36Sopenharmony_ci if (err) 15262306a36Sopenharmony_ci return err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci err = genl_register_family(&netdev_nl_family); 15562306a36Sopenharmony_ci if (err) 15662306a36Sopenharmony_ci goto err_unreg_ntf; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cierr_unreg_ntf: 16162306a36Sopenharmony_ci unregister_netdevice_notifier(&netdev_genl_nb); 16262306a36Sopenharmony_ci return err; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cisubsys_initcall(netdev_genl_init); 166