1 /* 2 * NetLabel Management Support 3 * 4 * This file defines the management functions for the NetLabel system. The 5 * NetLabel system manages static and dynamic label mappings for network 6 * protocols such as CIPSO and RIPSO. 7 * 8 * Author: Paul Moore <paul.moore@hp.com> 9 * 10 */ 11 12 /* 13 * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 14 * 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 23 * the GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 * 29 */ 30 31 #include <linux/types.h> 32 #include <linux/socket.h> 33 #include <linux/string.h> 34 #include <linux/skbuff.h> 35 #include <net/sock.h> 36 #include <net/netlink.h> 37 #include <net/genetlink.h> 38 #include <net/netlabel.h> 39 #include <net/cipso_ipv4.h> 40 41 #include "netlabel_domainhash.h" 42 #include "netlabel_user.h" 43 #include "netlabel_mgmt.h" 44 45 /* NetLabel configured protocol count */ 46 static DEFINE_SPINLOCK(netlabel_mgmt_protocount_lock); 47 static u32 netlabel_mgmt_protocount = 0; 48 49 /* Argument struct for netlbl_domhsh_walk() */ 50 struct netlbl_domhsh_walk_arg { 51 struct netlink_callback *nl_cb; 52 struct sk_buff *skb; 53 u32 seq; 54 }; 55 56 /* NetLabel Generic NETLINK CIPSOv4 family */ 57 static struct genl_family netlbl_mgmt_gnl_family = { 58 .id = GENL_ID_GENERATE, 59 .hdrsize = 0, 60 .name = NETLBL_NLTYPE_MGMT_NAME, 61 .version = NETLBL_PROTO_VERSION, 62 .maxattr = NLBL_MGMT_A_MAX, 63 }; 64 65 /* NetLabel Netlink attribute policy */ 66 static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { 67 [NLBL_MGMT_A_DOMAIN] = { .type = NLA_NUL_STRING }, 68 [NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 }, 69 [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 }, 70 [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 }, 71 }; 72 73 /* 74 * NetLabel Misc Managment Functions 75 */ 76 77 /** 78 * netlbl_mgmt_protocount_inc - Increment the configured labeled protocol count 79 * 80 * Description: 81 * Increment the number of labeled protocol configurations in the current 82 * NetLabel configuration. Keep track of this for use in determining if 83 * NetLabel label enforcement should be active/enabled or not in the LSM. 84 * 85 */ 86 void netlbl_mgmt_protocount_inc(void) 87 { 88 spin_lock(&netlabel_mgmt_protocount_lock); 89 netlabel_mgmt_protocount++; 90 spin_unlock(&netlabel_mgmt_protocount_lock); 91 } 92 93 /** 94 * netlbl_mgmt_protocount_dec - Decrement the configured labeled protocol count 95 * 96 * Description: 97 * Decrement the number of labeled protocol configurations in the current 98 * NetLabel configuration. Keep track of this for use in determining if 99 * NetLabel label enforcement should be active/enabled or not in the LSM. 100 * 101 */ 102 void netlbl_mgmt_protocount_dec(void) 103 { 104 spin_lock(&netlabel_mgmt_protocount_lock); 105 if (netlabel_mgmt_protocount > 0) 106 netlabel_mgmt_protocount--; 107 spin_unlock(&netlabel_mgmt_protocount_lock); 108 } 109 110 /** 111 * netlbl_mgmt_protocount_value - Return the number of configured protocols 112 * 113 * Description: 114 * Return the number of labeled protocols in the current NetLabel 115 * configuration. This value is useful in determining if NetLabel label 116 * enforcement should be active/enabled or not in the LSM. 117 * 118 */ 119 u32 netlbl_mgmt_protocount_value(void) 120 { 121 u32 val; 122 123 rcu_read_lock(); 124 val = netlabel_mgmt_protocount; 125 rcu_read_unlock(); 126 127 return val; 128 } 129 130 /* 131 * NetLabel Command Handlers 132 */ 133 134 /** 135 * netlbl_mgmt_add - Handle an ADD message 136 * @skb: the NETLINK buffer 137 * @info: the Generic NETLINK info block 138 * 139 * Description: 140 * Process a user generated ADD message and add the domains from the message 141 * to the hash table. See netlabel.h for a description of the message format. 142 * Returns zero on success, negative values on failure. 143 * 144 */ 145 static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) 146 { 147 int ret_val = -EINVAL; 148 struct netlbl_dom_map *entry = NULL; 149 size_t tmp_size; 150 u32 tmp_val; 151 struct netlbl_audit audit_info; 152 153 if (!info->attrs[NLBL_MGMT_A_DOMAIN] || 154 !info->attrs[NLBL_MGMT_A_PROTOCOL]) 155 goto add_failure; 156 157 netlbl_netlink_auditinfo(skb, &audit_info); 158 159 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 160 if (entry == NULL) { 161 ret_val = -ENOMEM; 162 goto add_failure; 163 } 164 tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]); 165 entry->domain = kmalloc(tmp_size, GFP_KERNEL); 166 if (entry->domain == NULL) { 167 ret_val = -ENOMEM; 168 goto add_failure; 169 } 170 entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); 171 nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size); 172 173 switch (entry->type) { 174 case NETLBL_NLTYPE_UNLABELED: 175 ret_val = netlbl_domhsh_add(entry, &audit_info); 176 break; 177 case NETLBL_NLTYPE_CIPSOV4: 178 if (!info->attrs[NLBL_MGMT_A_CV4DOI]) 179 goto add_failure; 180 181 tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); 182 /* We should be holding a rcu_read_lock() here while we hold 183 * the result but since the entry will always be deleted when 184 * the CIPSO DOI is deleted we aren't going to keep the 185 * lock. */ 186 rcu_read_lock(); 187 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); 188 if (entry->type_def.cipsov4 == NULL) { 189 rcu_read_unlock(); 190 goto add_failure; 191 } 192 ret_val = netlbl_domhsh_add(entry, &audit_info); 193 rcu_read_unlock(); 194 break; 195 default: 196 goto add_failure; 197 } 198 if (ret_val != 0) 199 goto add_failure; 200 201 return 0; 202 203 add_failure: 204 if (entry) 205 kfree(entry->domain); 206 kfree(entry); 207 return ret_val; 208 } 209 210 /** 211 * netlbl_mgmt_remove - Handle a REMOVE message 212 * @skb: the NETLINK buffer 213 * @info: the Generic NETLINK info block 214 * 215 * Description: 216 * Process a user generated REMOVE message and remove the specified domain 217 * mappings. Returns zero on success, negative values on failure. 218 * 219 */ 220 static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) 221 { 222 char *domain; 223 struct netlbl_audit audit_info; 224 225 if (!info->attrs[NLBL_MGMT_A_DOMAIN]) 226 return -EINVAL; 227 228 netlbl_netlink_auditinfo(skb, &audit_info); 229 230 domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); 231 return netlbl_domhsh_remove(domain, &audit_info); 232 } 233 234 /** 235 * netlbl_mgmt_listall_cb - netlbl_domhsh_walk() callback for LISTALL 236 * @entry: the domain mapping hash table entry 237 * @arg: the netlbl_domhsh_walk_arg structure 238 * 239 * Description: 240 * This function is designed to be used as a callback to the 241 * netlbl_domhsh_walk() function for use in generating a response for a LISTALL 242 * message. Returns the size of the message on success, negative values on 243 * failure. 244 * 245 */ 246 static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) 247 { 248 int ret_val = -ENOMEM; 249 struct netlbl_domhsh_walk_arg *cb_arg = arg; 250 void *data; 251 252 data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).pid, 253 cb_arg->seq, &netlbl_mgmt_gnl_family, 254 NLM_F_MULTI, NLBL_MGMT_C_LISTALL); 255 if (data == NULL) 256 goto listall_cb_failure; 257 258 ret_val = nla_put_string(cb_arg->skb, 259 NLBL_MGMT_A_DOMAIN, 260 entry->domain); 261 if (ret_val != 0) 262 goto listall_cb_failure; 263 ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type); 264 if (ret_val != 0) 265 goto listall_cb_failure; 266 switch (entry->type) { 267 case NETLBL_NLTYPE_CIPSOV4: 268 ret_val = nla_put_u32(cb_arg->skb, 269 NLBL_MGMT_A_CV4DOI, 270 entry->type_def.cipsov4->doi); 271 if (ret_val != 0) 272 goto listall_cb_failure; 273 break; 274 } 275 276 cb_arg->seq++; 277 return genlmsg_end(cb_arg->skb, data); 278 279 listall_cb_failure: 280 genlmsg_cancel(cb_arg->skb, data); 281 return ret_val; 282 } 283 284 /** 285 * netlbl_mgmt_listall - Handle a LISTALL message 286 * @skb: the NETLINK buffer 287 * @cb: the NETLINK callback 288 * 289 * Description: 290 * Process a user generated LISTALL message and dumps the domain hash table in 291 * a form suitable for use in a kernel generated LISTALL message. Returns zero 292 * on success, negative values on failure. 293 * 294 */ 295 static int netlbl_mgmt_listall(struct sk_buff *skb, 296 struct netlink_callback *cb) 297 { 298 struct netlbl_domhsh_walk_arg cb_arg; 299 u32 skip_bkt = cb->args[0]; 300 u32 skip_chain = cb->args[1]; 301 302 cb_arg.nl_cb = cb; 303 cb_arg.skb = skb; 304 cb_arg.seq = cb->nlh->nlmsg_seq; 305 306 netlbl_domhsh_walk(&skip_bkt, 307 &skip_chain, 308 netlbl_mgmt_listall_cb, 309 &cb_arg); 310 311 cb->args[0] = skip_bkt; 312 cb->args[1] = skip_chain; 313 return skb->len; 314 } 315 316 /** 317 * netlbl_mgmt_adddef - Handle an ADDDEF message 318 * @skb: the NETLINK buffer 319 * @info: the Generic NETLINK info block 320 * 321 * Description: 322 * Process a user generated ADDDEF message and respond accordingly. Returns 323 * zero on success, negative values on failure. 324 * 325 */ 326 static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) 327 { 328 int ret_val = -EINVAL; 329 struct netlbl_dom_map *entry = NULL; 330 u32 tmp_val; 331 struct netlbl_audit audit_info; 332 333 if (!info->attrs[NLBL_MGMT_A_PROTOCOL]) 334 goto adddef_failure; 335 336 netlbl_netlink_auditinfo(skb, &audit_info); 337 338 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 339 if (entry == NULL) { 340 ret_val = -ENOMEM; 341 goto adddef_failure; 342 } 343 entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); 344 345 switch (entry->type) { 346 case NETLBL_NLTYPE_UNLABELED: 347 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 348 break; 349 case NETLBL_NLTYPE_CIPSOV4: 350 if (!info->attrs[NLBL_MGMT_A_CV4DOI]) 351 goto adddef_failure; 352 353 tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); 354 /* We should be holding a rcu_read_lock() here while we hold 355 * the result but since the entry will always be deleted when 356 * the CIPSO DOI is deleted we aren't going to keep the 357 * lock. */ 358 rcu_read_lock(); 359 entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); 360 if (entry->type_def.cipsov4 == NULL) { 361 rcu_read_unlock(); 362 goto adddef_failure; 363 } 364 ret_val = netlbl_domhsh_add_default(entry, &audit_info); 365 rcu_read_unlock(); 366 break; 367 default: 368 goto adddef_failure; 369 } 370 if (ret_val != 0) 371 goto adddef_failure; 372 373 return 0; 374 375 adddef_failure: 376 kfree(entry); 377 return ret_val; 378 } 379 380 /** 381 * netlbl_mgmt_removedef - Handle a REMOVEDEF message 382 * @skb: the NETLINK buffer 383 * @info: the Generic NETLINK info block 384 * 385 * Description: 386 * Process a user generated REMOVEDEF message and remove the default domain 387 * mapping. Returns zero on success, negative values on failure. 388 * 389 */ 390 static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) 391 { 392 struct netlbl_audit audit_info; 393 394 netlbl_netlink_auditinfo(skb, &audit_info); 395 396 return netlbl_domhsh_remove_default(&audit_info); 397 } 398 399 /** 400 * netlbl_mgmt_listdef - Handle a LISTDEF message 401 * @skb: the NETLINK buffer 402 * @info: the Generic NETLINK info block 403 * 404 * Description: 405 * Process a user generated LISTDEF message and dumps the default domain 406 * mapping in a form suitable for use in a kernel generated LISTDEF message. 407 * Returns zero on success, negative values on failure. 408 * 409 */ 410 static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) 411 { 412 int ret_val = -ENOMEM; 413 struct sk_buff *ans_skb = NULL; 414 void *data; 415 struct netlbl_dom_map *entry; 416 417 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 418 if (ans_skb == NULL) 419 return -ENOMEM; 420 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 421 0, NLBL_MGMT_C_LISTDEF); 422 if (data == NULL) 423 goto listdef_failure; 424 425 rcu_read_lock(); 426 entry = netlbl_domhsh_getentry(NULL); 427 if (entry == NULL) { 428 ret_val = -ENOENT; 429 goto listdef_failure_lock; 430 } 431 ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type); 432 if (ret_val != 0) 433 goto listdef_failure_lock; 434 switch (entry->type) { 435 case NETLBL_NLTYPE_CIPSOV4: 436 ret_val = nla_put_u32(ans_skb, 437 NLBL_MGMT_A_CV4DOI, 438 entry->type_def.cipsov4->doi); 439 if (ret_val != 0) 440 goto listdef_failure_lock; 441 break; 442 } 443 rcu_read_unlock(); 444 445 genlmsg_end(ans_skb, data); 446 447 ret_val = genlmsg_reply(ans_skb, info); 448 if (ret_val != 0) 449 goto listdef_failure; 450 return 0; 451 452 listdef_failure_lock: 453 rcu_read_unlock(); 454 listdef_failure: 455 kfree_skb(ans_skb); 456 return ret_val; 457 } 458 459 /** 460 * netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response 461 * @skb: the skb to write to 462 * @seq: the NETLINK sequence number 463 * @cb: the NETLINK callback 464 * @protocol: the NetLabel protocol to use in the message 465 * 466 * Description: 467 * This function is to be used in conjunction with netlbl_mgmt_protocols() to 468 * answer a application's PROTOCOLS message. Returns the size of the message 469 * on success, negative values on failure. 470 * 471 */ 472 static int netlbl_mgmt_protocols_cb(struct sk_buff *skb, 473 struct netlink_callback *cb, 474 u32 protocol) 475 { 476 int ret_val = -ENOMEM; 477 void *data; 478 479 data = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, 480 &netlbl_mgmt_gnl_family, NLM_F_MULTI, 481 NLBL_MGMT_C_PROTOCOLS); 482 if (data == NULL) 483 goto protocols_cb_failure; 484 485 ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, protocol); 486 if (ret_val != 0) 487 goto protocols_cb_failure; 488 489 return genlmsg_end(skb, data); 490 491 protocols_cb_failure: 492 genlmsg_cancel(skb, data); 493 return ret_val; 494 } 495 496 /** 497 * netlbl_mgmt_protocols - Handle a PROTOCOLS message 498 * @skb: the NETLINK buffer 499 * @cb: the NETLINK callback 500 * 501 * Description: 502 * Process a user generated PROTOCOLS message and respond accordingly. 503 * 504 */ 505 static int netlbl_mgmt_protocols(struct sk_buff *skb, 506 struct netlink_callback *cb) 507 { 508 u32 protos_sent = cb->args[0]; 509 510 if (protos_sent == 0) { 511 if (netlbl_mgmt_protocols_cb(skb, 512 cb, 513 NETLBL_NLTYPE_UNLABELED) < 0) 514 goto protocols_return; 515 protos_sent++; 516 } 517 if (protos_sent == 1) { 518 if (netlbl_mgmt_protocols_cb(skb, 519 cb, 520 NETLBL_NLTYPE_CIPSOV4) < 0) 521 goto protocols_return; 522 protos_sent++; 523 } 524 525 protocols_return: 526 cb->args[0] = protos_sent; 527 return skb->len; 528 } 529 530 /** 531 * netlbl_mgmt_version - Handle a VERSION message 532 * @skb: the NETLINK buffer 533 * @info: the Generic NETLINK info block 534 * 535 * Description: 536 * Process a user generated VERSION message and respond accordingly. Returns 537 * zero on success, negative values on failure. 538 * 539 */ 540 static int netlbl_mgmt_version(struct sk_buff *skb, struct genl_info *info) 541 { 542 int ret_val = -ENOMEM; 543 struct sk_buff *ans_skb = NULL; 544 void *data; 545 546 ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 547 if (ans_skb == NULL) 548 return -ENOMEM; 549 data = genlmsg_put_reply(ans_skb, info, &netlbl_mgmt_gnl_family, 550 0, NLBL_MGMT_C_VERSION); 551 if (data == NULL) 552 goto version_failure; 553 554 ret_val = nla_put_u32(ans_skb, 555 NLBL_MGMT_A_VERSION, 556 NETLBL_PROTO_VERSION); 557 if (ret_val != 0) 558 goto version_failure; 559 560 genlmsg_end(ans_skb, data); 561 562 ret_val = genlmsg_reply(ans_skb, info); 563 if (ret_val != 0) 564 goto version_failure; 565 return 0; 566 567 version_failure: 568 kfree_skb(ans_skb); 569 return ret_val; 570 } 571 572 573 /* 574 * NetLabel Generic NETLINK Command Definitions 575 */ 576 577 static struct genl_ops netlbl_mgmt_genl_c_add = { 578 .cmd = NLBL_MGMT_C_ADD, 579 .flags = GENL_ADMIN_PERM, 580 .policy = netlbl_mgmt_genl_policy, 581 .doit = netlbl_mgmt_add, 582 .dumpit = NULL, 583 }; 584 585 static struct genl_ops netlbl_mgmt_genl_c_remove = { 586 .cmd = NLBL_MGMT_C_REMOVE, 587 .flags = GENL_ADMIN_PERM, 588 .policy = netlbl_mgmt_genl_policy, 589 .doit = netlbl_mgmt_remove, 590 .dumpit = NULL, 591 }; 592 593 static struct genl_ops netlbl_mgmt_genl_c_listall = { 594 .cmd = NLBL_MGMT_C_LISTALL, 595 .flags = 0, 596 .policy = netlbl_mgmt_genl_policy, 597 .doit = NULL, 598 .dumpit = netlbl_mgmt_listall, 599 }; 600 601 static struct genl_ops netlbl_mgmt_genl_c_adddef = { 602 .cmd = NLBL_MGMT_C_ADDDEF, 603 .flags = GENL_ADMIN_PERM, 604 .policy = netlbl_mgmt_genl_policy, 605 .doit = netlbl_mgmt_adddef, 606 .dumpit = NULL, 607 }; 608 609 static struct genl_ops netlbl_mgmt_genl_c_removedef = { 610 .cmd = NLBL_MGMT_C_REMOVEDEF, 611 .flags = GENL_ADMIN_PERM, 612 .policy = netlbl_mgmt_genl_policy, 613 .doit = netlbl_mgmt_removedef, 614 .dumpit = NULL, 615 }; 616 617 static struct genl_ops netlbl_mgmt_genl_c_listdef = { 618 .cmd = NLBL_MGMT_C_LISTDEF, 619 .flags = 0, 620 .policy = netlbl_mgmt_genl_policy, 621 .doit = netlbl_mgmt_listdef, 622 .dumpit = NULL, 623 }; 624 625 static struct genl_ops netlbl_mgmt_genl_c_protocols = { 626 .cmd = NLBL_MGMT_C_PROTOCOLS, 627 .flags = 0, 628 .policy = netlbl_mgmt_genl_policy, 629 .doit = NULL, 630 .dumpit = netlbl_mgmt_protocols, 631 }; 632 633 static struct genl_ops netlbl_mgmt_genl_c_version = { 634 .cmd = NLBL_MGMT_C_VERSION, 635 .flags = 0, 636 .policy = netlbl_mgmt_genl_policy, 637 .doit = netlbl_mgmt_version, 638 .dumpit = NULL, 639 }; 640 641 /* 642 * NetLabel Generic NETLINK Protocol Functions 643 */ 644 645 /** 646 * netlbl_mgmt_genl_init - Register the NetLabel management component 647 * 648 * Description: 649 * Register the NetLabel management component with the Generic NETLINK 650 * mechanism. Returns zero on success, negative values on failure. 651 * 652 */ 653 int netlbl_mgmt_genl_init(void) 654 { 655 int ret_val; 656 657 ret_val = genl_register_family(&netlbl_mgmt_gnl_family); 658 if (ret_val != 0) 659 return ret_val; 660 661 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 662 &netlbl_mgmt_genl_c_add); 663 if (ret_val != 0) 664 return ret_val; 665 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 666 &netlbl_mgmt_genl_c_remove); 667 if (ret_val != 0) 668 return ret_val; 669 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 670 &netlbl_mgmt_genl_c_listall); 671 if (ret_val != 0) 672 return ret_val; 673 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 674 &netlbl_mgmt_genl_c_adddef); 675 if (ret_val != 0) 676 return ret_val; 677 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 678 &netlbl_mgmt_genl_c_removedef); 679 if (ret_val != 0) 680 return ret_val; 681 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 682 &netlbl_mgmt_genl_c_listdef); 683 if (ret_val != 0) 684 return ret_val; 685 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 686 &netlbl_mgmt_genl_c_protocols); 687 if (ret_val != 0) 688 return ret_val; 689 ret_val = genl_register_ops(&netlbl_mgmt_gnl_family, 690 &netlbl_mgmt_genl_c_version); 691 if (ret_val != 0) 692 return ret_val; 693 694 return 0; 695 } 696