xref: /linux/net/ncsi/ncsi-netlink.c (revision b68fc09be48edbc47de1a0f3d42ef8adf6c0ac55)
1 /*
2  * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/if_arp.h>
13 #include <linux/rtnetlink.h>
14 #include <linux/etherdevice.h>
15 #include <net/genetlink.h>
16 #include <net/ncsi.h>
17 #include <linux/skbuff.h>
18 #include <net/sock.h>
19 #include <uapi/linux/ncsi.h>
20 
21 #include "internal.h"
22 #include "ncsi-netlink.h"
23 
24 static struct genl_family ncsi_genl_family;
25 
26 static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
27 	[NCSI_ATTR_IFINDEX] =		{ .type = NLA_U32 },
28 	[NCSI_ATTR_PACKAGE_LIST] =	{ .type = NLA_NESTED },
29 	[NCSI_ATTR_PACKAGE_ID] =	{ .type = NLA_U32 },
30 	[NCSI_ATTR_CHANNEL_ID] =	{ .type = NLA_U32 },
31 };
32 
33 static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
34 {
35 	struct ncsi_dev_priv *ndp;
36 	struct net_device *dev;
37 	struct ncsi_dev *nd;
38 	struct ncsi_dev;
39 
40 	if (!net)
41 		return NULL;
42 
43 	dev = dev_get_by_index(net, ifindex);
44 	if (!dev) {
45 		pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
46 		return NULL;
47 	}
48 
49 	nd = ncsi_find_dev(dev);
50 	ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
51 
52 	dev_put(dev);
53 	return ndp;
54 }
55 
56 static int ncsi_write_channel_info(struct sk_buff *skb,
57 				   struct ncsi_dev_priv *ndp,
58 				   struct ncsi_channel *nc)
59 {
60 	struct ncsi_channel_vlan_filter *ncf;
61 	struct ncsi_channel_mode *m;
62 	struct nlattr *vid_nest;
63 	int i;
64 
65 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
66 	m = &nc->modes[NCSI_MODE_LINK];
67 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
68 	if (nc->state == NCSI_CHANNEL_ACTIVE)
69 		nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
70 	if (ndp->force_channel == nc)
71 		nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
72 
73 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
74 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2);
75 	nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
76 
77 	vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
78 	if (!vid_nest)
79 		return -ENOMEM;
80 	ncf = &nc->vlan_filter;
81 	i = -1;
82 	while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
83 				  i + 1)) < ncf->n_vids) {
84 		if (ncf->vids[i])
85 			nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
86 				    ncf->vids[i]);
87 	}
88 	nla_nest_end(skb, vid_nest);
89 
90 	return 0;
91 }
92 
93 static int ncsi_write_package_info(struct sk_buff *skb,
94 				   struct ncsi_dev_priv *ndp, unsigned int id)
95 {
96 	struct nlattr *pnest, *cnest, *nest;
97 	struct ncsi_package *np;
98 	struct ncsi_channel *nc;
99 	bool found;
100 	int rc;
101 
102 	if (id > ndp->package_num - 1) {
103 		netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
104 		return -ENODEV;
105 	}
106 
107 	found = false;
108 	NCSI_FOR_EACH_PACKAGE(ndp, np) {
109 		if (np->id != id)
110 			continue;
111 		pnest = nla_nest_start(skb, NCSI_PKG_ATTR);
112 		if (!pnest)
113 			return -ENOMEM;
114 		nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
115 		if (ndp->force_package == np)
116 			nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
117 		cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
118 		if (!cnest) {
119 			nla_nest_cancel(skb, pnest);
120 			return -ENOMEM;
121 		}
122 		NCSI_FOR_EACH_CHANNEL(np, nc) {
123 			nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR);
124 			if (!nest) {
125 				nla_nest_cancel(skb, cnest);
126 				nla_nest_cancel(skb, pnest);
127 				return -ENOMEM;
128 			}
129 			rc = ncsi_write_channel_info(skb, ndp, nc);
130 			if (rc) {
131 				nla_nest_cancel(skb, nest);
132 				nla_nest_cancel(skb, cnest);
133 				nla_nest_cancel(skb, pnest);
134 				return rc;
135 			}
136 			nla_nest_end(skb, nest);
137 		}
138 		nla_nest_end(skb, cnest);
139 		nla_nest_end(skb, pnest);
140 		found = true;
141 	}
142 
143 	if (!found)
144 		return -ENODEV;
145 
146 	return 0;
147 }
148 
149 static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
150 {
151 	struct ncsi_dev_priv *ndp;
152 	unsigned int package_id;
153 	struct sk_buff *skb;
154 	struct nlattr *attr;
155 	void *hdr;
156 	int rc;
157 
158 	if (!info || !info->attrs)
159 		return -EINVAL;
160 
161 	if (!info->attrs[NCSI_ATTR_IFINDEX])
162 		return -EINVAL;
163 
164 	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
165 		return -EINVAL;
166 
167 	ndp = ndp_from_ifindex(genl_info_net(info),
168 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
169 	if (!ndp)
170 		return -ENODEV;
171 
172 	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
173 	if (!skb)
174 		return -ENOMEM;
175 
176 	hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
177 			  &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
178 	if (!hdr) {
179 		kfree_skb(skb);
180 		return -EMSGSIZE;
181 	}
182 
183 	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
184 
185 	attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
186 	if (!attr) {
187 		kfree_skb(skb);
188 		return -EMSGSIZE;
189 	}
190 	rc = ncsi_write_package_info(skb, ndp, package_id);
191 
192 	if (rc) {
193 		nla_nest_cancel(skb, attr);
194 		goto err;
195 	}
196 
197 	nla_nest_end(skb, attr);
198 
199 	genlmsg_end(skb, hdr);
200 	return genlmsg_reply(skb, info);
201 
202 err:
203 	kfree_skb(skb);
204 	return rc;
205 }
206 
207 static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
208 				struct netlink_callback *cb)
209 {
210 	struct nlattr *attrs[NCSI_ATTR_MAX + 1];
211 	struct ncsi_package *np, *package;
212 	struct ncsi_dev_priv *ndp;
213 	unsigned int package_id;
214 	struct nlattr *attr;
215 	void *hdr;
216 	int rc;
217 
218 	rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
219 			   ncsi_genl_policy, NULL);
220 	if (rc)
221 		return rc;
222 
223 	if (!attrs[NCSI_ATTR_IFINDEX])
224 		return -EINVAL;
225 
226 	ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
227 			       nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
228 
229 	if (!ndp)
230 		return -ENODEV;
231 
232 	package_id = cb->args[0];
233 	package = NULL;
234 	NCSI_FOR_EACH_PACKAGE(ndp, np)
235 		if (np->id == package_id)
236 			package = np;
237 
238 	if (!package)
239 		return 0; /* done */
240 
241 	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
242 			  &ncsi_genl_family, NLM_F_MULTI,  NCSI_CMD_PKG_INFO);
243 	if (!hdr) {
244 		rc = -EMSGSIZE;
245 		goto err;
246 	}
247 
248 	attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
249 	rc = ncsi_write_package_info(skb, ndp, package->id);
250 	if (rc) {
251 		nla_nest_cancel(skb, attr);
252 		goto err;
253 	}
254 
255 	nla_nest_end(skb, attr);
256 	genlmsg_end(skb, hdr);
257 
258 	cb->args[0] = package_id + 1;
259 
260 	return skb->len;
261 err:
262 	genlmsg_cancel(skb, hdr);
263 	return rc;
264 }
265 
266 static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
267 {
268 	struct ncsi_package *np, *package;
269 	struct ncsi_channel *nc, *channel;
270 	u32 package_id, channel_id;
271 	struct ncsi_dev_priv *ndp;
272 	unsigned long flags;
273 
274 	if (!info || !info->attrs)
275 		return -EINVAL;
276 
277 	if (!info->attrs[NCSI_ATTR_IFINDEX])
278 		return -EINVAL;
279 
280 	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
281 		return -EINVAL;
282 
283 	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
284 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
285 	if (!ndp)
286 		return -ENODEV;
287 
288 	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
289 	package = NULL;
290 
291 	spin_lock_irqsave(&ndp->lock, flags);
292 
293 	NCSI_FOR_EACH_PACKAGE(ndp, np)
294 		if (np->id == package_id)
295 			package = np;
296 	if (!package) {
297 		/* The user has set a package that does not exist */
298 		spin_unlock_irqrestore(&ndp->lock, flags);
299 		return -ERANGE;
300 	}
301 
302 	channel = NULL;
303 	if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
304 		/* Allow any channel */
305 		channel_id = NCSI_RESERVED_CHANNEL;
306 	} else {
307 		channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
308 		NCSI_FOR_EACH_CHANNEL(package, nc)
309 			if (nc->id == channel_id)
310 				channel = nc;
311 	}
312 
313 	if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
314 		/* The user has set a channel that does not exist on this
315 		 * package
316 		 */
317 		spin_unlock_irqrestore(&ndp->lock, flags);
318 		netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
319 			    channel_id);
320 		return -ERANGE;
321 	}
322 
323 	ndp->force_package = package;
324 	ndp->force_channel = channel;
325 	spin_unlock_irqrestore(&ndp->lock, flags);
326 
327 	netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n",
328 		    package_id, channel_id,
329 		    channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : "");
330 
331 	/* Bounce the NCSI channel to set changes */
332 	ncsi_stop_dev(&ndp->ndev);
333 	ncsi_start_dev(&ndp->ndev);
334 
335 	return 0;
336 }
337 
338 static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
339 {
340 	struct ncsi_dev_priv *ndp;
341 	unsigned long flags;
342 
343 	if (!info || !info->attrs)
344 		return -EINVAL;
345 
346 	if (!info->attrs[NCSI_ATTR_IFINDEX])
347 		return -EINVAL;
348 
349 	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
350 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
351 	if (!ndp)
352 		return -ENODEV;
353 
354 	/* Clear any override */
355 	spin_lock_irqsave(&ndp->lock, flags);
356 	ndp->force_package = NULL;
357 	ndp->force_channel = NULL;
358 	spin_unlock_irqrestore(&ndp->lock, flags);
359 	netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
360 
361 	/* Bounce the NCSI channel to set changes */
362 	ncsi_stop_dev(&ndp->ndev);
363 	ncsi_start_dev(&ndp->ndev);
364 
365 	return 0;
366 }
367 
368 static const struct genl_ops ncsi_ops[] = {
369 	{
370 		.cmd = NCSI_CMD_PKG_INFO,
371 		.policy = ncsi_genl_policy,
372 		.doit = ncsi_pkg_info_nl,
373 		.dumpit = ncsi_pkg_info_all_nl,
374 		.flags = 0,
375 	},
376 	{
377 		.cmd = NCSI_CMD_SET_INTERFACE,
378 		.policy = ncsi_genl_policy,
379 		.doit = ncsi_set_interface_nl,
380 		.flags = GENL_ADMIN_PERM,
381 	},
382 	{
383 		.cmd = NCSI_CMD_CLEAR_INTERFACE,
384 		.policy = ncsi_genl_policy,
385 		.doit = ncsi_clear_interface_nl,
386 		.flags = GENL_ADMIN_PERM,
387 	},
388 };
389 
390 static struct genl_family ncsi_genl_family __ro_after_init = {
391 	.name = "NCSI",
392 	.version = 0,
393 	.maxattr = NCSI_ATTR_MAX,
394 	.module = THIS_MODULE,
395 	.ops = ncsi_ops,
396 	.n_ops = ARRAY_SIZE(ncsi_ops),
397 };
398 
399 int ncsi_init_netlink(struct net_device *dev)
400 {
401 	int rc;
402 
403 	rc = genl_register_family(&ncsi_genl_family);
404 	if (rc)
405 		netdev_err(dev, "ncsi: failed to register netlink family\n");
406 
407 	return rc;
408 }
409 
410 int ncsi_unregister_netlink(struct net_device *dev)
411 {
412 	int rc;
413 
414 	rc = genl_unregister_family(&ncsi_genl_family);
415 	if (rc)
416 		netdev_err(dev, "ncsi: failed to unregister netlink family\n");
417 
418 	return rc;
419 }
420