1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/netdevice.h> 4 #include <linux/notifier.h> 5 #include <linux/rtnetlink.h> 6 #include <net/net_namespace.h> 7 #include <net/sock.h> 8 9 #include "netdev-genl-gen.h" 10 11 static int 12 netdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp, 13 u32 portid, u32 seq, int flags, u32 cmd) 14 { 15 void *hdr; 16 17 hdr = genlmsg_put(rsp, portid, seq, &netdev_nl_family, flags, cmd); 18 if (!hdr) 19 return -EMSGSIZE; 20 21 if (nla_put_u32(rsp, NETDEV_A_DEV_IFINDEX, netdev->ifindex) || 22 nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_FEATURES, 23 netdev->xdp_features, NETDEV_A_DEV_PAD)) { 24 genlmsg_cancel(rsp, hdr); 25 return -EINVAL; 26 } 27 28 if (netdev->xdp_features & NETDEV_XDP_ACT_XSK_ZEROCOPY) { 29 if (nla_put_u32(rsp, NETDEV_A_DEV_XDP_ZC_MAX_SEGS, 30 netdev->xdp_zc_max_segs)) { 31 genlmsg_cancel(rsp, hdr); 32 return -EINVAL; 33 } 34 } 35 36 genlmsg_end(rsp, hdr); 37 38 return 0; 39 } 40 41 static void 42 netdev_genl_dev_notify(struct net_device *netdev, int cmd) 43 { 44 struct sk_buff *ntf; 45 46 if (!genl_has_listeners(&netdev_nl_family, dev_net(netdev), 47 NETDEV_NLGRP_MGMT)) 48 return; 49 50 ntf = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); 51 if (!ntf) 52 return; 53 54 if (netdev_nl_dev_fill(netdev, ntf, 0, 0, 0, cmd)) { 55 nlmsg_free(ntf); 56 return; 57 } 58 59 genlmsg_multicast_netns(&netdev_nl_family, dev_net(netdev), ntf, 60 0, NETDEV_NLGRP_MGMT, GFP_KERNEL); 61 } 62 63 int netdev_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info) 64 { 65 struct net_device *netdev; 66 struct sk_buff *rsp; 67 u32 ifindex; 68 int err; 69 70 if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_DEV_IFINDEX)) 71 return -EINVAL; 72 73 ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]); 74 75 rsp = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); 76 if (!rsp) 77 return -ENOMEM; 78 79 rtnl_lock(); 80 81 netdev = __dev_get_by_index(genl_info_net(info), ifindex); 82 if (netdev) 83 err = netdev_nl_dev_fill(netdev, rsp, info->snd_portid, 84 info->snd_seq, 0, info->genlhdr->cmd); 85 else 86 err = -ENODEV; 87 88 rtnl_unlock(); 89 90 if (err) 91 goto err_free_msg; 92 93 return genlmsg_reply(rsp, info); 94 95 err_free_msg: 96 nlmsg_free(rsp); 97 return err; 98 } 99 100 int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 101 { 102 struct net *net = sock_net(skb->sk); 103 struct net_device *netdev; 104 int err = 0; 105 106 rtnl_lock(); 107 for_each_netdev_dump(net, netdev, cb->args[0]) { 108 err = netdev_nl_dev_fill(netdev, skb, 109 NETLINK_CB(cb->skb).portid, 110 cb->nlh->nlmsg_seq, 0, 111 NETDEV_CMD_DEV_GET); 112 if (err < 0) 113 break; 114 } 115 rtnl_unlock(); 116 117 if (err != -EMSGSIZE) 118 return err; 119 120 return skb->len; 121 } 122 123 static int netdev_genl_netdevice_event(struct notifier_block *nb, 124 unsigned long event, void *ptr) 125 { 126 struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 127 128 switch (event) { 129 case NETDEV_REGISTER: 130 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_ADD_NTF); 131 break; 132 case NETDEV_UNREGISTER: 133 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF); 134 break; 135 case NETDEV_XDP_FEAT_CHANGE: 136 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_CHANGE_NTF); 137 break; 138 } 139 140 return NOTIFY_OK; 141 } 142 143 static struct notifier_block netdev_genl_nb = { 144 .notifier_call = netdev_genl_netdevice_event, 145 }; 146 147 static int __init netdev_genl_init(void) 148 { 149 int err; 150 151 err = register_netdevice_notifier(&netdev_genl_nb); 152 if (err) 153 return err; 154 155 err = genl_register_family(&netdev_nl_family); 156 if (err) 157 goto err_unreg_ntf; 158 159 return 0; 160 161 err_unreg_ntf: 162 unregister_netdevice_notifier(&netdev_genl_nb); 163 return err; 164 } 165 166 subsys_initcall(netdev_genl_init); 167