xref: /openbmc/linux/net/core/netdev-genl.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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