Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: ioam: IOAM Generic Netlink API

Add Generic Netlink commands to allow userspace to configure IOAM
namespaces and schemas. The target is iproute2 and the patch is ready.
It will be posted as soon as this patchset is merged. Here is an overview:

$ ip ioam
Usage: ip ioam { COMMAND | help }
ip ioam namespace show
ip ioam namespace add ID [ data DATA32 ] [ wide DATA64 ]
ip ioam namespace del ID
ip ioam schema show
ip ioam schema add ID DATA
ip ioam schema del ID
ip ioam namespace set ID schema { ID | none }

Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Justin Iurman and committed by
David S. Miller
8c6f6fa6 9ee11f0f

+625 -3
+13
include/linux/ioam6_genl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * IPv6 IOAM Generic Netlink API 4 + * 5 + * Author: 6 + * Justin Iurman <justin.iurman@uliege.be> 7 + */ 8 + #ifndef _LINUX_IOAM6_GENL_H 9 + #define _LINUX_IOAM6_GENL_H 10 + 11 + #include <uapi/linux/ioam6_genl.h> 12 + 13 + #endif /* _LINUX_IOAM6_GENL_H */
+52
include/uapi/linux/ioam6_genl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ 2 + /* 3 + * IPv6 IOAM Generic Netlink API 4 + * 5 + * Author: 6 + * Justin Iurman <justin.iurman@uliege.be> 7 + */ 8 + 9 + #ifndef _UAPI_LINUX_IOAM6_GENL_H 10 + #define _UAPI_LINUX_IOAM6_GENL_H 11 + 12 + #define IOAM6_GENL_NAME "IOAM6" 13 + #define IOAM6_GENL_VERSION 0x1 14 + 15 + enum { 16 + IOAM6_ATTR_UNSPEC, 17 + 18 + IOAM6_ATTR_NS_ID, /* u16 */ 19 + IOAM6_ATTR_NS_DATA, /* u32 */ 20 + IOAM6_ATTR_NS_DATA_WIDE,/* u64 */ 21 + 22 + #define IOAM6_MAX_SCHEMA_DATA_LEN (255 * 4) 23 + IOAM6_ATTR_SC_ID, /* u32 */ 24 + IOAM6_ATTR_SC_DATA, /* Binary */ 25 + IOAM6_ATTR_SC_NONE, /* Flag */ 26 + 27 + IOAM6_ATTR_PAD, 28 + 29 + __IOAM6_ATTR_MAX, 30 + }; 31 + 32 + #define IOAM6_ATTR_MAX (__IOAM6_ATTR_MAX - 1) 33 + 34 + enum { 35 + IOAM6_CMD_UNSPEC, 36 + 37 + IOAM6_CMD_ADD_NAMESPACE, 38 + IOAM6_CMD_DEL_NAMESPACE, 39 + IOAM6_CMD_DUMP_NAMESPACES, 40 + 41 + IOAM6_CMD_ADD_SCHEMA, 42 + IOAM6_CMD_DEL_SCHEMA, 43 + IOAM6_CMD_DUMP_SCHEMAS, 44 + 45 + IOAM6_CMD_NS_SET_SCHEMA, 46 + 47 + __IOAM6_CMD_MAX, 48 + }; 49 + 50 + #define IOAM6_CMD_MAX (__IOAM6_CMD_MAX - 1) 51 + 52 + #endif /* _UAPI_LINUX_IOAM6_GENL_H */
+560 -3
net/ipv6/ioam6.c
··· 11 11 #include <linux/kernel.h> 12 12 #include <linux/net.h> 13 13 #include <linux/ioam6.h> 14 + #include <linux/ioam6_genl.h> 14 15 #include <linux/rhashtable.h> 15 16 16 17 #include <net/addrconf.h> 18 + #include <net/genetlink.h> 17 19 #include <net/ioam6.h> 18 20 19 21 static void ioam6_ns_release(struct ioam6_namespace *ns) ··· 72 70 .head_offset = offsetof(struct ioam6_schema, head), 73 71 .automatic_shrinking = true, 74 72 .obj_cmpfn = ioam6_sc_cmpfn, 73 + }; 74 + 75 + static struct genl_family ioam6_genl_family; 76 + 77 + static const struct nla_policy ioam6_genl_policy_addns[] = { 78 + [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 }, 79 + [IOAM6_ATTR_NS_DATA] = { .type = NLA_U32 }, 80 + [IOAM6_ATTR_NS_DATA_WIDE] = { .type = NLA_U64 }, 81 + }; 82 + 83 + static const struct nla_policy ioam6_genl_policy_delns[] = { 84 + [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 }, 85 + }; 86 + 87 + static const struct nla_policy ioam6_genl_policy_addsc[] = { 88 + [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 }, 89 + [IOAM6_ATTR_SC_DATA] = { .type = NLA_BINARY, 90 + .len = IOAM6_MAX_SCHEMA_DATA_LEN }, 91 + }; 92 + 93 + static const struct nla_policy ioam6_genl_policy_delsc[] = { 94 + [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 }, 95 + }; 96 + 97 + static const struct nla_policy ioam6_genl_policy_ns_sc[] = { 98 + [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 }, 99 + [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 }, 100 + [IOAM6_ATTR_SC_NONE] = { .type = NLA_FLAG }, 101 + }; 102 + 103 + static int ioam6_genl_addns(struct sk_buff *skb, struct genl_info *info) 104 + { 105 + struct ioam6_pernet_data *nsdata; 106 + struct ioam6_namespace *ns; 107 + u64 data64; 108 + u32 data32; 109 + __be16 id; 110 + int err; 111 + 112 + if (!info->attrs[IOAM6_ATTR_NS_ID]) 113 + return -EINVAL; 114 + 115 + id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); 116 + nsdata = ioam6_pernet(genl_info_net(info)); 117 + 118 + mutex_lock(&nsdata->lock); 119 + 120 + ns = rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params); 121 + if (ns) { 122 + err = -EEXIST; 123 + goto out_unlock; 124 + } 125 + 126 + ns = kzalloc(sizeof(*ns), GFP_KERNEL); 127 + if (!ns) { 128 + err = -ENOMEM; 129 + goto out_unlock; 130 + } 131 + 132 + ns->id = id; 133 + 134 + if (!info->attrs[IOAM6_ATTR_NS_DATA]) 135 + data32 = IOAM6_U32_UNAVAILABLE; 136 + else 137 + data32 = nla_get_u32(info->attrs[IOAM6_ATTR_NS_DATA]); 138 + 139 + if (!info->attrs[IOAM6_ATTR_NS_DATA_WIDE]) 140 + data64 = IOAM6_U64_UNAVAILABLE; 141 + else 142 + data64 = nla_get_u64(info->attrs[IOAM6_ATTR_NS_DATA_WIDE]); 143 + 144 + ns->data = cpu_to_be32(data32); 145 + ns->data_wide = cpu_to_be64(data64); 146 + 147 + err = rhashtable_lookup_insert_fast(&nsdata->namespaces, &ns->head, 148 + rht_ns_params); 149 + if (err) 150 + kfree(ns); 151 + 152 + out_unlock: 153 + mutex_unlock(&nsdata->lock); 154 + return err; 155 + } 156 + 157 + static int ioam6_genl_delns(struct sk_buff *skb, struct genl_info *info) 158 + { 159 + struct ioam6_pernet_data *nsdata; 160 + struct ioam6_namespace *ns; 161 + struct ioam6_schema *sc; 162 + __be16 id; 163 + int err; 164 + 165 + if (!info->attrs[IOAM6_ATTR_NS_ID]) 166 + return -EINVAL; 167 + 168 + id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); 169 + nsdata = ioam6_pernet(genl_info_net(info)); 170 + 171 + mutex_lock(&nsdata->lock); 172 + 173 + ns = rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params); 174 + if (!ns) { 175 + err = -ENOENT; 176 + goto out_unlock; 177 + } 178 + 179 + sc = rcu_dereference_protected(ns->schema, 180 + lockdep_is_held(&nsdata->lock)); 181 + 182 + err = rhashtable_remove_fast(&nsdata->namespaces, &ns->head, 183 + rht_ns_params); 184 + if (err) 185 + goto out_unlock; 186 + 187 + if (sc) 188 + rcu_assign_pointer(sc->ns, NULL); 189 + 190 + ioam6_ns_release(ns); 191 + 192 + out_unlock: 193 + mutex_unlock(&nsdata->lock); 194 + return err; 195 + } 196 + 197 + static int __ioam6_genl_dumpns_element(struct ioam6_namespace *ns, 198 + u32 portid, 199 + u32 seq, 200 + u32 flags, 201 + struct sk_buff *skb, 202 + u8 cmd) 203 + { 204 + struct ioam6_schema *sc; 205 + u64 data64; 206 + u32 data32; 207 + void *hdr; 208 + 209 + hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd); 210 + if (!hdr) 211 + return -ENOMEM; 212 + 213 + data32 = be32_to_cpu(ns->data); 214 + data64 = be64_to_cpu(ns->data_wide); 215 + 216 + if (nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id)) || 217 + (data32 != IOAM6_U32_UNAVAILABLE && 218 + nla_put_u32(skb, IOAM6_ATTR_NS_DATA, data32)) || 219 + (data64 != IOAM6_U64_UNAVAILABLE && 220 + nla_put_u64_64bit(skb, IOAM6_ATTR_NS_DATA_WIDE, 221 + data64, IOAM6_ATTR_PAD))) 222 + goto nla_put_failure; 223 + 224 + rcu_read_lock(); 225 + 226 + sc = rcu_dereference(ns->schema); 227 + if (sc && nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id)) { 228 + rcu_read_unlock(); 229 + goto nla_put_failure; 230 + } 231 + 232 + rcu_read_unlock(); 233 + 234 + genlmsg_end(skb, hdr); 235 + return 0; 236 + 237 + nla_put_failure: 238 + genlmsg_cancel(skb, hdr); 239 + return -EMSGSIZE; 240 + } 241 + 242 + static int ioam6_genl_dumpns_start(struct netlink_callback *cb) 243 + { 244 + struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk)); 245 + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 246 + 247 + if (!iter) { 248 + iter = kmalloc(sizeof(*iter), GFP_KERNEL); 249 + if (!iter) 250 + return -ENOMEM; 251 + 252 + cb->args[0] = (long)iter; 253 + } 254 + 255 + rhashtable_walk_enter(&nsdata->namespaces, iter); 256 + 257 + return 0; 258 + } 259 + 260 + static int ioam6_genl_dumpns_done(struct netlink_callback *cb) 261 + { 262 + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 263 + 264 + rhashtable_walk_exit(iter); 265 + kfree(iter); 266 + 267 + return 0; 268 + } 269 + 270 + static int ioam6_genl_dumpns(struct sk_buff *skb, struct netlink_callback *cb) 271 + { 272 + struct rhashtable_iter *iter; 273 + struct ioam6_namespace *ns; 274 + int err; 275 + 276 + iter = (struct rhashtable_iter *)cb->args[0]; 277 + rhashtable_walk_start(iter); 278 + 279 + for (;;) { 280 + ns = rhashtable_walk_next(iter); 281 + 282 + if (IS_ERR(ns)) { 283 + if (PTR_ERR(ns) == -EAGAIN) 284 + continue; 285 + err = PTR_ERR(ns); 286 + goto done; 287 + } else if (!ns) { 288 + break; 289 + } 290 + 291 + err = __ioam6_genl_dumpns_element(ns, 292 + NETLINK_CB(cb->skb).portid, 293 + cb->nlh->nlmsg_seq, 294 + NLM_F_MULTI, 295 + skb, 296 + IOAM6_CMD_DUMP_NAMESPACES); 297 + if (err) 298 + goto done; 299 + } 300 + 301 + err = skb->len; 302 + 303 + done: 304 + rhashtable_walk_stop(iter); 305 + return err; 306 + } 307 + 308 + static int ioam6_genl_addsc(struct sk_buff *skb, struct genl_info *info) 309 + { 310 + struct ioam6_pernet_data *nsdata; 311 + int len, len_aligned, err; 312 + struct ioam6_schema *sc; 313 + u32 id; 314 + 315 + if (!info->attrs[IOAM6_ATTR_SC_ID] || !info->attrs[IOAM6_ATTR_SC_DATA]) 316 + return -EINVAL; 317 + 318 + id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]); 319 + nsdata = ioam6_pernet(genl_info_net(info)); 320 + 321 + mutex_lock(&nsdata->lock); 322 + 323 + sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params); 324 + if (sc) { 325 + err = -EEXIST; 326 + goto out_unlock; 327 + } 328 + 329 + len = nla_len(info->attrs[IOAM6_ATTR_SC_DATA]); 330 + len_aligned = ALIGN(len, 4); 331 + 332 + sc = kzalloc(sizeof(*sc) + len_aligned, GFP_KERNEL); 333 + if (!sc) { 334 + err = -ENOMEM; 335 + goto out_unlock; 336 + } 337 + 338 + sc->id = id; 339 + sc->len = len_aligned; 340 + sc->hdr = cpu_to_be32(sc->id | ((u8)(sc->len / 4) << 24)); 341 + nla_memcpy(sc->data, info->attrs[IOAM6_ATTR_SC_DATA], len); 342 + 343 + err = rhashtable_lookup_insert_fast(&nsdata->schemas, &sc->head, 344 + rht_sc_params); 345 + if (err) 346 + goto free_sc; 347 + 348 + out_unlock: 349 + mutex_unlock(&nsdata->lock); 350 + return err; 351 + free_sc: 352 + kfree(sc); 353 + goto out_unlock; 354 + } 355 + 356 + static int ioam6_genl_delsc(struct sk_buff *skb, struct genl_info *info) 357 + { 358 + struct ioam6_pernet_data *nsdata; 359 + struct ioam6_namespace *ns; 360 + struct ioam6_schema *sc; 361 + int err; 362 + u32 id; 363 + 364 + if (!info->attrs[IOAM6_ATTR_SC_ID]) 365 + return -EINVAL; 366 + 367 + id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]); 368 + nsdata = ioam6_pernet(genl_info_net(info)); 369 + 370 + mutex_lock(&nsdata->lock); 371 + 372 + sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params); 373 + if (!sc) { 374 + err = -ENOENT; 375 + goto out_unlock; 376 + } 377 + 378 + ns = rcu_dereference_protected(sc->ns, lockdep_is_held(&nsdata->lock)); 379 + 380 + err = rhashtable_remove_fast(&nsdata->schemas, &sc->head, 381 + rht_sc_params); 382 + if (err) 383 + goto out_unlock; 384 + 385 + if (ns) 386 + rcu_assign_pointer(ns->schema, NULL); 387 + 388 + ioam6_sc_release(sc); 389 + 390 + out_unlock: 391 + mutex_unlock(&nsdata->lock); 392 + return err; 393 + } 394 + 395 + static int __ioam6_genl_dumpsc_element(struct ioam6_schema *sc, 396 + u32 portid, u32 seq, u32 flags, 397 + struct sk_buff *skb, u8 cmd) 398 + { 399 + struct ioam6_namespace *ns; 400 + void *hdr; 401 + 402 + hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd); 403 + if (!hdr) 404 + return -ENOMEM; 405 + 406 + if (nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id) || 407 + nla_put(skb, IOAM6_ATTR_SC_DATA, sc->len, sc->data)) 408 + goto nla_put_failure; 409 + 410 + rcu_read_lock(); 411 + 412 + ns = rcu_dereference(sc->ns); 413 + if (ns && nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id))) { 414 + rcu_read_unlock(); 415 + goto nla_put_failure; 416 + } 417 + 418 + rcu_read_unlock(); 419 + 420 + genlmsg_end(skb, hdr); 421 + return 0; 422 + 423 + nla_put_failure: 424 + genlmsg_cancel(skb, hdr); 425 + return -EMSGSIZE; 426 + } 427 + 428 + static int ioam6_genl_dumpsc_start(struct netlink_callback *cb) 429 + { 430 + struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk)); 431 + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 432 + 433 + if (!iter) { 434 + iter = kmalloc(sizeof(*iter), GFP_KERNEL); 435 + if (!iter) 436 + return -ENOMEM; 437 + 438 + cb->args[0] = (long)iter; 439 + } 440 + 441 + rhashtable_walk_enter(&nsdata->schemas, iter); 442 + 443 + return 0; 444 + } 445 + 446 + static int ioam6_genl_dumpsc_done(struct netlink_callback *cb) 447 + { 448 + struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 449 + 450 + rhashtable_walk_exit(iter); 451 + kfree(iter); 452 + 453 + return 0; 454 + } 455 + 456 + static int ioam6_genl_dumpsc(struct sk_buff *skb, struct netlink_callback *cb) 457 + { 458 + struct rhashtable_iter *iter; 459 + struct ioam6_schema *sc; 460 + int err; 461 + 462 + iter = (struct rhashtable_iter *)cb->args[0]; 463 + rhashtable_walk_start(iter); 464 + 465 + for (;;) { 466 + sc = rhashtable_walk_next(iter); 467 + 468 + if (IS_ERR(sc)) { 469 + if (PTR_ERR(sc) == -EAGAIN) 470 + continue; 471 + err = PTR_ERR(sc); 472 + goto done; 473 + } else if (!sc) { 474 + break; 475 + } 476 + 477 + err = __ioam6_genl_dumpsc_element(sc, 478 + NETLINK_CB(cb->skb).portid, 479 + cb->nlh->nlmsg_seq, 480 + NLM_F_MULTI, 481 + skb, 482 + IOAM6_CMD_DUMP_SCHEMAS); 483 + if (err) 484 + goto done; 485 + } 486 + 487 + err = skb->len; 488 + 489 + done: 490 + rhashtable_walk_stop(iter); 491 + return err; 492 + } 493 + 494 + static int ioam6_genl_ns_set_schema(struct sk_buff *skb, struct genl_info *info) 495 + { 496 + struct ioam6_namespace *ns, *ns_ref; 497 + struct ioam6_schema *sc, *sc_ref; 498 + struct ioam6_pernet_data *nsdata; 499 + __be16 ns_id; 500 + u32 sc_id; 501 + int err; 502 + 503 + if (!info->attrs[IOAM6_ATTR_NS_ID] || 504 + (!info->attrs[IOAM6_ATTR_SC_ID] && 505 + !info->attrs[IOAM6_ATTR_SC_NONE])) 506 + return -EINVAL; 507 + 508 + ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID])); 509 + nsdata = ioam6_pernet(genl_info_net(info)); 510 + 511 + mutex_lock(&nsdata->lock); 512 + 513 + ns = rhashtable_lookup_fast(&nsdata->namespaces, &ns_id, rht_ns_params); 514 + if (!ns) { 515 + err = -ENOENT; 516 + goto out_unlock; 517 + } 518 + 519 + if (info->attrs[IOAM6_ATTR_SC_NONE]) { 520 + sc = NULL; 521 + } else { 522 + sc_id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]); 523 + sc = rhashtable_lookup_fast(&nsdata->schemas, &sc_id, 524 + rht_sc_params); 525 + if (!sc) { 526 + err = -ENOENT; 527 + goto out_unlock; 528 + } 529 + } 530 + 531 + sc_ref = rcu_dereference_protected(ns->schema, 532 + lockdep_is_held(&nsdata->lock)); 533 + if (sc_ref) 534 + rcu_assign_pointer(sc_ref->ns, NULL); 535 + rcu_assign_pointer(ns->schema, sc); 536 + 537 + if (sc) { 538 + ns_ref = rcu_dereference_protected(sc->ns, 539 + lockdep_is_held(&nsdata->lock)); 540 + if (ns_ref) 541 + rcu_assign_pointer(ns_ref->schema, NULL); 542 + rcu_assign_pointer(sc->ns, ns); 543 + } 544 + 545 + err = 0; 546 + 547 + out_unlock: 548 + mutex_unlock(&nsdata->lock); 549 + return err; 550 + } 551 + 552 + static const struct genl_ops ioam6_genl_ops[] = { 553 + { 554 + .cmd = IOAM6_CMD_ADD_NAMESPACE, 555 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 556 + .doit = ioam6_genl_addns, 557 + .flags = GENL_ADMIN_PERM, 558 + .policy = ioam6_genl_policy_addns, 559 + .maxattr = ARRAY_SIZE(ioam6_genl_policy_addns) - 1, 560 + }, 561 + { 562 + .cmd = IOAM6_CMD_DEL_NAMESPACE, 563 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 564 + .doit = ioam6_genl_delns, 565 + .flags = GENL_ADMIN_PERM, 566 + .policy = ioam6_genl_policy_delns, 567 + .maxattr = ARRAY_SIZE(ioam6_genl_policy_delns) - 1, 568 + }, 569 + { 570 + .cmd = IOAM6_CMD_DUMP_NAMESPACES, 571 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 572 + .start = ioam6_genl_dumpns_start, 573 + .dumpit = ioam6_genl_dumpns, 574 + .done = ioam6_genl_dumpns_done, 575 + .flags = GENL_ADMIN_PERM, 576 + }, 577 + { 578 + .cmd = IOAM6_CMD_ADD_SCHEMA, 579 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 580 + .doit = ioam6_genl_addsc, 581 + .flags = GENL_ADMIN_PERM, 582 + .policy = ioam6_genl_policy_addsc, 583 + .maxattr = ARRAY_SIZE(ioam6_genl_policy_addsc) - 1, 584 + }, 585 + { 586 + .cmd = IOAM6_CMD_DEL_SCHEMA, 587 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 588 + .doit = ioam6_genl_delsc, 589 + .flags = GENL_ADMIN_PERM, 590 + .policy = ioam6_genl_policy_delsc, 591 + .maxattr = ARRAY_SIZE(ioam6_genl_policy_delsc) - 1, 592 + }, 593 + { 594 + .cmd = IOAM6_CMD_DUMP_SCHEMAS, 595 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 596 + .start = ioam6_genl_dumpsc_start, 597 + .dumpit = ioam6_genl_dumpsc, 598 + .done = ioam6_genl_dumpsc_done, 599 + .flags = GENL_ADMIN_PERM, 600 + }, 601 + { 602 + .cmd = IOAM6_CMD_NS_SET_SCHEMA, 603 + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 604 + .doit = ioam6_genl_ns_set_schema, 605 + .flags = GENL_ADMIN_PERM, 606 + .policy = ioam6_genl_policy_ns_sc, 607 + .maxattr = ARRAY_SIZE(ioam6_genl_policy_ns_sc) - 1, 608 + }, 609 + }; 610 + 611 + static struct genl_family ioam6_genl_family __ro_after_init = { 612 + .name = IOAM6_GENL_NAME, 613 + .version = IOAM6_GENL_VERSION, 614 + .netnsok = true, 615 + .parallel_ops = true, 616 + .ops = ioam6_genl_ops, 617 + .n_ops = ARRAY_SIZE(ioam6_genl_ops), 618 + .module = THIS_MODULE, 75 619 }; 76 620 77 621 struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id) ··· 867 319 int __init ioam6_init(void) 868 320 { 869 321 int err = register_pernet_subsys(&ioam6_net_ops); 870 - 871 322 if (err) 872 - return err; 323 + goto out; 324 + 325 + err = genl_register_family(&ioam6_genl_family); 326 + if (err) 327 + goto out_unregister_pernet_subsys; 873 328 874 329 pr_info("In-situ OAM (IOAM) with IPv6\n"); 875 - return 0; 330 + 331 + out: 332 + return err; 333 + out_unregister_pernet_subsys: 334 + unregister_pernet_subsys(&ioam6_net_ops); 335 + goto out; 876 336 } 877 337 878 338 void ioam6_exit(void) 879 339 { 340 + genl_unregister_family(&ioam6_genl_family); 880 341 unregister_pernet_subsys(&ioam6_net_ops); 881 342 }