netlabel_mgmt.c 16 KB

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