1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * IPv6 IOAM implementation
4 *
5 * Author:
6 * Justin Iurman <justin.iurman@uliege.be>
7 */
8
9#include <linux/errno.h>
10#include <linux/types.h>
11#include <linux/kernel.h>
12#include <linux/net.h>
13#include <linux/ioam6.h>
14#include <linux/ioam6_genl.h>
15#include <linux/rhashtable.h>
16#include <linux/netdevice.h>
17
18#include <net/addrconf.h>
19#include <net/genetlink.h>
20#include <net/ioam6.h>
21#include <net/sch_generic.h>
22
23static void ioam6_ns_release(struct ioam6_namespace *ns)
24{
25 kfree_rcu(ns, rcu);
26}
27
28static void ioam6_sc_release(struct ioam6_schema *sc)
29{
30 kfree_rcu(sc, rcu);
31}
32
33static void ioam6_free_ns(void *ptr, void *arg)
34{
35 struct ioam6_namespace *ns = (struct ioam6_namespace *)ptr;
36
37 if (ns)
38 ioam6_ns_release(ns);
39}
40
41static void ioam6_free_sc(void *ptr, void *arg)
42{
43 struct ioam6_schema *sc = (struct ioam6_schema *)ptr;
44
45 if (sc)
46 ioam6_sc_release(sc);
47}
48
49static int ioam6_ns_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
50{
51 const struct ioam6_namespace *ns = obj;
52
53 return (ns->id != *(__be16 *)arg->key);
54}
55
56static int ioam6_sc_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
57{
58 const struct ioam6_schema *sc = obj;
59
60 return (sc->id != *(u32 *)arg->key);
61}
62
63static const struct rhashtable_params rht_ns_params = {
64 .key_len = sizeof(__be16),
65 .key_offset = offsetof(struct ioam6_namespace, id),
66 .head_offset = offsetof(struct ioam6_namespace, head),
67 .automatic_shrinking = true,
68 .obj_cmpfn = ioam6_ns_cmpfn,
69};
70
71static const struct rhashtable_params rht_sc_params = {
72 .key_len = sizeof(u32),
73 .key_offset = offsetof(struct ioam6_schema, id),
74 .head_offset = offsetof(struct ioam6_schema, head),
75 .automatic_shrinking = true,
76 .obj_cmpfn = ioam6_sc_cmpfn,
77};
78
79static struct genl_family ioam6_genl_family;
80
81static const struct nla_policy ioam6_genl_policy_addns[] = {
82 [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
83 [IOAM6_ATTR_NS_DATA] = { .type = NLA_U32 },
84 [IOAM6_ATTR_NS_DATA_WIDE] = { .type = NLA_U64 },
85};
86
87static const struct nla_policy ioam6_genl_policy_delns[] = {
88 [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
89};
90
91static const struct nla_policy ioam6_genl_policy_addsc[] = {
92 [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
93 [IOAM6_ATTR_SC_DATA] = { .type = NLA_BINARY,
94 .len = IOAM6_MAX_SCHEMA_DATA_LEN },
95};
96
97static const struct nla_policy ioam6_genl_policy_delsc[] = {
98 [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
99};
100
101static const struct nla_policy ioam6_genl_policy_ns_sc[] = {
102 [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
103 [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
104 [IOAM6_ATTR_SC_NONE] = { .type = NLA_FLAG },
105};
106
107static int ioam6_genl_addns(struct sk_buff *skb, struct genl_info *info)
108{
109 struct ioam6_pernet_data *nsdata;
110 struct ioam6_namespace *ns;
111 u64 data64;
112 u32 data32;
113 __be16 id;
114 int err;
115
116 if (!info->attrs[IOAM6_ATTR_NS_ID])
117 return -EINVAL;
118
119 id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
120 nsdata = ioam6_pernet(net: genl_info_net(info));
121
122 mutex_lock(&nsdata->lock);
123
124 ns = rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &id, params: rht_ns_params);
125 if (ns) {
126 err = -EEXIST;
127 goto out_unlock;
128 }
129
130 ns = kzalloc(size: sizeof(*ns), GFP_KERNEL);
131 if (!ns) {
132 err = -ENOMEM;
133 goto out_unlock;
134 }
135
136 ns->id = id;
137
138 if (!info->attrs[IOAM6_ATTR_NS_DATA])
139 data32 = IOAM6_U32_UNAVAILABLE;
140 else
141 data32 = nla_get_u32(nla: info->attrs[IOAM6_ATTR_NS_DATA]);
142
143 if (!info->attrs[IOAM6_ATTR_NS_DATA_WIDE])
144 data64 = IOAM6_U64_UNAVAILABLE;
145 else
146 data64 = nla_get_u64(nla: info->attrs[IOAM6_ATTR_NS_DATA_WIDE]);
147
148 ns->data = cpu_to_be32(data32);
149 ns->data_wide = cpu_to_be64(data64);
150
151 err = rhashtable_lookup_insert_fast(ht: &nsdata->namespaces, obj: &ns->head,
152 params: rht_ns_params);
153 if (err)
154 kfree(objp: ns);
155
156out_unlock:
157 mutex_unlock(lock: &nsdata->lock);
158 return err;
159}
160
161static int ioam6_genl_delns(struct sk_buff *skb, struct genl_info *info)
162{
163 struct ioam6_pernet_data *nsdata;
164 struct ioam6_namespace *ns;
165 struct ioam6_schema *sc;
166 __be16 id;
167 int err;
168
169 if (!info->attrs[IOAM6_ATTR_NS_ID])
170 return -EINVAL;
171
172 id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
173 nsdata = ioam6_pernet(net: genl_info_net(info));
174
175 mutex_lock(&nsdata->lock);
176
177 ns = rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &id, params: rht_ns_params);
178 if (!ns) {
179 err = -ENOENT;
180 goto out_unlock;
181 }
182
183 sc = rcu_dereference_protected(ns->schema,
184 lockdep_is_held(&nsdata->lock));
185
186 err = rhashtable_remove_fast(ht: &nsdata->namespaces, obj: &ns->head,
187 params: rht_ns_params);
188 if (err)
189 goto out_unlock;
190
191 if (sc)
192 rcu_assign_pointer(sc->ns, NULL);
193
194 ioam6_ns_release(ns);
195
196out_unlock:
197 mutex_unlock(lock: &nsdata->lock);
198 return err;
199}
200
201static int __ioam6_genl_dumpns_element(struct ioam6_namespace *ns,
202 u32 portid,
203 u32 seq,
204 u32 flags,
205 struct sk_buff *skb,
206 u8 cmd)
207{
208 struct ioam6_schema *sc;
209 u64 data64;
210 u32 data32;
211 void *hdr;
212
213 hdr = genlmsg_put(skb, portid, seq, family: &ioam6_genl_family, flags, cmd);
214 if (!hdr)
215 return -ENOMEM;
216
217 data32 = be32_to_cpu(ns->data);
218 data64 = be64_to_cpu(ns->data_wide);
219
220 if (nla_put_u16(skb, attrtype: IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id)) ||
221 (data32 != IOAM6_U32_UNAVAILABLE &&
222 nla_put_u32(skb, attrtype: IOAM6_ATTR_NS_DATA, value: data32)) ||
223 (data64 != IOAM6_U64_UNAVAILABLE &&
224 nla_put_u64_64bit(skb, attrtype: IOAM6_ATTR_NS_DATA_WIDE,
225 value: data64, padattr: IOAM6_ATTR_PAD)))
226 goto nla_put_failure;
227
228 rcu_read_lock();
229
230 sc = rcu_dereference(ns->schema);
231 if (sc && nla_put_u32(skb, attrtype: IOAM6_ATTR_SC_ID, value: sc->id)) {
232 rcu_read_unlock();
233 goto nla_put_failure;
234 }
235
236 rcu_read_unlock();
237
238 genlmsg_end(skb, hdr);
239 return 0;
240
241nla_put_failure:
242 genlmsg_cancel(skb, hdr);
243 return -EMSGSIZE;
244}
245
246static int ioam6_genl_dumpns_start(struct netlink_callback *cb)
247{
248 struct ioam6_pernet_data *nsdata = ioam6_pernet(net: sock_net(sk: cb->skb->sk));
249 struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
250
251 if (!iter) {
252 iter = kmalloc(size: sizeof(*iter), GFP_KERNEL);
253 if (!iter)
254 return -ENOMEM;
255
256 cb->args[0] = (long)iter;
257 }
258
259 rhashtable_walk_enter(ht: &nsdata->namespaces, iter);
260
261 return 0;
262}
263
264static int ioam6_genl_dumpns_done(struct netlink_callback *cb)
265{
266 struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
267
268 rhashtable_walk_exit(iter);
269 kfree(objp: iter);
270
271 return 0;
272}
273
274static int ioam6_genl_dumpns(struct sk_buff *skb, struct netlink_callback *cb)
275{
276 struct rhashtable_iter *iter;
277 struct ioam6_namespace *ns;
278 int err;
279
280 iter = (struct rhashtable_iter *)cb->args[0];
281 rhashtable_walk_start(iter);
282
283 for (;;) {
284 ns = rhashtable_walk_next(iter);
285
286 if (IS_ERR(ptr: ns)) {
287 if (PTR_ERR(ptr: ns) == -EAGAIN)
288 continue;
289 err = PTR_ERR(ptr: ns);
290 goto done;
291 } else if (!ns) {
292 break;
293 }
294
295 err = __ioam6_genl_dumpns_element(ns,
296 NETLINK_CB(cb->skb).portid,
297 seq: cb->nlh->nlmsg_seq,
298 NLM_F_MULTI,
299 skb,
300 cmd: IOAM6_CMD_DUMP_NAMESPACES);
301 if (err)
302 goto done;
303 }
304
305 err = skb->len;
306
307done:
308 rhashtable_walk_stop(iter);
309 return err;
310}
311
312static int ioam6_genl_addsc(struct sk_buff *skb, struct genl_info *info)
313{
314 struct ioam6_pernet_data *nsdata;
315 int len, len_aligned, err;
316 struct ioam6_schema *sc;
317 u32 id;
318
319 if (!info->attrs[IOAM6_ATTR_SC_ID] || !info->attrs[IOAM6_ATTR_SC_DATA])
320 return -EINVAL;
321
322 id = nla_get_u32(nla: info->attrs[IOAM6_ATTR_SC_ID]);
323 nsdata = ioam6_pernet(net: genl_info_net(info));
324
325 mutex_lock(&nsdata->lock);
326
327 sc = rhashtable_lookup_fast(ht: &nsdata->schemas, key: &id, params: rht_sc_params);
328 if (sc) {
329 err = -EEXIST;
330 goto out_unlock;
331 }
332
333 len = nla_len(nla: info->attrs[IOAM6_ATTR_SC_DATA]);
334 len_aligned = ALIGN(len, 4);
335
336 sc = kzalloc(size: sizeof(*sc) + len_aligned, GFP_KERNEL);
337 if (!sc) {
338 err = -ENOMEM;
339 goto out_unlock;
340 }
341
342 sc->id = id;
343 sc->len = len_aligned;
344 sc->hdr = cpu_to_be32(sc->id | ((u8)(sc->len / 4) << 24));
345 nla_memcpy(dest: sc->data, src: info->attrs[IOAM6_ATTR_SC_DATA], count: len);
346
347 err = rhashtable_lookup_insert_fast(ht: &nsdata->schemas, obj: &sc->head,
348 params: rht_sc_params);
349 if (err)
350 goto free_sc;
351
352out_unlock:
353 mutex_unlock(lock: &nsdata->lock);
354 return err;
355free_sc:
356 kfree(objp: sc);
357 goto out_unlock;
358}
359
360static int ioam6_genl_delsc(struct sk_buff *skb, struct genl_info *info)
361{
362 struct ioam6_pernet_data *nsdata;
363 struct ioam6_namespace *ns;
364 struct ioam6_schema *sc;
365 int err;
366 u32 id;
367
368 if (!info->attrs[IOAM6_ATTR_SC_ID])
369 return -EINVAL;
370
371 id = nla_get_u32(nla: info->attrs[IOAM6_ATTR_SC_ID]);
372 nsdata = ioam6_pernet(net: genl_info_net(info));
373
374 mutex_lock(&nsdata->lock);
375
376 sc = rhashtable_lookup_fast(ht: &nsdata->schemas, key: &id, params: rht_sc_params);
377 if (!sc) {
378 err = -ENOENT;
379 goto out_unlock;
380 }
381
382 ns = rcu_dereference_protected(sc->ns, lockdep_is_held(&nsdata->lock));
383
384 err = rhashtable_remove_fast(ht: &nsdata->schemas, obj: &sc->head,
385 params: rht_sc_params);
386 if (err)
387 goto out_unlock;
388
389 if (ns)
390 rcu_assign_pointer(ns->schema, NULL);
391
392 ioam6_sc_release(sc);
393
394out_unlock:
395 mutex_unlock(lock: &nsdata->lock);
396 return err;
397}
398
399static int __ioam6_genl_dumpsc_element(struct ioam6_schema *sc,
400 u32 portid, u32 seq, u32 flags,
401 struct sk_buff *skb, u8 cmd)
402{
403 struct ioam6_namespace *ns;
404 void *hdr;
405
406 hdr = genlmsg_put(skb, portid, seq, family: &ioam6_genl_family, flags, cmd);
407 if (!hdr)
408 return -ENOMEM;
409
410 if (nla_put_u32(skb, attrtype: IOAM6_ATTR_SC_ID, value: sc->id) ||
411 nla_put(skb, attrtype: IOAM6_ATTR_SC_DATA, attrlen: sc->len, data: sc->data))
412 goto nla_put_failure;
413
414 rcu_read_lock();
415
416 ns = rcu_dereference(sc->ns);
417 if (ns && nla_put_u16(skb, attrtype: IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id))) {
418 rcu_read_unlock();
419 goto nla_put_failure;
420 }
421
422 rcu_read_unlock();
423
424 genlmsg_end(skb, hdr);
425 return 0;
426
427nla_put_failure:
428 genlmsg_cancel(skb, hdr);
429 return -EMSGSIZE;
430}
431
432static int ioam6_genl_dumpsc_start(struct netlink_callback *cb)
433{
434 struct ioam6_pernet_data *nsdata = ioam6_pernet(net: sock_net(sk: cb->skb->sk));
435 struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
436
437 if (!iter) {
438 iter = kmalloc(size: sizeof(*iter), GFP_KERNEL);
439 if (!iter)
440 return -ENOMEM;
441
442 cb->args[0] = (long)iter;
443 }
444
445 rhashtable_walk_enter(ht: &nsdata->schemas, iter);
446
447 return 0;
448}
449
450static int ioam6_genl_dumpsc_done(struct netlink_callback *cb)
451{
452 struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
453
454 rhashtable_walk_exit(iter);
455 kfree(objp: iter);
456
457 return 0;
458}
459
460static int ioam6_genl_dumpsc(struct sk_buff *skb, struct netlink_callback *cb)
461{
462 struct rhashtable_iter *iter;
463 struct ioam6_schema *sc;
464 int err;
465
466 iter = (struct rhashtable_iter *)cb->args[0];
467 rhashtable_walk_start(iter);
468
469 for (;;) {
470 sc = rhashtable_walk_next(iter);
471
472 if (IS_ERR(ptr: sc)) {
473 if (PTR_ERR(ptr: sc) == -EAGAIN)
474 continue;
475 err = PTR_ERR(ptr: sc);
476 goto done;
477 } else if (!sc) {
478 break;
479 }
480
481 err = __ioam6_genl_dumpsc_element(sc,
482 NETLINK_CB(cb->skb).portid,
483 seq: cb->nlh->nlmsg_seq,
484 NLM_F_MULTI,
485 skb,
486 cmd: IOAM6_CMD_DUMP_SCHEMAS);
487 if (err)
488 goto done;
489 }
490
491 err = skb->len;
492
493done:
494 rhashtable_walk_stop(iter);
495 return err;
496}
497
498static int ioam6_genl_ns_set_schema(struct sk_buff *skb, struct genl_info *info)
499{
500 struct ioam6_namespace *ns, *ns_ref;
501 struct ioam6_schema *sc, *sc_ref;
502 struct ioam6_pernet_data *nsdata;
503 __be16 ns_id;
504 u32 sc_id;
505 int err;
506
507 if (!info->attrs[IOAM6_ATTR_NS_ID] ||
508 (!info->attrs[IOAM6_ATTR_SC_ID] &&
509 !info->attrs[IOAM6_ATTR_SC_NONE]))
510 return -EINVAL;
511
512 ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
513 nsdata = ioam6_pernet(net: genl_info_net(info));
514
515 mutex_lock(&nsdata->lock);
516
517 ns = rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &ns_id, params: rht_ns_params);
518 if (!ns) {
519 err = -ENOENT;
520 goto out_unlock;
521 }
522
523 if (info->attrs[IOAM6_ATTR_SC_NONE]) {
524 sc = NULL;
525 } else {
526 sc_id = nla_get_u32(nla: info->attrs[IOAM6_ATTR_SC_ID]);
527 sc = rhashtable_lookup_fast(ht: &nsdata->schemas, key: &sc_id,
528 params: rht_sc_params);
529 if (!sc) {
530 err = -ENOENT;
531 goto out_unlock;
532 }
533 }
534
535 sc_ref = rcu_dereference_protected(ns->schema,
536 lockdep_is_held(&nsdata->lock));
537 if (sc_ref)
538 rcu_assign_pointer(sc_ref->ns, NULL);
539 rcu_assign_pointer(ns->schema, sc);
540
541 if (sc) {
542 ns_ref = rcu_dereference_protected(sc->ns,
543 lockdep_is_held(&nsdata->lock));
544 if (ns_ref)
545 rcu_assign_pointer(ns_ref->schema, NULL);
546 rcu_assign_pointer(sc->ns, ns);
547 }
548
549 err = 0;
550
551out_unlock:
552 mutex_unlock(lock: &nsdata->lock);
553 return err;
554}
555
556static const struct genl_ops ioam6_genl_ops[] = {
557 {
558 .cmd = IOAM6_CMD_ADD_NAMESPACE,
559 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
560 .doit = ioam6_genl_addns,
561 .flags = GENL_ADMIN_PERM,
562 .policy = ioam6_genl_policy_addns,
563 .maxattr = ARRAY_SIZE(ioam6_genl_policy_addns) - 1,
564 },
565 {
566 .cmd = IOAM6_CMD_DEL_NAMESPACE,
567 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
568 .doit = ioam6_genl_delns,
569 .flags = GENL_ADMIN_PERM,
570 .policy = ioam6_genl_policy_delns,
571 .maxattr = ARRAY_SIZE(ioam6_genl_policy_delns) - 1,
572 },
573 {
574 .cmd = IOAM6_CMD_DUMP_NAMESPACES,
575 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
576 .start = ioam6_genl_dumpns_start,
577 .dumpit = ioam6_genl_dumpns,
578 .done = ioam6_genl_dumpns_done,
579 .flags = GENL_ADMIN_PERM,
580 },
581 {
582 .cmd = IOAM6_CMD_ADD_SCHEMA,
583 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
584 .doit = ioam6_genl_addsc,
585 .flags = GENL_ADMIN_PERM,
586 .policy = ioam6_genl_policy_addsc,
587 .maxattr = ARRAY_SIZE(ioam6_genl_policy_addsc) - 1,
588 },
589 {
590 .cmd = IOAM6_CMD_DEL_SCHEMA,
591 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
592 .doit = ioam6_genl_delsc,
593 .flags = GENL_ADMIN_PERM,
594 .policy = ioam6_genl_policy_delsc,
595 .maxattr = ARRAY_SIZE(ioam6_genl_policy_delsc) - 1,
596 },
597 {
598 .cmd = IOAM6_CMD_DUMP_SCHEMAS,
599 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
600 .start = ioam6_genl_dumpsc_start,
601 .dumpit = ioam6_genl_dumpsc,
602 .done = ioam6_genl_dumpsc_done,
603 .flags = GENL_ADMIN_PERM,
604 },
605 {
606 .cmd = IOAM6_CMD_NS_SET_SCHEMA,
607 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
608 .doit = ioam6_genl_ns_set_schema,
609 .flags = GENL_ADMIN_PERM,
610 .policy = ioam6_genl_policy_ns_sc,
611 .maxattr = ARRAY_SIZE(ioam6_genl_policy_ns_sc) - 1,
612 },
613};
614
615#define IOAM6_GENL_EV_GRP_OFFSET 0
616
617static const struct genl_multicast_group ioam6_mcgrps[] = {
618 [IOAM6_GENL_EV_GRP_OFFSET] = { .name = IOAM6_GENL_EV_GRP_NAME,
619 .flags = GENL_MCAST_CAP_NET_ADMIN },
620};
621
622static int ioam6_event_put_trace(struct sk_buff *skb,
623 struct ioam6_trace_hdr *trace,
624 unsigned int len)
625{
626 if (nla_put_u16(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_NAMESPACE,
627 be16_to_cpu(trace->namespace_id)) ||
628 nla_put_u8(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_NODELEN, value: trace->nodelen) ||
629 nla_put_u32(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_TYPE,
630 be32_to_cpu(trace->type_be32)) ||
631 nla_put(skb, attrtype: IOAM6_EVENT_ATTR_TRACE_DATA,
632 attrlen: len - sizeof(struct ioam6_trace_hdr) - trace->remlen * 4,
633 data: trace->data + trace->remlen * 4))
634 return 1;
635
636 return 0;
637}
638
639void ioam6_event(enum ioam6_event_type type, struct net *net, gfp_t gfp,
640 void *opt, unsigned int opt_len)
641{
642 struct nlmsghdr *nlh;
643 struct sk_buff *skb;
644
645 if (!genl_has_listeners(family: &ioam6_genl_family, net,
646 IOAM6_GENL_EV_GRP_OFFSET))
647 return;
648
649 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp);
650 if (!skb)
651 return;
652
653 nlh = genlmsg_put(skb, portid: 0, seq: 0, family: &ioam6_genl_family, flags: 0, cmd: type);
654 if (!nlh)
655 goto nla_put_failure;
656
657 switch (type) {
658 case IOAM6_EVENT_UNSPEC:
659 WARN_ON_ONCE(1);
660 break;
661 case IOAM6_EVENT_TRACE:
662 if (ioam6_event_put_trace(skb, trace: (struct ioam6_trace_hdr *)opt,
663 len: opt_len))
664 goto nla_put_failure;
665 break;
666 }
667
668 genlmsg_end(skb, hdr: nlh);
669 genlmsg_multicast_netns(family: &ioam6_genl_family, net, skb, portid: 0,
670 IOAM6_GENL_EV_GRP_OFFSET, flags: gfp);
671 return;
672
673nla_put_failure:
674 nlmsg_free(skb);
675}
676
677static struct genl_family ioam6_genl_family __ro_after_init = {
678 .name = IOAM6_GENL_NAME,
679 .version = IOAM6_GENL_VERSION,
680 .netnsok = true,
681 .parallel_ops = true,
682 .ops = ioam6_genl_ops,
683 .n_ops = ARRAY_SIZE(ioam6_genl_ops),
684 .resv_start_op = IOAM6_CMD_NS_SET_SCHEMA + 1,
685 .mcgrps = ioam6_mcgrps,
686 .n_mcgrps = ARRAY_SIZE(ioam6_mcgrps),
687 .module = THIS_MODULE,
688};
689
690struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id)
691{
692 struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
693
694 return rhashtable_lookup_fast(ht: &nsdata->namespaces, key: &id, params: rht_ns_params);
695}
696
697static void __ioam6_fill_trace_data(struct sk_buff *skb,
698 struct ioam6_namespace *ns,
699 struct ioam6_trace_hdr *trace,
700 struct ioam6_schema *sc,
701 u8 sclen, bool is_input)
702{
703 struct timespec64 ts;
704 ktime_t tstamp;
705 u64 raw64;
706 u32 raw32;
707 u16 raw16;
708 u8 *data;
709 u8 byte;
710
711 data = trace->data + trace->remlen * 4 - trace->nodelen * 4 - sclen * 4;
712
713 /* hop_lim and node_id */
714 if (trace->type.bit0) {
715 byte = ipv6_hdr(skb)->hop_limit;
716 if (is_input)
717 byte--;
718
719 raw32 = dev_net(dev: skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id;
720
721 *(__be32 *)data = cpu_to_be32((byte << 24) | raw32);
722 data += sizeof(__be32);
723 }
724
725 /* ingress_if_id and egress_if_id */
726 if (trace->type.bit1) {
727 if (!skb->dev)
728 raw16 = IOAM6_U16_UNAVAILABLE;
729 else
730 raw16 = (__force u16)READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id);
731
732 *(__be16 *)data = cpu_to_be16(raw16);
733 data += sizeof(__be16);
734
735 if (skb_dst(skb)->dev->flags & IFF_LOOPBACK)
736 raw16 = IOAM6_U16_UNAVAILABLE;
737 else
738 raw16 = (__force u16)READ_ONCE(__in6_dev_get(skb_dst(skb)->dev)->cnf.ioam6_id);
739
740 *(__be16 *)data = cpu_to_be16(raw16);
741 data += sizeof(__be16);
742 }
743
744 /* timestamp seconds */
745 if (trace->type.bit2) {
746 if (!skb->dev) {
747 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
748 } else {
749 tstamp = skb_tstamp_cond(skb, cond: true);
750 ts = ktime_to_timespec64(tstamp);
751
752 *(__be32 *)data = cpu_to_be32((u32)ts.tv_sec);
753 }
754 data += sizeof(__be32);
755 }
756
757 /* timestamp subseconds */
758 if (trace->type.bit3) {
759 if (!skb->dev) {
760 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
761 } else {
762 if (!trace->type.bit2) {
763 tstamp = skb_tstamp_cond(skb, cond: true);
764 ts = ktime_to_timespec64(tstamp);
765 }
766
767 *(__be32 *)data = cpu_to_be32((u32)(ts.tv_nsec / NSEC_PER_USEC));
768 }
769 data += sizeof(__be32);
770 }
771
772 /* transit delay */
773 if (trace->type.bit4) {
774 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
775 data += sizeof(__be32);
776 }
777
778 /* namespace data */
779 if (trace->type.bit5) {
780 *(__be32 *)data = ns->data;
781 data += sizeof(__be32);
782 }
783
784 /* queue depth */
785 if (trace->type.bit6) {
786 struct netdev_queue *queue;
787 struct Qdisc *qdisc;
788 __u32 qlen, backlog;
789
790 if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) {
791 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
792 } else {
793 queue = skb_get_tx_queue(dev: skb_dst(skb)->dev, skb);
794 qdisc = rcu_dereference(queue->qdisc);
795 qdisc_qstats_qlen_backlog(sch: qdisc, qlen: &qlen, backlog: &backlog);
796
797 *(__be32 *)data = cpu_to_be32(backlog);
798 }
799 data += sizeof(__be32);
800 }
801
802 /* checksum complement */
803 if (trace->type.bit7) {
804 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
805 data += sizeof(__be32);
806 }
807
808 /* hop_lim and node_id (wide) */
809 if (trace->type.bit8) {
810 byte = ipv6_hdr(skb)->hop_limit;
811 if (is_input)
812 byte--;
813
814 raw64 = dev_net(dev: skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id_wide;
815
816 *(__be64 *)data = cpu_to_be64(((u64)byte << 56) | raw64);
817 data += sizeof(__be64);
818 }
819
820 /* ingress_if_id and egress_if_id (wide) */
821 if (trace->type.bit9) {
822 if (!skb->dev)
823 raw32 = IOAM6_U32_UNAVAILABLE;
824 else
825 raw32 = READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id_wide);
826
827 *(__be32 *)data = cpu_to_be32(raw32);
828 data += sizeof(__be32);
829
830 if (skb_dst(skb)->dev->flags & IFF_LOOPBACK)
831 raw32 = IOAM6_U32_UNAVAILABLE;
832 else
833 raw32 = READ_ONCE(__in6_dev_get(skb_dst(skb)->dev)->cnf.ioam6_id_wide);
834
835 *(__be32 *)data = cpu_to_be32(raw32);
836 data += sizeof(__be32);
837 }
838
839 /* namespace data (wide) */
840 if (trace->type.bit10) {
841 *(__be64 *)data = ns->data_wide;
842 data += sizeof(__be64);
843 }
844
845 /* buffer occupancy */
846 if (trace->type.bit11) {
847 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
848 data += sizeof(__be32);
849 }
850
851 /* bit12 undefined: filled with empty value */
852 if (trace->type.bit12) {
853 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
854 data += sizeof(__be32);
855 }
856
857 /* bit13 undefined: filled with empty value */
858 if (trace->type.bit13) {
859 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
860 data += sizeof(__be32);
861 }
862
863 /* bit14 undefined: filled with empty value */
864 if (trace->type.bit14) {
865 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
866 data += sizeof(__be32);
867 }
868
869 /* bit15 undefined: filled with empty value */
870 if (trace->type.bit15) {
871 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
872 data += sizeof(__be32);
873 }
874
875 /* bit16 undefined: filled with empty value */
876 if (trace->type.bit16) {
877 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
878 data += sizeof(__be32);
879 }
880
881 /* bit17 undefined: filled with empty value */
882 if (trace->type.bit17) {
883 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
884 data += sizeof(__be32);
885 }
886
887 /* bit18 undefined: filled with empty value */
888 if (trace->type.bit18) {
889 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
890 data += sizeof(__be32);
891 }
892
893 /* bit19 undefined: filled with empty value */
894 if (trace->type.bit19) {
895 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
896 data += sizeof(__be32);
897 }
898
899 /* bit20 undefined: filled with empty value */
900 if (trace->type.bit20) {
901 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
902 data += sizeof(__be32);
903 }
904
905 /* bit21 undefined: filled with empty value */
906 if (trace->type.bit21) {
907 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
908 data += sizeof(__be32);
909 }
910
911 /* opaque state snapshot */
912 if (trace->type.bit22) {
913 if (!sc) {
914 *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE >> 8);
915 } else {
916 *(__be32 *)data = sc->hdr;
917 data += sizeof(__be32);
918
919 memcpy(data, sc->data, sc->len);
920 }
921 }
922}
923
924/* called with rcu_read_lock() */
925void ioam6_fill_trace_data(struct sk_buff *skb,
926 struct ioam6_namespace *ns,
927 struct ioam6_trace_hdr *trace,
928 bool is_input)
929{
930 struct ioam6_schema *sc;
931 u8 sclen = 0;
932
933 /* Skip if Overflow flag is set
934 */
935 if (trace->overflow)
936 return;
937
938 /* NodeLen does not include Opaque State Snapshot length. We need to
939 * take it into account if the corresponding bit is set (bit 22) and
940 * if the current IOAM namespace has an active schema attached to it
941 */
942 sc = rcu_dereference(ns->schema);
943 if (trace->type.bit22) {
944 sclen = sizeof_field(struct ioam6_schema, hdr) / 4;
945
946 if (sc)
947 sclen += sc->len / 4;
948 }
949
950 /* If there is no space remaining, we set the Overflow flag and we
951 * skip without filling the trace
952 */
953 if (!trace->remlen || trace->remlen < trace->nodelen + sclen) {
954 trace->overflow = 1;
955 return;
956 }
957
958 __ioam6_fill_trace_data(skb, ns, trace, sc, sclen, is_input);
959 trace->remlen -= trace->nodelen + sclen;
960}
961
962static int __net_init ioam6_net_init(struct net *net)
963{
964 struct ioam6_pernet_data *nsdata;
965 int err = -ENOMEM;
966
967 nsdata = kzalloc(size: sizeof(*nsdata), GFP_KERNEL);
968 if (!nsdata)
969 goto out;
970
971 mutex_init(&nsdata->lock);
972 net->ipv6.ioam6_data = nsdata;
973
974 err = rhashtable_init(ht: &nsdata->namespaces, params: &rht_ns_params);
975 if (err)
976 goto free_nsdata;
977
978 err = rhashtable_init(ht: &nsdata->schemas, params: &rht_sc_params);
979 if (err)
980 goto free_rht_ns;
981
982out:
983 return err;
984free_rht_ns:
985 rhashtable_destroy(ht: &nsdata->namespaces);
986free_nsdata:
987 kfree(objp: nsdata);
988 net->ipv6.ioam6_data = NULL;
989 goto out;
990}
991
992static void __net_exit ioam6_net_exit(struct net *net)
993{
994 struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
995
996 rhashtable_free_and_destroy(ht: &nsdata->namespaces, free_fn: ioam6_free_ns, NULL);
997 rhashtable_free_and_destroy(ht: &nsdata->schemas, free_fn: ioam6_free_sc, NULL);
998
999 kfree(objp: nsdata);
1000}
1001
1002static struct pernet_operations ioam6_net_ops = {
1003 .init = ioam6_net_init,
1004 .exit = ioam6_net_exit,
1005};
1006
1007int __init ioam6_init(void)
1008{
1009 int err = register_pernet_subsys(&ioam6_net_ops);
1010 if (err)
1011 goto out;
1012
1013 err = genl_register_family(family: &ioam6_genl_family);
1014 if (err)
1015 goto out_unregister_pernet_subsys;
1016
1017#ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
1018 err = ioam6_iptunnel_init();
1019 if (err)
1020 goto out_unregister_genl;
1021#endif
1022
1023 pr_info("In-situ OAM (IOAM) with IPv6\n");
1024
1025out:
1026 return err;
1027#ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
1028out_unregister_genl:
1029 genl_unregister_family(family: &ioam6_genl_family);
1030#endif
1031out_unregister_pernet_subsys:
1032 unregister_pernet_subsys(&ioam6_net_ops);
1033 goto out;
1034}
1035
1036void ioam6_exit(void)
1037{
1038#ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
1039 ioam6_iptunnel_exit();
1040#endif
1041 genl_unregister_family(family: &ioam6_genl_family);
1042 unregister_pernet_subsys(&ioam6_net_ops);
1043}
1044

source code of linux/net/ipv6/ioam6.c