xref: /linux/net/devlink/netlink.c (revision 35c2c39832e569449b9192fa1afbbc4c66227af7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5  */
6 
7 #include <net/genetlink.h>
8 #include <net/sock.h>
9 
10 #include "devl_internal.h"
11 
12 #define DEVLINK_NL_FLAG_NEED_PORT		BIT(0)
13 #define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT	BIT(1)
14 #define DEVLINK_NL_FLAG_NEED_DEV_LOCK		BIT(2)
15 
16 static const struct genl_multicast_group devlink_nl_mcgrps[] = {
17 	[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
18 };
19 
20 struct devlink_nl_sock_priv {
21 	struct devlink_obj_desc __rcu *flt;
22 	spinlock_t flt_lock; /* Protects flt. */
23 };
24 
25 static void devlink_nl_sock_priv_init(void *priv)
26 {
27 	struct devlink_nl_sock_priv *sk_priv = priv;
28 
29 	spin_lock_init(&sk_priv->flt_lock);
30 }
31 
32 static void devlink_nl_sock_priv_destroy(void *priv)
33 {
34 	struct devlink_nl_sock_priv *sk_priv = priv;
35 	struct devlink_obj_desc *flt;
36 
37 	flt = rcu_dereference_protected(sk_priv->flt, true);
38 	kfree_rcu(flt, rcu);
39 }
40 
41 int devlink_nl_notify_filter_set_doit(struct sk_buff *skb,
42 				      struct genl_info *info)
43 {
44 	struct devlink_nl_sock_priv *sk_priv;
45 	struct nlattr **attrs = info->attrs;
46 	struct devlink_obj_desc *flt;
47 	size_t data_offset = 0;
48 	size_t data_size = 0;
49 	char *pos;
50 
51 	if (attrs[DEVLINK_ATTR_BUS_NAME])
52 		data_size = size_add(data_size,
53 				     nla_len(attrs[DEVLINK_ATTR_BUS_NAME]) + 1);
54 	if (attrs[DEVLINK_ATTR_DEV_NAME])
55 		data_size = size_add(data_size,
56 				     nla_len(attrs[DEVLINK_ATTR_DEV_NAME]) + 1);
57 
58 	flt = kzalloc(size_add(sizeof(*flt), data_size), GFP_KERNEL);
59 	if (!flt)
60 		return -ENOMEM;
61 
62 	pos = (char *) flt->data;
63 	if (attrs[DEVLINK_ATTR_BUS_NAME]) {
64 		data_offset += nla_strscpy(pos,
65 					   attrs[DEVLINK_ATTR_BUS_NAME],
66 					   data_size) + 1;
67 		flt->bus_name = pos;
68 		pos += data_offset;
69 	}
70 	if (attrs[DEVLINK_ATTR_DEV_NAME]) {
71 		nla_strscpy(pos, attrs[DEVLINK_ATTR_DEV_NAME],
72 			    data_size - data_offset);
73 		flt->dev_name = pos;
74 	}
75 
76 	if (attrs[DEVLINK_ATTR_INDEX]) {
77 		flt->devlink_index = nla_get_uint(attrs[DEVLINK_ATTR_INDEX]);
78 		flt->devlink_index_valid = true;
79 	}
80 
81 	if (attrs[DEVLINK_ATTR_PORT_INDEX]) {
82 		flt->port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
83 		flt->port_index_valid = true;
84 	}
85 
86 	/* Don't attach empty filter. */
87 	if (!flt->bus_name && !flt->dev_name &&
88 	    !flt->devlink_index_valid && !flt->port_index_valid) {
89 		kfree(flt);
90 		flt = NULL;
91 	}
92 
93 	sk_priv = genl_sk_priv_get(&devlink_nl_family, NETLINK_CB(skb).sk);
94 	if (IS_ERR(sk_priv)) {
95 		kfree(flt);
96 		return PTR_ERR(sk_priv);
97 	}
98 	spin_lock(&sk_priv->flt_lock);
99 	flt = rcu_replace_pointer(sk_priv->flt, flt,
100 				  lockdep_is_held(&sk_priv->flt_lock));
101 	spin_unlock(&sk_priv->flt_lock);
102 	kfree_rcu(flt, rcu);
103 	return 0;
104 }
105 
106 static bool devlink_obj_desc_match(const struct devlink_obj_desc *desc,
107 				   const struct devlink_obj_desc *flt)
108 {
109 	if (desc->devlink_index_valid && flt->devlink_index_valid &&
110 	    desc->devlink_index != flt->devlink_index)
111 		return false;
112 	if (desc->bus_name && flt->bus_name &&
113 	    strcmp(desc->bus_name, flt->bus_name))
114 		return false;
115 	if (desc->dev_name && flt->dev_name &&
116 	    strcmp(desc->dev_name, flt->dev_name))
117 		return false;
118 	if (desc->port_index_valid && flt->port_index_valid &&
119 	    desc->port_index != flt->port_index)
120 		return false;
121 	return true;
122 }
123 
124 int devlink_nl_notify_filter(struct sock *dsk, struct sk_buff *skb, void *data)
125 {
126 	struct devlink_obj_desc *desc = data;
127 	struct devlink_nl_sock_priv *sk_priv;
128 	struct devlink_obj_desc *flt;
129 	int ret = 0;
130 
131 	rcu_read_lock();
132 	sk_priv = __genl_sk_priv_get(&devlink_nl_family, dsk);
133 	if (!IS_ERR_OR_NULL(sk_priv)) {
134 		flt = rcu_dereference(sk_priv->flt);
135 		if (flt)
136 			ret = !devlink_obj_desc_match(desc, flt);
137 	}
138 	rcu_read_unlock();
139 	return ret;
140 }
141 
142 int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net,
143 				 struct devlink *devlink, int attrtype)
144 {
145 	struct nlattr *nested_attr;
146 	struct net *devl_net;
147 
148 	nested_attr = nla_nest_start(msg, attrtype);
149 	if (!nested_attr)
150 		return -EMSGSIZE;
151 	if (devlink_nl_put_handle(msg, devlink))
152 		goto nla_put_failure;
153 
154 	rcu_read_lock();
155 	devl_net = read_pnet_rcu(&devlink->_net);
156 	if (!net_eq(net, devl_net)) {
157 		int id = peernet2id_alloc(net, devl_net, GFP_ATOMIC);
158 
159 		rcu_read_unlock();
160 		if (nla_put_s32(msg, DEVLINK_ATTR_NETNS_ID, id))
161 			return -EMSGSIZE;
162 	} else {
163 		rcu_read_unlock();
164 	}
165 
166 	nla_nest_end(msg, nested_attr);
167 	return 0;
168 
169 nla_put_failure:
170 	nla_nest_cancel(msg, nested_attr);
171 	return -EMSGSIZE;
172 }
173 
174 int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info)
175 {
176 	int err;
177 
178 	if (*msg) {
179 		err = genlmsg_reply(*msg, info);
180 		if (err)
181 			return err;
182 	}
183 	*msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
184 	if (!*msg)
185 		return -ENOMEM;
186 	return 0;
187 }
188 
189 struct devlink *
190 devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs,
191 			    bool dev_lock)
192 {
193 	struct devlink *devlink;
194 	unsigned long index;
195 	char *busname;
196 	char *devname;
197 
198 	if (attrs[DEVLINK_ATTR_INDEX]) {
199 		if (attrs[DEVLINK_ATTR_BUS_NAME] ||
200 		    attrs[DEVLINK_ATTR_DEV_NAME])
201 			return ERR_PTR(-EINVAL);
202 		index = nla_get_u32(attrs[DEVLINK_ATTR_INDEX]);
203 		devlink = devlinks_xa_lookup_get(net, index);
204 		if (!devlink)
205 			return ERR_PTR(-ENODEV);
206 		goto found;
207 	}
208 
209 	if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])
210 		return ERR_PTR(-EINVAL);
211 
212 	busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
213 	devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]);
214 
215 	if (!strcmp(busname, DEVLINK_INDEX_BUS_NAME)) {
216 		if (kstrtoul(devname, 10, &index))
217 			return ERR_PTR(-ENODEV);
218 		devlink = devlinks_xa_lookup_get(net, index);
219 		if (!devlink)
220 			return ERR_PTR(-ENODEV);
221 		goto found;
222 	}
223 
224 	devlinks_xa_for_each_registered_get(net, index, devlink) {
225 		if (strcmp(devlink_bus_name(devlink), busname) == 0 &&
226 		    strcmp(devlink_dev_name(devlink), devname) == 0)
227 			goto found;
228 		devlink_put(devlink);
229 	}
230 
231 	return ERR_PTR(-ENODEV);
232 
233 found:
234 	devl_dev_lock(devlink, dev_lock);
235 	if (devl_is_registered(devlink))
236 		return devlink;
237 	devl_dev_unlock(devlink, dev_lock);
238 	devlink_put(devlink);
239 	return ERR_PTR(-ENODEV);
240 }
241 
242 static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info,
243 				 u8 flags)
244 {
245 	bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
246 	struct devlink_port *devlink_port;
247 	struct devlink *devlink;
248 	int err;
249 
250 	devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs,
251 					      dev_lock);
252 	if (IS_ERR(devlink))
253 		return PTR_ERR(devlink);
254 
255 	info->user_ptr[0] = devlink;
256 	if (flags & DEVLINK_NL_FLAG_NEED_PORT) {
257 		devlink_port = devlink_port_get_from_info(devlink, info);
258 		if (IS_ERR(devlink_port)) {
259 			err = PTR_ERR(devlink_port);
260 			goto unlock;
261 		}
262 		info->user_ptr[1] = devlink_port;
263 	} else if (flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
264 		devlink_port = devlink_port_get_from_info(devlink, info);
265 		if (!IS_ERR(devlink_port))
266 			info->user_ptr[1] = devlink_port;
267 	}
268 	return 0;
269 
270 unlock:
271 	devl_dev_unlock(devlink, dev_lock);
272 	devlink_put(devlink);
273 	return err;
274 }
275 
276 int devlink_nl_pre_doit(const struct genl_split_ops *ops,
277 			struct sk_buff *skb, struct genl_info *info)
278 {
279 	return __devlink_nl_pre_doit(skb, info, 0);
280 }
281 
282 int devlink_nl_pre_doit_port(const struct genl_split_ops *ops,
283 			     struct sk_buff *skb, struct genl_info *info)
284 {
285 	return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_PORT);
286 }
287 
288 int devlink_nl_pre_doit_dev_lock(const struct genl_split_ops *ops,
289 				 struct sk_buff *skb, struct genl_info *info)
290 {
291 	return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEV_LOCK);
292 }
293 
294 int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops,
295 				      struct sk_buff *skb,
296 				      struct genl_info *info)
297 {
298 	return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT);
299 }
300 
301 static void __devlink_nl_post_doit(struct sk_buff *skb, struct genl_info *info,
302 				   u8 flags)
303 {
304 	bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
305 	struct devlink *devlink;
306 
307 	devlink = info->user_ptr[0];
308 	devl_dev_unlock(devlink, dev_lock);
309 	devlink_put(devlink);
310 }
311 
312 void devlink_nl_post_doit(const struct genl_split_ops *ops,
313 			  struct sk_buff *skb, struct genl_info *info)
314 {
315 	__devlink_nl_post_doit(skb, info, 0);
316 }
317 
318 void
319 devlink_nl_post_doit_dev_lock(const struct genl_split_ops *ops,
320 			      struct sk_buff *skb, struct genl_info *info)
321 {
322 	__devlink_nl_post_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEV_LOCK);
323 }
324 
325 static int devlink_nl_inst_single_dumpit(struct sk_buff *msg,
326 					 struct netlink_callback *cb, int flags,
327 					 devlink_nl_dump_one_func_t *dump_one,
328 					 struct nlattr **attrs)
329 {
330 	struct devlink *devlink;
331 	int err;
332 
333 	devlink = devlink_get_from_attrs_lock(sock_net(msg->sk), attrs, false);
334 	if (IS_ERR(devlink))
335 		return PTR_ERR(devlink);
336 	err = dump_one(msg, devlink, cb, flags | NLM_F_DUMP_FILTERED);
337 
338 	devl_unlock(devlink);
339 	devlink_put(devlink);
340 
341 	if (err != -EMSGSIZE)
342 		return err;
343 	return msg->len;
344 }
345 
346 static int devlink_nl_inst_iter_dumpit(struct sk_buff *msg,
347 				       struct netlink_callback *cb, int flags,
348 				       devlink_nl_dump_one_func_t *dump_one)
349 {
350 	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
351 	struct devlink *devlink;
352 	int err = 0;
353 
354 	while ((devlink = devlinks_xa_find_get(sock_net(msg->sk),
355 					       &state->instance))) {
356 		devl_lock(devlink);
357 
358 		if (devl_is_registered(devlink))
359 			err = dump_one(msg, devlink, cb, flags);
360 		else
361 			err = 0;
362 
363 		devl_unlock(devlink);
364 		devlink_put(devlink);
365 
366 		if (err)
367 			break;
368 
369 		state->instance++;
370 
371 		/* restart sub-object walk for the next instance */
372 		state->idx = 0;
373 		state->port_ctx.index = 0;
374 		state->port_ctx.index_valid = false;
375 	}
376 
377 	if (err != -EMSGSIZE)
378 		return err;
379 	return msg->len;
380 }
381 
382 int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
383 		      devlink_nl_dump_one_func_t *dump_one)
384 {
385 	const struct genl_info *info = genl_info_dump(cb);
386 	struct nlattr **attrs = info->attrs;
387 	int flags = NLM_F_MULTI;
388 
389 	if (attrs &&
390 	    (attrs[DEVLINK_ATTR_BUS_NAME] || attrs[DEVLINK_ATTR_DEV_NAME] ||
391 	     attrs[DEVLINK_ATTR_INDEX]))
392 		return devlink_nl_inst_single_dumpit(msg, cb, flags, dump_one,
393 						     attrs);
394 	else
395 		return devlink_nl_inst_iter_dumpit(msg, cb, flags, dump_one);
396 }
397 
398 struct genl_family devlink_nl_family __ro_after_init = {
399 	.name		= DEVLINK_GENL_NAME,
400 	.version	= DEVLINK_GENL_VERSION,
401 	.netnsok	= true,
402 	.parallel_ops	= true,
403 	.module		= THIS_MODULE,
404 	.split_ops	= devlink_nl_ops,
405 	.n_split_ops	= ARRAY_SIZE(devlink_nl_ops),
406 	.resv_start_op	= DEVLINK_CMD_SELFTESTS_RUN + 1,
407 	.mcgrps		= devlink_nl_mcgrps,
408 	.n_mcgrps	= ARRAY_SIZE(devlink_nl_mcgrps),
409 	.sock_priv_size		= sizeof(struct devlink_nl_sock_priv),
410 	.sock_priv_init		= devlink_nl_sock_priv_init,
411 	.sock_priv_destroy	= devlink_nl_sock_priv_destroy,
412 };
413