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