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