1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (C) 2016-2018 B.A.T.M.A.N. contributors: 3 * 4 * Matthias Schiffer 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of version 2 of the GNU General Public 8 * License as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include "netlink.h" 20 #include "main.h" 21 22 #include <linux/atomic.h> 23 #include <linux/byteorder/generic.h> 24 #include <linux/cache.h> 25 #include <linux/errno.h> 26 #include <linux/export.h> 27 #include <linux/genetlink.h> 28 #include <linux/gfp.h> 29 #include <linux/if_ether.h> 30 #include <linux/init.h> 31 #include <linux/kernel.h> 32 #include <linux/list.h> 33 #include <linux/netdevice.h> 34 #include <linux/netlink.h> 35 #include <linux/printk.h> 36 #include <linux/rtnetlink.h> 37 #include <linux/skbuff.h> 38 #include <linux/stddef.h> 39 #include <linux/types.h> 40 #include <net/genetlink.h> 41 #include <net/netlink.h> 42 #include <net/sock.h> 43 #include <uapi/linux/batadv_packet.h> 44 #include <uapi/linux/batman_adv.h> 45 46 #include "bat_algo.h" 47 #include "bridge_loop_avoidance.h" 48 #include "distributed-arp-table.h" 49 #include "gateway_client.h" 50 #include "hard-interface.h" 51 #include "multicast.h" 52 #include "originator.h" 53 #include "soft-interface.h" 54 #include "tp_meter.h" 55 #include "translation-table.h" 56 57 struct genl_family batadv_netlink_family; 58 59 /* multicast groups */ 60 enum batadv_netlink_multicast_groups { 61 BATADV_NL_MCGRP_TPMETER, 62 }; 63 64 static const struct genl_multicast_group batadv_netlink_mcgrps[] = { 65 [BATADV_NL_MCGRP_TPMETER] = { .name = BATADV_NL_MCAST_GROUP_TPMETER }, 66 }; 67 68 static const struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { 69 [BATADV_ATTR_VERSION] = { .type = NLA_STRING }, 70 [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING }, 71 [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, 72 [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING }, 73 [BATADV_ATTR_MESH_ADDRESS] = { .len = ETH_ALEN }, 74 [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 }, 75 [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING }, 76 [BATADV_ATTR_HARD_ADDRESS] = { .len = ETH_ALEN }, 77 [BATADV_ATTR_ORIG_ADDRESS] = { .len = ETH_ALEN }, 78 [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 }, 79 [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 }, 80 [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 }, 81 [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 }, 82 [BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG }, 83 [BATADV_ATTR_TT_ADDRESS] = { .len = ETH_ALEN }, 84 [BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 }, 85 [BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 }, 86 [BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 }, 87 [BATADV_ATTR_TT_VID] = { .type = NLA_U16 }, 88 [BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 }, 89 [BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG }, 90 [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 }, 91 [BATADV_ATTR_NEIGH_ADDRESS] = { .len = ETH_ALEN }, 92 [BATADV_ATTR_TQ] = { .type = NLA_U8 }, 93 [BATADV_ATTR_THROUGHPUT] = { .type = NLA_U32 }, 94 [BATADV_ATTR_BANDWIDTH_UP] = { .type = NLA_U32 }, 95 [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NLA_U32 }, 96 [BATADV_ATTR_ROUTER] = { .len = ETH_ALEN }, 97 [BATADV_ATTR_BLA_OWN] = { .type = NLA_FLAG }, 98 [BATADV_ATTR_BLA_ADDRESS] = { .len = ETH_ALEN }, 99 [BATADV_ATTR_BLA_VID] = { .type = NLA_U16 }, 100 [BATADV_ATTR_BLA_BACKBONE] = { .len = ETH_ALEN }, 101 [BATADV_ATTR_BLA_CRC] = { .type = NLA_U16 }, 102 [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { .type = NLA_U32 }, 103 [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { .len = ETH_ALEN }, 104 [BATADV_ATTR_DAT_CACHE_VID] = { .type = NLA_U16 }, 105 [BATADV_ATTR_MCAST_FLAGS] = { .type = NLA_U32 }, 106 [BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NLA_U32 }, 107 }; 108 109 /** 110 * batadv_netlink_get_ifindex() - Extract an interface index from a message 111 * @nlh: Message header 112 * @attrtype: Attribute which holds an interface index 113 * 114 * Return: interface index, or 0. 115 */ 116 int 117 batadv_netlink_get_ifindex(const struct nlmsghdr *nlh, int attrtype) 118 { 119 struct nlattr *attr = nlmsg_find_attr(nlh, GENL_HDRLEN, attrtype); 120 121 return attr ? nla_get_u32(attr) : 0; 122 } 123 124 /** 125 * batadv_netlink_mesh_info_put() - fill in generic information about mesh 126 * interface 127 * @msg: netlink message to be sent back 128 * @soft_iface: interface for which the data should be taken 129 * 130 * Return: 0 on success, < 0 on error 131 */ 132 static int 133 batadv_netlink_mesh_info_put(struct sk_buff *msg, struct net_device *soft_iface) 134 { 135 struct batadv_priv *bat_priv = netdev_priv(soft_iface); 136 struct batadv_hard_iface *primary_if = NULL; 137 struct net_device *hard_iface; 138 int ret = -ENOBUFS; 139 140 if (nla_put_string(msg, BATADV_ATTR_VERSION, BATADV_SOURCE_VERSION) || 141 nla_put_string(msg, BATADV_ATTR_ALGO_NAME, 142 bat_priv->algo_ops->name) || 143 nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, soft_iface->ifindex) || 144 nla_put_string(msg, BATADV_ATTR_MESH_IFNAME, soft_iface->name) || 145 nla_put(msg, BATADV_ATTR_MESH_ADDRESS, ETH_ALEN, 146 soft_iface->dev_addr) || 147 nla_put_u8(msg, BATADV_ATTR_TT_TTVN, 148 (u8)atomic_read(&bat_priv->tt.vn))) 149 goto out; 150 151 #ifdef CONFIG_BATMAN_ADV_BLA 152 if (nla_put_u16(msg, BATADV_ATTR_BLA_CRC, 153 ntohs(bat_priv->bla.claim_dest.group))) 154 goto out; 155 #endif 156 157 if (batadv_mcast_mesh_info_put(msg, bat_priv)) 158 goto out; 159 160 primary_if = batadv_primary_if_get_selected(bat_priv); 161 if (primary_if && primary_if->if_status == BATADV_IF_ACTIVE) { 162 hard_iface = primary_if->net_dev; 163 164 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, 165 hard_iface->ifindex) || 166 nla_put_string(msg, BATADV_ATTR_HARD_IFNAME, 167 hard_iface->name) || 168 nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN, 169 hard_iface->dev_addr)) 170 goto out; 171 } 172 173 ret = 0; 174 175 out: 176 if (primary_if) 177 batadv_hardif_put(primary_if); 178 179 return ret; 180 } 181 182 /** 183 * batadv_netlink_get_mesh_info() - handle incoming BATADV_CMD_GET_MESH_INFO 184 * netlink request 185 * @skb: received netlink message 186 * @info: receiver information 187 * 188 * Return: 0 on success, < 0 on error 189 */ 190 static int 191 batadv_netlink_get_mesh_info(struct sk_buff *skb, struct genl_info *info) 192 { 193 struct net *net = genl_info_net(info); 194 struct net_device *soft_iface; 195 struct sk_buff *msg = NULL; 196 void *msg_head; 197 int ifindex; 198 int ret; 199 200 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX]) 201 return -EINVAL; 202 203 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]); 204 if (!ifindex) 205 return -EINVAL; 206 207 soft_iface = dev_get_by_index(net, ifindex); 208 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { 209 ret = -ENODEV; 210 goto out; 211 } 212 213 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 214 if (!msg) { 215 ret = -ENOMEM; 216 goto out; 217 } 218 219 msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq, 220 &batadv_netlink_family, 0, 221 BATADV_CMD_GET_MESH_INFO); 222 if (!msg_head) { 223 ret = -ENOBUFS; 224 goto out; 225 } 226 227 ret = batadv_netlink_mesh_info_put(msg, soft_iface); 228 229 out: 230 if (soft_iface) 231 dev_put(soft_iface); 232 233 if (ret) { 234 if (msg) 235 nlmsg_free(msg); 236 return ret; 237 } 238 239 genlmsg_end(msg, msg_head); 240 return genlmsg_reply(msg, info); 241 } 242 243 /** 244 * batadv_netlink_tp_meter_put() - Fill information of started tp_meter session 245 * @msg: netlink message to be sent back 246 * @cookie: tp meter session cookie 247 * 248 * Return: 0 on success, < 0 on error 249 */ 250 static int 251 batadv_netlink_tp_meter_put(struct sk_buff *msg, u32 cookie) 252 { 253 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie)) 254 return -ENOBUFS; 255 256 return 0; 257 } 258 259 /** 260 * batadv_netlink_tpmeter_notify() - send tp_meter result via netlink to client 261 * @bat_priv: the bat priv with all the soft interface information 262 * @dst: destination of tp_meter session 263 * @result: reason for tp meter session stop 264 * @test_time: total time ot the tp_meter session 265 * @total_bytes: bytes acked to the receiver 266 * @cookie: cookie of tp_meter session 267 * 268 * Return: 0 on success, < 0 on error 269 */ 270 int batadv_netlink_tpmeter_notify(struct batadv_priv *bat_priv, const u8 *dst, 271 u8 result, u32 test_time, u64 total_bytes, 272 u32 cookie) 273 { 274 struct sk_buff *msg; 275 void *hdr; 276 int ret; 277 278 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 279 if (!msg) 280 return -ENOMEM; 281 282 hdr = genlmsg_put(msg, 0, 0, &batadv_netlink_family, 0, 283 BATADV_CMD_TP_METER); 284 if (!hdr) { 285 ret = -ENOBUFS; 286 goto err_genlmsg; 287 } 288 289 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_COOKIE, cookie)) 290 goto nla_put_failure; 291 292 if (nla_put_u32(msg, BATADV_ATTR_TPMETER_TEST_TIME, test_time)) 293 goto nla_put_failure; 294 295 if (nla_put_u64_64bit(msg, BATADV_ATTR_TPMETER_BYTES, total_bytes, 296 BATADV_ATTR_PAD)) 297 goto nla_put_failure; 298 299 if (nla_put_u8(msg, BATADV_ATTR_TPMETER_RESULT, result)) 300 goto nla_put_failure; 301 302 if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, dst)) 303 goto nla_put_failure; 304 305 genlmsg_end(msg, hdr); 306 307 genlmsg_multicast_netns(&batadv_netlink_family, 308 dev_net(bat_priv->soft_iface), msg, 0, 309 BATADV_NL_MCGRP_TPMETER, GFP_KERNEL); 310 311 return 0; 312 313 nla_put_failure: 314 genlmsg_cancel(msg, hdr); 315 ret = -EMSGSIZE; 316 317 err_genlmsg: 318 nlmsg_free(msg); 319 return ret; 320 } 321 322 /** 323 * batadv_netlink_tp_meter_start() - Start a new tp_meter session 324 * @skb: received netlink message 325 * @info: receiver information 326 * 327 * Return: 0 on success, < 0 on error 328 */ 329 static int 330 batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info) 331 { 332 struct net *net = genl_info_net(info); 333 struct net_device *soft_iface; 334 struct batadv_priv *bat_priv; 335 struct sk_buff *msg = NULL; 336 u32 test_length; 337 void *msg_head; 338 int ifindex; 339 u32 cookie; 340 u8 *dst; 341 int ret; 342 343 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX]) 344 return -EINVAL; 345 346 if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS]) 347 return -EINVAL; 348 349 if (!info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]) 350 return -EINVAL; 351 352 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]); 353 if (!ifindex) 354 return -EINVAL; 355 356 dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]); 357 358 test_length = nla_get_u32(info->attrs[BATADV_ATTR_TPMETER_TEST_TIME]); 359 360 soft_iface = dev_get_by_index(net, ifindex); 361 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { 362 ret = -ENODEV; 363 goto out; 364 } 365 366 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 367 if (!msg) { 368 ret = -ENOMEM; 369 goto out; 370 } 371 372 msg_head = genlmsg_put(msg, info->snd_portid, info->snd_seq, 373 &batadv_netlink_family, 0, 374 BATADV_CMD_TP_METER); 375 if (!msg_head) { 376 ret = -ENOBUFS; 377 goto out; 378 } 379 380 bat_priv = netdev_priv(soft_iface); 381 batadv_tp_start(bat_priv, dst, test_length, &cookie); 382 383 ret = batadv_netlink_tp_meter_put(msg, cookie); 384 385 out: 386 if (soft_iface) 387 dev_put(soft_iface); 388 389 if (ret) { 390 if (msg) 391 nlmsg_free(msg); 392 return ret; 393 } 394 395 genlmsg_end(msg, msg_head); 396 return genlmsg_reply(msg, info); 397 } 398 399 /** 400 * batadv_netlink_tp_meter_start() - Cancel a running tp_meter session 401 * @skb: received netlink message 402 * @info: receiver information 403 * 404 * Return: 0 on success, < 0 on error 405 */ 406 static int 407 batadv_netlink_tp_meter_cancel(struct sk_buff *skb, struct genl_info *info) 408 { 409 struct net *net = genl_info_net(info); 410 struct net_device *soft_iface; 411 struct batadv_priv *bat_priv; 412 int ifindex; 413 u8 *dst; 414 int ret = 0; 415 416 if (!info->attrs[BATADV_ATTR_MESH_IFINDEX]) 417 return -EINVAL; 418 419 if (!info->attrs[BATADV_ATTR_ORIG_ADDRESS]) 420 return -EINVAL; 421 422 ifindex = nla_get_u32(info->attrs[BATADV_ATTR_MESH_IFINDEX]); 423 if (!ifindex) 424 return -EINVAL; 425 426 dst = nla_data(info->attrs[BATADV_ATTR_ORIG_ADDRESS]); 427 428 soft_iface = dev_get_by_index(net, ifindex); 429 if (!soft_iface || !batadv_softif_is_valid(soft_iface)) { 430 ret = -ENODEV; 431 goto out; 432 } 433 434 bat_priv = netdev_priv(soft_iface); 435 batadv_tp_stop(bat_priv, dst, BATADV_TP_REASON_CANCEL); 436 437 out: 438 if (soft_iface) 439 dev_put(soft_iface); 440 441 return ret; 442 } 443 444 /** 445 * batadv_netlink_dump_hardif_entry() - Dump one hard interface into a message 446 * @msg: Netlink message to dump into 447 * @portid: Port making netlink request 448 * @cb: Control block containing additional options 449 * @hard_iface: Hard interface to dump 450 * 451 * Return: error code, or 0 on success 452 */ 453 static int 454 batadv_netlink_dump_hardif_entry(struct sk_buff *msg, u32 portid, 455 struct netlink_callback *cb, 456 struct batadv_hard_iface *hard_iface) 457 { 458 struct net_device *net_dev = hard_iface->net_dev; 459 void *hdr; 460 461 hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq, 462 &batadv_netlink_family, NLM_F_MULTI, 463 BATADV_CMD_GET_HARDIFS); 464 if (!hdr) 465 return -EMSGSIZE; 466 467 genl_dump_check_consistent(cb, hdr); 468 469 if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX, 470 net_dev->ifindex) || 471 nla_put_string(msg, BATADV_ATTR_HARD_IFNAME, 472 net_dev->name) || 473 nla_put(msg, BATADV_ATTR_HARD_ADDRESS, ETH_ALEN, 474 net_dev->dev_addr)) 475 goto nla_put_failure; 476 477 if (hard_iface->if_status == BATADV_IF_ACTIVE) { 478 if (nla_put_flag(msg, BATADV_ATTR_ACTIVE)) 479 goto nla_put_failure; 480 } 481 482 genlmsg_end(msg, hdr); 483 return 0; 484 485 nla_put_failure: 486 genlmsg_cancel(msg, hdr); 487 return -EMSGSIZE; 488 } 489 490 /** 491 * batadv_netlink_dump_hardifs() - Dump all hard interface into a messages 492 * @msg: Netlink message to dump into 493 * @cb: Parameters from query 494 * 495 * Return: error code, or length of reply message on success 496 */ 497 static int 498 batadv_netlink_dump_hardifs(struct sk_buff *msg, struct netlink_callback *cb) 499 { 500 struct net *net = sock_net(cb->skb->sk); 501 struct net_device *soft_iface; 502 struct batadv_hard_iface *hard_iface; 503 int ifindex; 504 int portid = NETLINK_CB(cb->skb).portid; 505 int skip = cb->args[0]; 506 int i = 0; 507 508 ifindex = batadv_netlink_get_ifindex(cb->nlh, 509 BATADV_ATTR_MESH_IFINDEX); 510 if (!ifindex) 511 return -EINVAL; 512 513 soft_iface = dev_get_by_index(net, ifindex); 514 if (!soft_iface) 515 return -ENODEV; 516 517 if (!batadv_softif_is_valid(soft_iface)) { 518 dev_put(soft_iface); 519 return -ENODEV; 520 } 521 522 rtnl_lock(); 523 cb->seq = batadv_hardif_generation << 1 | 1; 524 525 list_for_each_entry(hard_iface, &batadv_hardif_list, list) { 526 if (hard_iface->soft_iface != soft_iface) 527 continue; 528 529 if (i++ < skip) 530 continue; 531 532 if (batadv_netlink_dump_hardif_entry(msg, portid, cb, 533 hard_iface)) { 534 i--; 535 break; 536 } 537 } 538 539 rtnl_unlock(); 540 541 dev_put(soft_iface); 542 543 cb->args[0] = i; 544 545 return msg->len; 546 } 547 548 static const struct genl_ops batadv_netlink_ops[] = { 549 { 550 .cmd = BATADV_CMD_GET_MESH_INFO, 551 .flags = GENL_ADMIN_PERM, 552 .policy = batadv_netlink_policy, 553 .doit = batadv_netlink_get_mesh_info, 554 }, 555 { 556 .cmd = BATADV_CMD_TP_METER, 557 .flags = GENL_ADMIN_PERM, 558 .policy = batadv_netlink_policy, 559 .doit = batadv_netlink_tp_meter_start, 560 }, 561 { 562 .cmd = BATADV_CMD_TP_METER_CANCEL, 563 .flags = GENL_ADMIN_PERM, 564 .policy = batadv_netlink_policy, 565 .doit = batadv_netlink_tp_meter_cancel, 566 }, 567 { 568 .cmd = BATADV_CMD_GET_ROUTING_ALGOS, 569 .flags = GENL_ADMIN_PERM, 570 .policy = batadv_netlink_policy, 571 .dumpit = batadv_algo_dump, 572 }, 573 { 574 .cmd = BATADV_CMD_GET_HARDIFS, 575 .flags = GENL_ADMIN_PERM, 576 .policy = batadv_netlink_policy, 577 .dumpit = batadv_netlink_dump_hardifs, 578 }, 579 { 580 .cmd = BATADV_CMD_GET_TRANSTABLE_LOCAL, 581 .flags = GENL_ADMIN_PERM, 582 .policy = batadv_netlink_policy, 583 .dumpit = batadv_tt_local_dump, 584 }, 585 { 586 .cmd = BATADV_CMD_GET_TRANSTABLE_GLOBAL, 587 .flags = GENL_ADMIN_PERM, 588 .policy = batadv_netlink_policy, 589 .dumpit = batadv_tt_global_dump, 590 }, 591 { 592 .cmd = BATADV_CMD_GET_ORIGINATORS, 593 .flags = GENL_ADMIN_PERM, 594 .policy = batadv_netlink_policy, 595 .dumpit = batadv_orig_dump, 596 }, 597 { 598 .cmd = BATADV_CMD_GET_NEIGHBORS, 599 .flags = GENL_ADMIN_PERM, 600 .policy = batadv_netlink_policy, 601 .dumpit = batadv_hardif_neigh_dump, 602 }, 603 { 604 .cmd = BATADV_CMD_GET_GATEWAYS, 605 .flags = GENL_ADMIN_PERM, 606 .policy = batadv_netlink_policy, 607 .dumpit = batadv_gw_dump, 608 }, 609 { 610 .cmd = BATADV_CMD_GET_BLA_CLAIM, 611 .flags = GENL_ADMIN_PERM, 612 .policy = batadv_netlink_policy, 613 .dumpit = batadv_bla_claim_dump, 614 }, 615 { 616 .cmd = BATADV_CMD_GET_BLA_BACKBONE, 617 .flags = GENL_ADMIN_PERM, 618 .policy = batadv_netlink_policy, 619 .dumpit = batadv_bla_backbone_dump, 620 }, 621 { 622 .cmd = BATADV_CMD_GET_DAT_CACHE, 623 .flags = GENL_ADMIN_PERM, 624 .policy = batadv_netlink_policy, 625 .dumpit = batadv_dat_cache_dump, 626 }, 627 { 628 .cmd = BATADV_CMD_GET_MCAST_FLAGS, 629 .flags = GENL_ADMIN_PERM, 630 .policy = batadv_netlink_policy, 631 .dumpit = batadv_mcast_flags_dump, 632 }, 633 634 }; 635 636 struct genl_family batadv_netlink_family __ro_after_init = { 637 .hdrsize = 0, 638 .name = BATADV_NL_NAME, 639 .version = 1, 640 .maxattr = BATADV_ATTR_MAX, 641 .netnsok = true, 642 .module = THIS_MODULE, 643 .ops = batadv_netlink_ops, 644 .n_ops = ARRAY_SIZE(batadv_netlink_ops), 645 .mcgrps = batadv_netlink_mcgrps, 646 .n_mcgrps = ARRAY_SIZE(batadv_netlink_mcgrps), 647 }; 648 649 /** 650 * batadv_netlink_register() - register batadv genl netlink family 651 */ 652 void __init batadv_netlink_register(void) 653 { 654 int ret; 655 656 ret = genl_register_family(&batadv_netlink_family); 657 if (ret) 658 pr_warn("unable to register netlink family"); 659 } 660 661 /** 662 * batadv_netlink_unregister() - unregister batadv genl netlink family 663 */ 664 void batadv_netlink_unregister(void) 665 { 666 genl_unregister_family(&batadv_netlink_family); 667 } 668