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