blob: 0c0d20bb92cee1763a9491fd28379450935ba5de [file] [log] [blame]
Matthias Schiffer09748a22016-05-09 18:41:08 +02001/* 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
Sven Eckelmannf32ed4b2016-07-03 13:31:38 +020021#include <linux/atomic.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020022#include <linux/errno.h>
23#include <linux/fs.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020024#include <linux/genetlink.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020025#include <linux/if_ether.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020026#include <linux/init.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020027#include <linux/netdevice.h>
28#include <linux/netlink.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020029#include <linux/printk.h>
Matthias Schifferb60620c2016-07-03 13:31:36 +020030#include <linux/rculist.h>
31#include <linux/rcupdate.h>
32#include <linux/skbuff.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020033#include <linux/stddef.h>
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020034#include <linux/types.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020035#include <net/genetlink.h>
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020036#include <net/netlink.h>
Matthias Schifferb60620c2016-07-03 13:31:36 +020037#include <net/sock.h>
Matthias Schiffer09748a22016-05-09 18:41:08 +020038#include <uapi/linux/batman_adv.h>
39
Matthias Schiffer07a30612016-07-03 13:31:35 +020040#include "bat_algo.h"
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020041#include "hard-interface.h"
42#include "soft-interface.h"
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020043#include "tp_meter.h"
Matthias Schifferd34f0552016-07-03 13:31:37 +020044#include "translation-table.h"
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020045
Matthias Schiffer07a30612016-07-03 13:31:35 +020046struct genl_family batadv_netlink_family = {
Matthias Schiffer09748a22016-05-09 18:41:08 +020047 .id = GENL_ID_GENERATE,
48 .hdrsize = 0,
49 .name = BATADV_NL_NAME,
50 .version = 1,
51 .maxattr = BATADV_ATTR_MAX,
52};
53
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020054/* multicast groups */
55enum batadv_netlink_multicast_groups {
56 BATADV_NL_MCGRP_TPMETER,
57};
58
59static struct genl_multicast_group batadv_netlink_mcgrps[] = {
60 [BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER },
61};
62
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020063static struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
64 [BATADV_ATTR_VERSION] = { .type = NLA_STRING },
65 [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING },
66 [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 },
67 [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING },
68 [BATADV_ATTR_MESH_ADDRESS] = { .len = ETH_ALEN },
69 [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 },
70 [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING },
71 [BATADV_ATTR_HARD_ADDRESS] = { .len = ETH_ALEN },
Antonio Quartulli33a3bb42016-05-05 13:09:43 +020072 [BATADV_ATTR_ORIG_ADDRESS] = { .len = ETH_ALEN },
73 [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 },
74 [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 },
75 [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 },
76 [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 },
Matthias Schifferb60620c2016-07-03 13:31:36 +020077 [BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG },
Matthias Schifferd34f0552016-07-03 13:31:37 +020078 [BATADV_ATTR_TT_ADDRESS] = { .len = ETH_ALEN },
79 [BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 },
80 [BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 },
81 [BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 },
82 [BATADV_ATTR_TT_VID] = { .type = NLA_U16 },
83 [BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 },
84 [BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG },
85 [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 },
Matthias Schiffer5da0aef2016-05-09 18:41:09 +020086};
87
88/**
Matthias Schifferb60620c2016-07-03 13:31:36 +020089 * batadv_netlink_get_ifindex - Extract an interface index from a message
90 * @nlh: Message header
91 * @attrtype: Attribute which holds an interface index
92 *
93 * Return: interface index, or 0.
94 */
Matthias Schifferd34f0552016-07-03 13:31:37 +020095int
Matthias Schifferb60620c2016-07-03 13:31:36 +020096batadv_netlink_get_ifindex(const struct nlmsghdr *nlh, int attrtype)
97{
98 struct nlattr *attr = nlmsg_find_attr(nlh, GENL_HDRLEN, attrtype);
99
100 return attr ? nla_get_u32(attr) : 0;
101}
102
103/**
Matthias Schiffer5da0aef2016-05-09 18:41:09 +0200104 * batadv_netlink_mesh_info_put - fill in generic information about mesh
105 * interface
106 * @msg: netlink message to be sent back
107 * @soft_iface: interface for which the data should be taken
108 *
109 * Return: 0 on success, < 0 on error
110 */
111static int
112batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface)
113{
114 struct batadv_priv *bat_priv = netdev_priv(soft_iface);
115 struct batadv_hard_iface *primary_if = NULL;
116 struct net_device *hard_iface;
117 int ret = -ENOBUFS;
118
119 if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) ||
120 nla_put_string(msg, BATADV_ATTR_ALGO_NAME,
Antonio Quartulli29824a52016-05-25 23:27:31 +0800121 bat_priv->algo_ops->name) ||
Matthias Schiffer5da0aef2016-05-09 18:41:09 +0200122 nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, soft_iface->ifindex) ||
123 nla_put_string(msg, BATADV_ATTR_MESH_IFNAME, soft_iface->name) ||
124 nla_put(msg, BATADV_ATTR_MESH_ADDRESS, ETH_ALEN,
Sven Eckelmannf32ed4b2016-07-03 13:31:38 +0200125 soft_iface->dev_addr) ||
126 nla_put_u8(msg, BATADV_ATTR_TT_TTVN,
127 (u8)atomic_read(&bat_priv->tt.vn)))
Matthias Schiffer5da0aef2016-05-09 18:41:09 +0200128 goto out;
129
130 primary_if = batadv_primary_if_get_selected(bat_priv);
131 if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) {
132 hard_iface = primary_if->net_dev;
133
134 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
135 hard_iface->ifindex) ||
136 nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
137 hard_iface->name) ||
138 nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
139 hard_iface->dev_addr))
140 goto out;
141 }
142
143 ret = 0;
144
145 out:
146 if (primary_if)
147 batadv_hardif_put(primary_if);
148
149 return ret;
150}
151
152/**
153 * batadv_netlink_get_mesh_info - handle incoming BATADV_CMD_GET_MESH_INFO
154 * netlink request
155 * @skb: received netlink message
156 * @info: receiver information
157 *
158 * Return: 0 on success, < 0 on error
159 */
160static int
161batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info)
162{
163 struct net *net = genl_info_net(info);
164 struct net_device *soft_iface;
165 struct sk_buff *msg = NULL;
166 void *msg_head;
167 int ifindex;
168 int ret;
169
170 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
171 return -EINVAL;
172
173 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
174 if (!ifindex)
175 return -EINVAL;
176
177 soft_iface = dev_get_by_index(net, ifindex);
178 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
179 ret = -ENODEV;
180 goto out;
181 }
182
183 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
184 if (!msg) {
185 ret = -ENOMEM;
186 goto out;
187 }
188
189 msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
190 &batadv_netlink_family, 0,
191 BATADV_CMD_GET_MESH_INFO);
192 if (!msg_head) {
193 ret = -ENOBUFS;
194 goto out;
195 }
196
197 ret = batadv_netlink_mesh_info_put(msg, soft_iface);
198
199 out:
200 if (soft_iface)
201 dev_put(soft_iface);
202
203 if (ret) {
204 if (msg)
205 nlmsg_free(msg);
206 return ret;
207 }
208
209 genlmsg_end(msg, msg_head);
210 return genlmsg_reply(msg, info);
211}
212
Antonio Quartulli33a3bb42016-05-05 13:09:43 +0200213/**
214 * batadv_netlink_tp_meter_put - Fill information of started tp_meter session
215 * @msg: netlink message to be sent back
216 * @cookie: tp meter session cookie
217 *
218 * Return: 0 on success, < 0 on error
219 */
220static int
221batadv_netlink_tp_meter_put(struct sk_buff *msg, u32 cookie)
222{
223 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
224 return -ENOBUFS;
225
226 return 0;
227}
228
229/**
230 * batadv_netlink_tpmeter_notify - send tp_meter result via netlink to client
231 * @bat_priv: the bat priv with all the soft interface information
232 * @dst: destination of tp_meter session
233 * @result: reason for tp meter session stop
234 * @test_time: total time ot the tp_meter session
235 * @total_bytes: bytes acked to the receiver
236 * @cookie: cookie of tp_meter session
237 *
238 * Return: 0 on success, < 0 on error
239 */
240int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst,
241 u8 result, u32 test_time, u64 total_bytes,
242 u32 cookie)
243{
244 struct sk_buff *msg;
245 void *hdr;
246 int ret;
247
248 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
249 if (!msg)
250 return -ENOMEM;
251
252 hdr = genlmsg_put(msg, 0, 0, &batadv_netlink_family, 0,
253 BATADV_CMD_TP_METER);
254 if (!hdr) {
255 ret = -ENOBUFS;
256 goto err_genlmsg;
257 }
258
259 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie))
260 goto nla_put_failure;
261
262 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, test_time))
263 goto nla_put_failure;
264
265 if (nla_put_u64_64bit(msg, BATADV_ATTR_TPMETER_BYTES, total_bytes,
266 BATADV_ATTR_PAD))
267 goto nla_put_failure;
268
269 if (nla_put_u8(msg, BATADV_ATTR_TPMETER_RESULT, result))
270 goto nla_put_failure;
271
272 if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst))
273 goto nla_put_failure;
274
275 genlmsg_end(msg, hdr);
276
277 genlmsg_multicast_netns(&batadv_netlink_family,
278 dev_net(bat_priv->soft_iface), msg, 0,
279 BATADV_NL_MCGRP_TPMETER, GFP_KERNEL);
280
281 return 0;
282
283nla_put_failure:
284 genlmsg_cancel(msg, hdr);
285 ret = -EMSGSIZE;
286
287err_genlmsg:
288 nlmsg_free(msg);
289 return ret;
290}
291
292/**
293 * batadv_netlink_tp_meter_start - Start a new tp_meter session
294 * @skb: received netlink message
295 * @info: receiver information
296 *
297 * Return: 0 on success, < 0 on error
298 */
299static int
300batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
301{
302 struct net *net = genl_info_net(info);
303 struct net_device *soft_iface;
304 struct batadv_priv *bat_priv;
305 struct sk_buff *msg = NULL;
306 u32 test_length;
307 void *msg_head;
308 int ifindex;
309 u32 cookie;
310 u8 *dst;
311 int ret;
312
313 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
314 return -EINVAL;
315
316 if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
317 return -EINVAL;
318
319 if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME])
320 return -EINVAL;
321
322 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
323 if (!ifindex)
324 return -EINVAL;
325
326 dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
327
328 test_length = nla_get_u32(info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]);
329
330 soft_iface = dev_get_by_index(net, ifindex);
331 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
332 ret = -ENODEV;
333 goto out;
334 }
335
336 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
337 if (!msg) {
338 ret = -ENOMEM;
339 goto out;
340 }
341
342 msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq,
343 &batadv_netlink_family, 0,
344 BATADV_CMD_TP_METER);
345 if (!msg_head) {
346 ret = -ENOBUFS;
347 goto out;
348 }
349
350 bat_priv = netdev_priv(soft_iface);
351 batadv_tp_start(bat_priv, dst, test_length, &cookie);
352
353 ret = batadv_netlink_tp_meter_put(msg, cookie);
354
355 out:
356 if (soft_iface)
357 dev_put(soft_iface);
358
359 if (ret) {
360 if (msg)
361 nlmsg_free(msg);
362 return ret;
363 }
364
365 genlmsg_end(msg, msg_head);
366 return genlmsg_reply(msg, info);
367}
368
369/**
370 * batadv_netlink_tp_meter_start - Cancel a running tp_meter session
371 * @skb: received netlink message
372 * @info: receiver information
373 *
374 * Return: 0 on success, < 0 on error
375 */
376static int
377batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info)
378{
379 struct net *net = genl_info_net(info);
380 struct net_device *soft_iface;
381 struct batadv_priv *bat_priv;
382 int ifindex;
383 u8 *dst;
384 int ret = 0;
385
386 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX])
387 return -EINVAL;
388
389 if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS])
390 return -EINVAL;
391
392 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]);
393 if (!ifindex)
394 return -EINVAL;
395
396 dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]);
397
398 soft_iface = dev_get_by_index(net, ifindex);
399 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) {
400 ret = -ENODEV;
401 goto out;
402 }
403
404 bat_priv = netdev_priv(soft_iface);
405 batadv_tp_stop(bat_priv, dst, BATADV_TP_REASON_CANCEL);
406
407out:
408 if (soft_iface)
409 dev_put(soft_iface);
410
411 return ret;
412}
413
Matthias Schifferb60620c2016-07-03 13:31:36 +0200414/**
415 * batadv_netlink_dump_hardif_entry - Dump one hard interface into a message
416 * @msg: Netlink message to dump into
417 * @portid: Port making netlink request
418 * @seq: Sequence number of netlink message
419 * @hard_iface: Hard interface to dump
420 *
421 * Return: error code, or 0 on success
422 */
423static int
424batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid, u32 seq,
425 struct batadv_hard_iface *hard_iface)
426{
427 struct net_device *net_dev = hard_iface->net_dev;
428 void *hdr;
429
430 hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
431 BATADV_CMD_GET_HARDIFS);
432 if (!hdr)
433 return -EMSGSIZE;
434
435 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
436 net_dev->ifindex) ||
437 nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
438 net_dev->name) ||
439 nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN,
440 net_dev->dev_addr))
441 goto nla_put_failure;
442
443 if (hard_iface->if_status == BATADV_IF_ACTIVE) {
444 if (nla_put_flag(msg, BATADV_ATTR_ACTIVE))
445 goto nla_put_failure;
446 }
447
448 genlmsg_end(msg, hdr);
449 return 0;
450
451 nla_put_failure:
452 genlmsg_cancel(msg, hdr);
453 return -EMSGSIZE;
454}
455
456/**
457 * batadv_netlink_dump_hardifs - Dump all hard interface into a messages
458 * @msg: Netlink message to dump into
459 * @cb: Parameters from query
460 *
461 * Return: error code, or length of reply message on success
462 */
463static int
464batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb)
465{
466 struct net *net = sock_net(cb->skb->sk);
467 struct net_device *soft_iface;
468 struct batadv_hard_iface *hard_iface;
469 int ifindex;
470 int portid = NETLINK_CB(cb->skb).portid;
471 int seq = cb->nlh->nlmsg_seq;
472 int skip = cb->args[0];
473 int i = 0;
474
475 ifindex = batadv_netlink_get_ifindex(cb->nlh,
476 BATADV_ATTR_MESH_IFINDEX);
477 if (!ifindex)
478 return -EINVAL;
479
480 soft_iface = dev_get_by_index(net, ifindex);
481 if (!soft_iface)
482 return -ENODEV;
483
484 if (!batadv_softif_is_valid(soft_iface)) {
485 dev_put(soft_iface);
486 return -ENODEV;
487 }
488
489 rcu_read_lock();
490
491 list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
492 if (hard_iface->soft_iface != soft_iface)
493 continue;
494
495 if (i++ < skip)
496 continue;
497
498 if (batadv_netlink_dump_hardif_entry(msg, portid, seq,
499 hard_iface)) {
500 i--;
501 break;
502 }
503 }
504
505 rcu_read_unlock();
506
507 dev_put(soft_iface);
508
509 cb->args[0] = i;
510
511 return msg->len;
512}
513
Matthias Schiffer09748a22016-05-09 18:41:08 +0200514static struct genl_ops batadv_netlink_ops[] = {
Matthias Schiffer5da0aef2016-05-09 18:41:09 +0200515 {
516 .cmd = BATADV_CMD_GET_MESH_INFO,
517 .flags = GENL_ADMIN_PERM,
518 .policy = batadv_netlink_policy,
519 .doit = batadv_netlink_get_mesh_info,
520 },
Antonio Quartulli33a3bb42016-05-05 13:09:43 +0200521 {
522 .cmd = BATADV_CMD_TP_METER,
523 .flags = GENL_ADMIN_PERM,
524 .policy = batadv_netlink_policy,
525 .doit = batadv_netlink_tp_meter_start,
526 },
527 {
528 .cmd = BATADV_CMD_TP_METER_CANCEL,
529 .flags = GENL_ADMIN_PERM,
530 .policy = batadv_netlink_policy,
531 .doit = batadv_netlink_tp_meter_cancel,
532 },
Matthias Schiffer07a30612016-07-03 13:31:35 +0200533 {
534 .cmd = BATADV_CMD_GET_ROUTING_ALGOS,
535 .flags = GENL_ADMIN_PERM,
536 .policy = batadv_netlink_policy,
537 .dumpit = batadv_algo_dump,
538 },
Matthias Schifferb60620c2016-07-03 13:31:36 +0200539 {
540 .cmd = BATADV_CMD_GET_HARDIFS,
541 .flags = GENL_ADMIN_PERM,
542 .policy = batadv_netlink_policy,
543 .dumpit = batadv_netlink_dump_hardifs,
544 },
Matthias Schifferd34f0552016-07-03 13:31:37 +0200545 {
546 .cmd = BATADV_CMD_GET_TRANSTABLE_LOCAL,
547 .flags = GENL_ADMIN_PERM,
548 .policy = batadv_netlink_policy,
549 .dumpit = batadv_tt_local_dump,
550 },
551 {
552 .cmd = BATADV_CMD_GET_TRANSTABLE_GLOBAL,
553 .flags = GENL_ADMIN_PERM,
554 .policy = batadv_netlink_policy,
555 .dumpit = batadv_tt_global_dump,
556 },
Matthias Schiffer09748a22016-05-09 18:41:08 +0200557};
558
559/**
560 * batadv_netlink_register - register batadv genl netlink family
561 */
562void __init batadv_netlink_register(void)
563{
564 int ret;
565
Antonio Quartulli33a3bb42016-05-05 13:09:43 +0200566 ret = genl_register_family_with_ops_groups(&batadv_netlink_family,
567 batadv_netlink_ops,
568 batadv_netlink_mcgrps);
Matthias Schiffer09748a22016-05-09 18:41:08 +0200569 if (ret)
570 pr_warn("unable to register netlink family");
571}
572
573/**
574 * batadv_netlink_unregister - unregister batadv genl netlink family
575 */
576void batadv_netlink_unregister(void)
577{
578 genl_unregister_family(&batadv_netlink_family);
579}