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 idx = 0, s_idx; 105 int h, s_h; 106 int err; 107 108 s_h = cb->args[0]; 109 s_idx = cb->args[1]; 110 111 rtnl_lock(); 112 113 for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 114 struct hlist_head *head; 115 116 idx = 0; 117 head = &net->dev_index_head[h]; 118 hlist_for_each_entry(netdev, head, index_hlist) { 119 if (idx < s_idx) 120 goto cont; 121 err = netdev_nl_dev_fill(netdev, skb, 122 NETLINK_CB(cb->skb).portid, 123 cb->nlh->nlmsg_seq, 0, 124 NETDEV_CMD_DEV_GET); 125 if (err < 0) 126 break; 127 cont: 128 idx++; 129 } 130 } 131 132 rtnl_unlock(); 133 134 if (err != -EMSGSIZE) 135 return err; 136 137 cb->args[1] = idx; 138 cb->args[0] = h; 139 cb->seq = net->dev_base_seq; 140 141 return skb->len; 142 } 143 144 static int netdev_genl_netdevice_event(struct notifier_block *nb, 145 unsigned long event, void *ptr) 146 { 147 struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 148 149 switch (event) { 150 case NETDEV_REGISTER: 151 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_ADD_NTF); 152 break; 153 case NETDEV_UNREGISTER: 154 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF); 155 break; 156 case NETDEV_XDP_FEAT_CHANGE: 157 netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_CHANGE_NTF); 158 break; 159 } 160 161 return NOTIFY_OK; 162 } 163 164 static struct notifier_block netdev_genl_nb = { 165 .notifier_call = netdev_genl_netdevice_event, 166 }; 167 168 static int __init netdev_genl_init(void) 169 { 170 int err; 171 172 err = register_netdevice_notifier(&netdev_genl_nb); 173 if (err) 174 return err; 175 176 err = genl_register_family(&netdev_nl_family); 177 if (err) 178 goto err_unreg_ntf; 179 180 return 0; 181 182 err_unreg_ntf: 183 unregister_netdevice_notifier(&netdev_genl_nb); 184 return err; 185 } 186 187 subsys_initcall(netdev_genl_init); 188