xref: /openbmc/linux/net/batman-adv/netlink.c (revision 77a87824)
1 /* Copyright (C) 2016 B.A.T.M.A.N. contributors:
2  *
3  * Matthias Schiffer
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "netlink.h"
19 #include "main.h"
20 
21 #include <linux/errno.h>
22 #include <linux/fs.h>
23 #include <linux/genetlink.h>
24 #include <linux/if_ether.h>
25 #include <linux/init.h>
26 #include <linux/netdevice.h>
27 #include <linux/netlink.h>
28 #include <linux/printk.h>
29 #include <linux/stddef.h>
30 #include <linux/types.h>
31 #include <net/genetlink.h>
32 #include <net/netlink.h>
33 #include <uapi/linux/batman_adv.h>
34 
35 #include "hard-interface.h"
36 #include "soft-interface.h"
37 #include "tp_meter.h"
38 
39 struct sk_buff;
40 
41 static struct genl_family batadv_netlink_family = {
42 	.id = GENL_ID_GENERATE,
43 	.hdrsize = 0,
44 	.name = BATADV_NL_NAME,
45 	.version = 1,
46 	.maxattr = BATADV_ATTR_MAX,
47 };
48 
49 /* multicast groups */
50 enum batadv_netlink_multicast_groups {
51 	BATADV_NL_MCGRP_TPMETER,
52 };
53 
54 static struct genl_multicast_group batadv_netlink_mcgrps[] = {
55 	[BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER },
56 };
57 
58 static struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
59 	[BATADV_ATTR_VERSION]		= { .type = NLA_STRING },
60 	[BATADV_ATTR_ALGO_NAME]		= { .type = NLA_STRING },
61 	[BATADV_ATTR_MESH_IFINDEX]	= { .type = NLA_U32 },
62 	[BATADV_ATTR_MESH_IFNAME]	= { .type = NLA_STRING },
63 	[BATADV_ATTR_MESH_ADDRESS]	= { .len = ETH_ALEN },
64 	[BATADV_ATTR_HARD_IFINDEX]	= { .type = NLA_U32 },
65 	[BATADV_ATTR_HARD_IFNAME]	= { .type = NLA_STRING },
66 	[BATADV_ATTR_HARD_ADDRESS]	= { .len = ETH_ALEN },
67 	[BATADV_ATTR_ORIG_ADDRESS]	= { .len = ETH_ALEN },
68 	[BATADV_ATTR_TPMETER_RESULT]	= { .type = NLA_U8 },
69 	[BATADV_ATTR_TPMETER_TEST_TIME]	= { .type = NLA_U32 },
70 	[BATADV_ATTR_TPMETER_BYTES]	= { .type = NLA_U64 },
71 	[BATADV_ATTR_TPMETER_COOKIE]	= { .type = NLA_U32 },
72 };
73 
74 /**
75  * batadv_netlink_mesh_info_put - fill in generic information about mesh
76  *  interface
77  * @msg: netlink message to be sent back
78  * @soft_iface: interface for which the data should be taken
79  *
80  * Return: 0 on success, < 0 on error
81  */
82 static int
83 batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
84 {
85 	struct batadv_priv *bat_priv = netdev_priv(soft_iface);
86 	struct batadv_hard_iface *primary_if = NULL;
87 	struct net_device *hard_iface;
88 	int ret = -ENOBUFS;
89 
90 	if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) ||
91 	    nla_put_string(msg, BATADV_ATTR_ALGO_NAME,
92 			   bat_priv->algo_ops->name) ||
93 	    nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, soft_iface->ifindex) ||
94 	    nla_put_string(msg, BATADV_ATTR_MESH_IFNAME, soft_iface->name) ||
95 	    nla_put(msg, BATADV_ATTR_MESH_ADDRESS, ETH_ALEN,
96 		    soft_iface->dev_addr))
97 		goto out;
98 
99 	primary_if = batadv_primary_if_get_selected(bat_priv);
100 	if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) {
101 		hard_iface = primary_if->net_dev;
102 
103 		if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
104 				hard_iface->ifindex) ||
105 		    nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
106 				   hard_iface->name) ||
107 		    nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
108 			    hard_iface->dev_addr))
109 			goto out;
110 	}
111 
112 	ret = 0;
113 
114  out:
115 	if (primary_if)
116 		batadv_hardif_put(primary_if);
117 
118 	return ret;
119 }
120 
121 /**
122  * batadv_netlink_get_mesh_info - handle incoming BATADV_CMD_GET_MESH_INFO
123  *  netlink request
124  * @skb: received netlink message
125  * @info: receiver information
126  *
127  * Return: 0 on success, < 0 on error
128  */
129 static int
130 batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info)
131 {
132 	struct net *net = genl_info_net(info);
133 	struct net_device *soft_iface;
134 	struct sk_buff *msg = NULL;
135 	void *msg_head;
136 	int ifindex;
137 	int ret;
138 
139 	if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
140 		return -EINVAL;
141 
142 	ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
143 	if (!ifindex)
144 		return -EINVAL;
145 
146 	soft_iface = dev_get_by_index(net, ifindex);
147 	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
148 		ret = -ENODEV;
149 		goto out;
150 	}
151 
152 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
153 	if (!msg) {
154 		ret = -ENOMEM;
155 		goto out;
156 	}
157 
158 	msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
159 			       &batadv_netlink_family, 0,
160 			       BATADV_CMD_GET_MESH_INFO);
161 	if (!msg_head) {
162 		ret = -ENOBUFS;
163 		goto out;
164 	}
165 
166 	ret = batadv_netlink_mesh_info_put(msg, soft_iface);
167 
168  out:
169 	if (soft_iface)
170 		dev_put(soft_iface);
171 
172 	if (ret) {
173 		if (msg)
174 			nlmsg_free(msg);
175 		return ret;
176 	}
177 
178 	genlmsg_end(msg, msg_head);
179 	return genlmsg_reply(msg, info);
180 }
181 
182 /**
183  * batadv_netlink_tp_meter_put - Fill information of started tp_meter session
184  * @msg: netlink message to be sent back
185  * @cookie: tp meter session cookie
186  *
187  *  Return: 0 on success, < 0 on error
188  */
189 static int
190 batadv_netlink_tp_meter_put(struct sk_buff *msg, u32 cookie)
191 {
192 	if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
193 		return -ENOBUFS;
194 
195 	return 0;
196 }
197 
198 /**
199  * batadv_netlink_tpmeter_notify - send tp_meter result via netlink to client
200  * @bat_priv: the bat priv with all the soft interface information
201  * @dst: destination of tp_meter session
202  * @result: reason for tp meter session stop
203  * @test_time: total time ot the tp_meter session
204  * @total_bytes: bytes acked to the receiver
205  * @cookie: cookie of tp_meter session
206  *
207  * Return: 0 on success, < 0 on error
208  */
209 int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
210 				  u8 result, u32 test_time, u64 total_bytes,
211 				  u32 cookie)
212 {
213 	struct sk_buff *msg;
214 	void *hdr;
215 	int ret;
216 
217 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
218 	if (!msg)
219 		return -ENOMEM;
220 
221 	hdr = genlmsg_put(msg, 0, 0, &batadv_netlink_family, 0,
222 			  BATADV_CMD_TP_METER);
223 	if (!hdr) {
224 		ret = -ENOBUFS;
225 		goto err_genlmsg;
226 	}
227 
228 	if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
229 		goto nla_put_failure;
230 
231 	if (nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, test_time))
232 		goto nla_put_failure;
233 
234 	if (nla_put_u64_64bit(msg, BATADV_ATTR_TPMETER_BYTES, total_bytes,
235 			      BATADV_ATTR_PAD))
236 		goto nla_put_failure;
237 
238 	if (nla_put_u8(msg, BATADV_ATTR_TPMETER_RESULT, result))
239 		goto nla_put_failure;
240 
241 	if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst))
242 		goto nla_put_failure;
243 
244 	genlmsg_end(msg, hdr);
245 
246 	genlmsg_multicast_netns(&batadv_netlink_family,
247 				dev_net(bat_priv->soft_iface), msg, 0,
248 				BATADV_NL_MCGRP_TPMETER, GFP_KERNEL);
249 
250 	return 0;
251 
252 nla_put_failure:
253 	genlmsg_cancel(msg, hdr);
254 	ret = -EMSGSIZE;
255 
256 err_genlmsg:
257 	nlmsg_free(msg);
258 	return ret;
259 }
260 
261 /**
262  * batadv_netlink_tp_meter_start - Start a new tp_meter session
263  * @skb: received netlink message
264  * @info: receiver information
265  *
266  * Return: 0 on success, < 0 on error
267  */
268 static int
269 batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
270 {
271 	struct net *net = genl_info_net(info);
272 	struct net_device *soft_iface;
273 	struct batadv_priv *bat_priv;
274 	struct sk_buff *msg = NULL;
275 	u32 test_length;
276 	void *msg_head;
277 	int ifindex;
278 	u32 cookie;
279 	u8 *dst;
280 	int ret;
281 
282 	if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
283 		return -EINVAL;
284 
285 	if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
286 		return -EINVAL;
287 
288 	if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME])
289 		return -EINVAL;
290 
291 	ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
292 	if (!ifindex)
293 		return -EINVAL;
294 
295 	dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
296 
297 	test_length = nla_get_u32(info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]);
298 
299 	soft_iface = dev_get_by_index(net, ifindex);
300 	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
301 		ret = -ENODEV;
302 		goto out;
303 	}
304 
305 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
306 	if (!msg) {
307 		ret = -ENOMEM;
308 		goto out;
309 	}
310 
311 	msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
312 			       &batadv_netlink_family, 0,
313 			       BATADV_CMD_TP_METER);
314 	if (!msg_head) {
315 		ret = -ENOBUFS;
316 		goto out;
317 	}
318 
319 	bat_priv = netdev_priv(soft_iface);
320 	batadv_tp_start(bat_priv, dst, test_length, &cookie);
321 
322 	ret = batadv_netlink_tp_meter_put(msg, cookie);
323 
324  out:
325 	if (soft_iface)
326 		dev_put(soft_iface);
327 
328 	if (ret) {
329 		if (msg)
330 			nlmsg_free(msg);
331 		return ret;
332 	}
333 
334 	genlmsg_end(msg, msg_head);
335 	return genlmsg_reply(msg, info);
336 }
337 
338 /**
339  * batadv_netlink_tp_meter_start - Cancel a running tp_meter session
340  * @skb: received netlink message
341  * @info: receiver information
342  *
343  * Return: 0 on success, < 0 on error
344  */
345 static int
346 batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
347 {
348 	struct net *net = genl_info_net(info);
349 	struct net_device *soft_iface;
350 	struct batadv_priv *bat_priv;
351 	int ifindex;
352 	u8 *dst;
353 	int ret = 0;
354 
355 	if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
356 		return -EINVAL;
357 
358 	if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
359 		return -EINVAL;
360 
361 	ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
362 	if (!ifindex)
363 		return -EINVAL;
364 
365 	dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
366 
367 	soft_iface = dev_get_by_index(net, ifindex);
368 	if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
369 		ret = -ENODEV;
370 		goto out;
371 	}
372 
373 	bat_priv = netdev_priv(soft_iface);
374 	batadv_tp_stop(bat_priv, dst, BATADV_TP_REASON_CANCEL);
375 
376 out:
377 	if (soft_iface)
378 		dev_put(soft_iface);
379 
380 	return ret;
381 }
382 
383 static struct genl_ops batadv_netlink_ops[] = {
384 	{
385 		.cmd = BATADV_CMD_GET_MESH_INFO,
386 		.flags = GENL_ADMIN_PERM,
387 		.policy = batadv_netlink_policy,
388 		.doit = batadv_netlink_get_mesh_info,
389 	},
390 	{
391 		.cmd = BATADV_CMD_TP_METER,
392 		.flags = GENL_ADMIN_PERM,
393 		.policy = batadv_netlink_policy,
394 		.doit = batadv_netlink_tp_meter_start,
395 	},
396 	{
397 		.cmd = BATADV_CMD_TP_METER_CANCEL,
398 		.flags = GENL_ADMIN_PERM,
399 		.policy = batadv_netlink_policy,
400 		.doit = batadv_netlink_tp_meter_cancel,
401 	},
402 };
403 
404 /**
405  * batadv_netlink_register - register batadv genl netlink family
406  */
407 void __init batadv_netlink_register(void)
408 {
409 	int ret;
410 
411 	ret = genl_register_family_with_ops_groups(&batadv_netlink_family,
412 						   batadv_netlink_ops,
413 						   batadv_netlink_mcgrps);
414 	if (ret)
415 		pr_warn("unable to register netlink family");
416 }
417 
418 /**
419  * batadv_netlink_unregister - unregister batadv genl netlink family
420  */
421 void batadv_netlink_unregister(void)
422 {
423 	genl_unregister_family(&batadv_netlink_family);
424 }
425