| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * IPv6 IOAM implementation
- *
- * Author:
- * Justin Iurman <justin.iurman@uliege.be>
- */
- #include <linux/errno.h>
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/net.h>
- #include <linux/ioam6.h>
- #include <linux/ioam6_genl.h>
- #include <linux/rhashtable.h>
- #include <linux/netdevice.h>
- #include <net/addrconf.h>
- #include <net/genetlink.h>
- #include <net/ioam6.h>
- #include <net/sch_generic.h>
- static void ioam6_ns_release(struct ioam6_namespace *ns)
- {
- kfree_rcu(ns, rcu);
- }
- static void ioam6_sc_release(struct ioam6_schema *sc)
- {
- kfree_rcu(sc, rcu);
- }
- static void ioam6_free_ns(void *ptr, void *arg)
- {
- struct ioam6_namespace *ns = (struct ioam6_namespace *)ptr;
- if (ns)
- ioam6_ns_release(ns);
- }
- static void ioam6_free_sc(void *ptr, void *arg)
- {
- struct ioam6_schema *sc = (struct ioam6_schema *)ptr;
- if (sc)
- ioam6_sc_release(sc);
- }
- static int ioam6_ns_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
- {
- const struct ioam6_namespace *ns = obj;
- return (ns->id != *(__be16 *)arg->key);
- }
- static int ioam6_sc_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
- {
- const struct ioam6_schema *sc = obj;
- return (sc->id != *(u32 *)arg->key);
- }
- static const struct rhashtable_params rht_ns_params = {
- .key_len = sizeof(__be16),
- .key_offset = offsetof(struct ioam6_namespace, id),
- .head_offset = offsetof(struct ioam6_namespace, head),
- .automatic_shrinking = true,
- .obj_cmpfn = ioam6_ns_cmpfn,
- };
- static const struct rhashtable_params rht_sc_params = {
- .key_len = sizeof(u32),
- .key_offset = offsetof(struct ioam6_schema, id),
- .head_offset = offsetof(struct ioam6_schema, head),
- .automatic_shrinking = true,
- .obj_cmpfn = ioam6_sc_cmpfn,
- };
- static struct genl_family ioam6_genl_family;
- static const struct nla_policy ioam6_genl_policy_addns[] = {
- [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
- [IOAM6_ATTR_NS_DATA] = { .type = NLA_U32 },
- [IOAM6_ATTR_NS_DATA_WIDE] = { .type = NLA_U64 },
- };
- static const struct nla_policy ioam6_genl_policy_delns[] = {
- [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
- };
- static const struct nla_policy ioam6_genl_policy_addsc[] = {
- [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
- [IOAM6_ATTR_SC_DATA] = { .type = NLA_BINARY,
- .len = IOAM6_MAX_SCHEMA_DATA_LEN },
- };
- static const struct nla_policy ioam6_genl_policy_delsc[] = {
- [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
- };
- static const struct nla_policy ioam6_genl_policy_ns_sc[] = {
- [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
- [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
- [IOAM6_ATTR_SC_NONE] = { .type = NLA_FLAG },
- };
- static int ioam6_genl_addns(struct sk_buff *skb, struct genl_info *info)
- {
- struct ioam6_pernet_data *nsdata;
- struct ioam6_namespace *ns;
- u64 data64;
- u32 data32;
- __be16 id;
- int err;
- if (!info->attrs[IOAM6_ATTR_NS_ID])
- return -EINVAL;
- id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
- nsdata = ioam6_pernet(genl_info_net(info));
- mutex_lock(&nsdata->lock);
- ns = rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
- if (ns) {
- err = -EEXIST;
- goto out_unlock;
- }
- ns = kzalloc_obj(*ns);
- if (!ns) {
- err = -ENOMEM;
- goto out_unlock;
- }
- ns->id = id;
- data32 = nla_get_u32_default(info->attrs[IOAM6_ATTR_NS_DATA],
- IOAM6_U32_UNAVAILABLE);
- data64 = nla_get_u64_default(info->attrs[IOAM6_ATTR_NS_DATA_WIDE],
- IOAM6_U64_UNAVAILABLE);
- ns->data = cpu_to_be32(data32);
- ns->data_wide = cpu_to_be64(data64);
- err = rhashtable_lookup_insert_fast(&nsdata->namespaces, &ns->head,
- rht_ns_params);
- if (err)
- kfree(ns);
- out_unlock:
- mutex_unlock(&nsdata->lock);
- return err;
- }
- static int ioam6_genl_delns(struct sk_buff *skb, struct genl_info *info)
- {
- struct ioam6_pernet_data *nsdata;
- struct ioam6_namespace *ns;
- struct ioam6_schema *sc;
- __be16 id;
- int err;
- if (!info->attrs[IOAM6_ATTR_NS_ID])
- return -EINVAL;
- id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
- nsdata = ioam6_pernet(genl_info_net(info));
- mutex_lock(&nsdata->lock);
- ns = rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
- if (!ns) {
- err = -ENOENT;
- goto out_unlock;
- }
- sc = rcu_dereference_protected(ns->schema,
- lockdep_is_held(&nsdata->lock));
- err = rhashtable_remove_fast(&nsdata->namespaces, &ns->head,
- rht_ns_params);
- if (err)
- goto out_unlock;
- if (sc)
- rcu_assign_pointer(sc->ns, NULL);
- ioam6_ns_release(ns);
- out_unlock:
- mutex_unlock(&nsdata->lock);
- return err;
- }
- static int __ioam6_genl_dumpns_element(struct ioam6_namespace *ns,
- u32 portid,
- u32 seq,
- u32 flags,
- struct sk_buff *skb,
- u8 cmd)
- {
- struct ioam6_schema *sc;
- u64 data64;
- u32 data32;
- void *hdr;
- hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd);
- if (!hdr)
- return -ENOMEM;
- data32 = be32_to_cpu(ns->data);
- data64 = be64_to_cpu(ns->data_wide);
- if (nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id)) ||
- (data32 != IOAM6_U32_UNAVAILABLE &&
- nla_put_u32(skb, IOAM6_ATTR_NS_DATA, data32)) ||
- (data64 != IOAM6_U64_UNAVAILABLE &&
- nla_put_u64_64bit(skb, IOAM6_ATTR_NS_DATA_WIDE,
- data64, IOAM6_ATTR_PAD)))
- goto nla_put_failure;
- rcu_read_lock();
- sc = rcu_dereference(ns->schema);
- if (sc && nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id)) {
- rcu_read_unlock();
- goto nla_put_failure;
- }
- rcu_read_unlock();
- genlmsg_end(skb, hdr);
- return 0;
- nla_put_failure:
- genlmsg_cancel(skb, hdr);
- return -EMSGSIZE;
- }
- static int ioam6_genl_dumpns_start(struct netlink_callback *cb)
- {
- struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk));
- struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
- if (!iter) {
- iter = kmalloc_obj(*iter);
- if (!iter)
- return -ENOMEM;
- cb->args[0] = (long)iter;
- }
- rhashtable_walk_enter(&nsdata->namespaces, iter);
- return 0;
- }
- static int ioam6_genl_dumpns_done(struct netlink_callback *cb)
- {
- struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
- rhashtable_walk_exit(iter);
- kfree(iter);
- return 0;
- }
- static int ioam6_genl_dumpns(struct sk_buff *skb, struct netlink_callback *cb)
- {
- struct rhashtable_iter *iter;
- struct ioam6_namespace *ns;
- int err;
- iter = (struct rhashtable_iter *)cb->args[0];
- rhashtable_walk_start(iter);
- for (;;) {
- ns = rhashtable_walk_next(iter);
- if (IS_ERR(ns)) {
- if (PTR_ERR(ns) == -EAGAIN)
- continue;
- err = PTR_ERR(ns);
- goto done;
- } else if (!ns) {
- break;
- }
- err = __ioam6_genl_dumpns_element(ns,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- NLM_F_MULTI,
- skb,
- IOAM6_CMD_DUMP_NAMESPACES);
- if (err)
- goto done;
- }
- err = skb->len;
- done:
- rhashtable_walk_stop(iter);
- return err;
- }
- static int ioam6_genl_addsc(struct sk_buff *skb, struct genl_info *info)
- {
- struct ioam6_pernet_data *nsdata;
- int len, len_aligned, err;
- struct ioam6_schema *sc;
- u32 id;
- if (!info->attrs[IOAM6_ATTR_SC_ID] || !info->attrs[IOAM6_ATTR_SC_DATA])
- return -EINVAL;
- id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
- nsdata = ioam6_pernet(genl_info_net(info));
- mutex_lock(&nsdata->lock);
- sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params);
- if (sc) {
- err = -EEXIST;
- goto out_unlock;
- }
- len = nla_len(info->attrs[IOAM6_ATTR_SC_DATA]);
- len_aligned = ALIGN(len, 4);
- sc = kzalloc(sizeof(*sc) + len_aligned, GFP_KERNEL);
- if (!sc) {
- err = -ENOMEM;
- goto out_unlock;
- }
- sc->id = id;
- sc->len = len_aligned;
- sc->hdr = cpu_to_be32(sc->id | ((u8)(sc->len / 4) << 24));
- nla_memcpy(sc->data, info->attrs[IOAM6_ATTR_SC_DATA], len);
- err = rhashtable_lookup_insert_fast(&nsdata->schemas, &sc->head,
- rht_sc_params);
- if (err)
- goto free_sc;
- out_unlock:
- mutex_unlock(&nsdata->lock);
- return err;
- free_sc:
- kfree(sc);
- goto out_unlock;
- }
- static int ioam6_genl_delsc(struct sk_buff *skb, struct genl_info *info)
- {
- struct ioam6_pernet_data *nsdata;
- struct ioam6_namespace *ns;
- struct ioam6_schema *sc;
- int err;
- u32 id;
- if (!info->attrs[IOAM6_ATTR_SC_ID])
- return -EINVAL;
- id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
- nsdata = ioam6_pernet(genl_info_net(info));
- mutex_lock(&nsdata->lock);
- sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params);
- if (!sc) {
- err = -ENOENT;
- goto out_unlock;
- }
- ns = rcu_dereference_protected(sc->ns, lockdep_is_held(&nsdata->lock));
- err = rhashtable_remove_fast(&nsdata->schemas, &sc->head,
- rht_sc_params);
- if (err)
- goto out_unlock;
- if (ns)
- rcu_assign_pointer(ns->schema, NULL);
- ioam6_sc_release(sc);
- out_unlock:
- mutex_unlock(&nsdata->lock);
- return err;
- }
- static int __ioam6_genl_dumpsc_element(struct ioam6_schema *sc,
- u32 portid, u32 seq, u32 flags,
- struct sk_buff *skb, u8 cmd)
- {
- struct ioam6_namespace *ns;
- void *hdr;
- hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd);
- if (!hdr)
- return -ENOMEM;
- if (nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id) ||
- nla_put(skb, IOAM6_ATTR_SC_DATA, sc->len, sc->data))
- goto nla_put_failure;
- rcu_read_lock();
- ns = rcu_dereference(sc->ns);
- if (ns && nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id))) {
- rcu_read_unlock();
- goto nla_put_failure;
- }
- rcu_read_unlock();
- genlmsg_end(skb, hdr);
- return 0;
- nla_put_failure:
- genlmsg_cancel(skb, hdr);
- return -EMSGSIZE;
- }
- static int ioam6_genl_dumpsc_start(struct netlink_callback *cb)
- {
- struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk));
- struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
- if (!iter) {
- iter = kmalloc_obj(*iter);
- if (!iter)
- return -ENOMEM;
- cb->args[0] = (long)iter;
- }
- rhashtable_walk_enter(&nsdata->schemas, iter);
- return 0;
- }
- static int ioam6_genl_dumpsc_done(struct netlink_callback *cb)
- {
- struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
- rhashtable_walk_exit(iter);
- kfree(iter);
- return 0;
- }
- static int ioam6_genl_dumpsc(struct sk_buff *skb, struct netlink_callback *cb)
- {
- struct rhashtable_iter *iter;
- struct ioam6_schema *sc;
- int err;
- iter = (struct rhashtable_iter *)cb->args[0];
- rhashtable_walk_start(iter);
- for (;;) {
- sc = rhashtable_walk_next(iter);
- if (IS_ERR(sc)) {
- if (PTR_ERR(sc) == -EAGAIN)
- continue;
- err = PTR_ERR(sc);
- goto done;
- } else if (!sc) {
- break;
- }
- err = __ioam6_genl_dumpsc_element(sc,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- NLM_F_MULTI,
- skb,
- IOAM6_CMD_DUMP_SCHEMAS);
- if (err)
- goto done;
- }
- err = skb->len;
- done:
- rhashtable_walk_stop(iter);
- return err;
- }
- static int ioam6_genl_ns_set_schema(struct sk_buff *skb, struct genl_info *info)
- {
- struct ioam6_namespace *ns, *ns_ref;
- struct ioam6_schema *sc, *sc_ref;
- struct ioam6_pernet_data *nsdata;
- __be16 ns_id;
- u32 sc_id;
- int err;
- if (!info->attrs[IOAM6_ATTR_NS_ID] ||
- (!info->attrs[IOAM6_ATTR_SC_ID] &&
- !info->attrs[IOAM6_ATTR_SC_NONE]))
- return -EINVAL;
- ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
- nsdata = ioam6_pernet(genl_info_net(info));
- mutex_lock(&nsdata->lock);
- ns = rhashtable_lookup_fast(&nsdata->namespaces, &ns_id, rht_ns_params);
- if (!ns) {
- err = -ENOENT;
- goto out_unlock;
- }
- if (info->attrs[IOAM6_ATTR_SC_NONE]) {
- sc = NULL;
- } else {
- sc_id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
- sc = rhashtable_lookup_fast(&nsdata->schemas, &sc_id,
- rht_sc_params);
- if (!sc) {
- err = -ENOENT;
- goto out_unlock;
- }
- }
- sc_ref = rcu_dereference_protected(ns->schema,
- lockdep_is_held(&nsdata->lock));
- if (sc_ref)
- rcu_assign_pointer(sc_ref->ns, NULL);
- rcu_assign_pointer(ns->schema, sc);
- if (sc) {
- ns_ref = rcu_dereference_protected(sc->ns,
- lockdep_is_held(&nsdata->lock));
- if (ns_ref)
- rcu_assign_pointer(ns_ref->schema, NULL);
- rcu_assign_pointer(sc->ns, ns);
- }
- err = 0;
- out_unlock:
- mutex_unlock(&nsdata->lock);
- return err;
- }
- static const struct genl_ops ioam6_genl_ops[] = {
- {
- .cmd = IOAM6_CMD_ADD_NAMESPACE,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = ioam6_genl_addns,
- .flags = GENL_ADMIN_PERM,
- .policy = ioam6_genl_policy_addns,
- .maxattr = ARRAY_SIZE(ioam6_genl_policy_addns) - 1,
- },
- {
- .cmd = IOAM6_CMD_DEL_NAMESPACE,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = ioam6_genl_delns,
- .flags = GENL_ADMIN_PERM,
- .policy = ioam6_genl_policy_delns,
- .maxattr = ARRAY_SIZE(ioam6_genl_policy_delns) - 1,
- },
- {
- .cmd = IOAM6_CMD_DUMP_NAMESPACES,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .start = ioam6_genl_dumpns_start,
- .dumpit = ioam6_genl_dumpns,
- .done = ioam6_genl_dumpns_done,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = IOAM6_CMD_ADD_SCHEMA,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = ioam6_genl_addsc,
- .flags = GENL_ADMIN_PERM,
- .policy = ioam6_genl_policy_addsc,
- .maxattr = ARRAY_SIZE(ioam6_genl_policy_addsc) - 1,
- },
- {
- .cmd = IOAM6_CMD_DEL_SCHEMA,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = ioam6_genl_delsc,
- .flags = GENL_ADMIN_PERM,
- .policy = ioam6_genl_policy_delsc,
- .maxattr = ARRAY_SIZE(ioam6_genl_policy_delsc) - 1,
- },
- {
- .cmd = IOAM6_CMD_DUMP_SCHEMAS,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .start = ioam6_genl_dumpsc_start,
- .dumpit = ioam6_genl_dumpsc,
- .done = ioam6_genl_dumpsc_done,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = IOAM6_CMD_NS_SET_SCHEMA,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = ioam6_genl_ns_set_schema,
- .flags = GENL_ADMIN_PERM,
- .policy = ioam6_genl_policy_ns_sc,
- .maxattr = ARRAY_SIZE(ioam6_genl_policy_ns_sc) - 1,
- },
- };
- #define IOAM6_GENL_EV_GRP_OFFSET 0
- static const struct genl_multicast_group ioam6_mcgrps[] = {
- [IOAM6_GENL_EV_GRP_OFFSET] = { .name = IOAM6_GENL_EV_GRP_NAME,
- .flags = GENL_MCAST_CAP_NET_ADMIN },
- };
- static int ioam6_event_put_trace(struct sk_buff *skb,
- struct ioam6_trace_hdr *trace,
- unsigned int len)
- {
- if (nla_put_u16(skb, IOAM6_EVENT_ATTR_TRACE_NAMESPACE,
- be16_to_cpu(trace->namespace_id)) ||
- nla_put_u8(skb, IOAM6_EVENT_ATTR_TRACE_NODELEN, trace->nodelen) ||
- nla_put_u32(skb, IOAM6_EVENT_ATTR_TRACE_TYPE,
- be32_to_cpu(trace->type_be32)) ||
- nla_put(skb, IOAM6_EVENT_ATTR_TRACE_DATA,
- len - sizeof(struct ioam6_trace_hdr) - trace->remlen * 4,
- trace->data + trace->remlen * 4))
- return 1;
- return 0;
- }
- void ioam6_event(enum ioam6_event_type type, struct net *net, gfp_t gfp,
- void *opt, unsigned int opt_len)
- {
- struct nlmsghdr *nlh;
- struct sk_buff *skb;
- if (!genl_has_listeners(&ioam6_genl_family, net,
- IOAM6_GENL_EV_GRP_OFFSET))
- return;
- skb = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
- if (!skb)
- return;
- nlh = genlmsg_put(skb, 0, 0, &ioam6_genl_family, 0, type);
- if (!nlh)
- goto nla_put_failure;
- switch (type) {
- case IOAM6_EVENT_UNSPEC:
- WARN_ON_ONCE(1);
- break;
- case IOAM6_EVENT_TRACE:
- if (ioam6_event_put_trace(skb, (struct ioam6_trace_hdr *)opt,
- opt_len))
- goto nla_put_failure;
- break;
- }
- genlmsg_end(skb, nlh);
- genlmsg_multicast_netns(&ioam6_genl_family, net, skb, 0,
- IOAM6_GENL_EV_GRP_OFFSET, gfp);
- return;
- nla_put_failure:
- nlmsg_free(skb);
- }
- static struct genl_family ioam6_genl_family __ro_after_init = {
- .name = IOAM6_GENL_NAME,
- .version = IOAM6_GENL_VERSION,
- .netnsok = true,
- .parallel_ops = true,
- .ops = ioam6_genl_ops,
- .n_ops = ARRAY_SIZE(ioam6_genl_ops),
- .resv_start_op = IOAM6_CMD_NS_SET_SCHEMA + 1,
- .mcgrps = ioam6_mcgrps,
- .n_mcgrps = ARRAY_SIZE(ioam6_mcgrps),
- .module = THIS_MODULE,
- };
- struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id)
- {
- struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
- return rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
- }
- #define IOAM6_MASK_SHORT_FIELDS 0xff1ffc00
- #define IOAM6_MASK_WIDE_FIELDS 0x00e00000
- u8 ioam6_trace_compute_nodelen(u32 trace_type)
- {
- u8 nodelen = hweight32(trace_type & IOAM6_MASK_SHORT_FIELDS)
- * (sizeof(__be32) / 4);
- nodelen += hweight32(trace_type & IOAM6_MASK_WIDE_FIELDS)
- * (sizeof(__be64) / 4);
- return nodelen;
- }
- static void __ioam6_fill_trace_data(struct sk_buff *skb,
- struct ioam6_namespace *ns,
- struct ioam6_trace_hdr *trace,
- struct ioam6_schema *sc,
- unsigned int sclen, bool is_input)
- {
- /* Note: skb_dst_dev_rcu() can't be NULL at this point. */
- struct net_device *dev = skb_dst_dev_rcu(skb);
- struct inet6_dev *i_skb_dev, *idev;
- struct timespec64 ts;
- ktime_t tstamp;
- u64 raw64;
- u32 raw32;
- u16 raw16;
- u8 *data;
- u8 byte;
- data = trace->data + trace->remlen * 4 - trace->nodelen * 4 - sclen * 4;
- i_skb_dev = skb->dev ? __in6_dev_get(skb->dev) : NULL;
- idev = __in6_dev_get(dev);
- /* hop_lim and node_id */
- if (trace->type.bit0) {
- byte = ipv6_hdr(skb)->hop_limit;
- if (is_input)
- byte--;
- raw32 = READ_ONCE(dev_net(dev)->ipv6.sysctl.ioam6_id);
- *(__be32 *)data = cpu_to_be32((byte << 24) | raw32);
- data += sizeof(__be32);
- }
- /* ingress_if_id and egress_if_id */
- if (trace->type.bit1) {
- if (!i_skb_dev)
- raw16 = IOAM6_U16_UNAVAILABLE;
- else
- raw16 = (__force u16)READ_ONCE(i_skb_dev->cnf.ioam6_id);
- *(__be16 *)data = cpu_to_be16(raw16);
- data += sizeof(__be16);
- if ((dev->flags & IFF_LOOPBACK) || !idev)
- raw16 = IOAM6_U16_UNAVAILABLE;
- else
- raw16 = (__force u16)READ_ONCE(idev->cnf.ioam6_id);
- *(__be16 *)data = cpu_to_be16(raw16);
- data += sizeof(__be16);
- }
- /* timestamp seconds */
- if (trace->type.bit2) {
- if (!skb->dev) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- } else {
- tstamp = skb_tstamp_cond(skb, true);
- ts = ktime_to_timespec64(tstamp);
- *(__be32 *)data = cpu_to_be32((u32)ts.tv_sec);
- }
- data += sizeof(__be32);
- }
- /* timestamp subseconds */
- if (trace->type.bit3) {
- if (!skb->dev) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- } else {
- if (!trace->type.bit2) {
- tstamp = skb_tstamp_cond(skb, true);
- ts = ktime_to_timespec64(tstamp);
- }
- *(__be32 *)data = cpu_to_be32((u32)(ts.tv_nsec / NSEC_PER_USEC));
- }
- data += sizeof(__be32);
- }
- /* transit delay */
- if (trace->type.bit4) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* namespace data */
- if (trace->type.bit5) {
- *(__be32 *)data = ns->data;
- data += sizeof(__be32);
- }
- /* queue depth */
- if (trace->type.bit6) {
- struct netdev_queue *queue;
- struct Qdisc *qdisc;
- __u32 qlen, backlog;
- if (dev->flags & IFF_LOOPBACK ||
- skb_get_queue_mapping(skb) >= dev->num_tx_queues) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- } else {
- queue = skb_get_tx_queue(dev, skb);
- qdisc = rcu_dereference(queue->qdisc);
- spin_lock_bh(qdisc_lock(qdisc));
- qdisc_qstats_qlen_backlog(qdisc, &qlen, &backlog);
- spin_unlock_bh(qdisc_lock(qdisc));
- *(__be32 *)data = cpu_to_be32(backlog);
- }
- data += sizeof(__be32);
- }
- /* checksum complement */
- if (trace->type.bit7) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* hop_lim and node_id (wide) */
- if (trace->type.bit8) {
- byte = ipv6_hdr(skb)->hop_limit;
- if (is_input)
- byte--;
- raw64 = READ_ONCE(dev_net(dev)->ipv6.sysctl.ioam6_id_wide);
- *(__be64 *)data = cpu_to_be64(((u64)byte << 56) | raw64);
- data += sizeof(__be64);
- }
- /* ingress_if_id and egress_if_id (wide) */
- if (trace->type.bit9) {
- if (!i_skb_dev)
- raw32 = IOAM6_U32_UNAVAILABLE;
- else
- raw32 = READ_ONCE(i_skb_dev->cnf.ioam6_id_wide);
- *(__be32 *)data = cpu_to_be32(raw32);
- data += sizeof(__be32);
- if ((dev->flags & IFF_LOOPBACK) || !idev)
- raw32 = IOAM6_U32_UNAVAILABLE;
- else
- raw32 = READ_ONCE(idev->cnf.ioam6_id_wide);
- *(__be32 *)data = cpu_to_be32(raw32);
- data += sizeof(__be32);
- }
- /* namespace data (wide) */
- if (trace->type.bit10) {
- *(__be64 *)data = ns->data_wide;
- data += sizeof(__be64);
- }
- /* buffer occupancy */
- if (trace->type.bit11) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit12 undefined: filled with empty value */
- if (trace->type.bit12) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit13 undefined: filled with empty value */
- if (trace->type.bit13) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit14 undefined: filled with empty value */
- if (trace->type.bit14) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit15 undefined: filled with empty value */
- if (trace->type.bit15) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit16 undefined: filled with empty value */
- if (trace->type.bit16) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit17 undefined: filled with empty value */
- if (trace->type.bit17) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit18 undefined: filled with empty value */
- if (trace->type.bit18) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit19 undefined: filled with empty value */
- if (trace->type.bit19) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit20 undefined: filled with empty value */
- if (trace->type.bit20) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* bit21 undefined: filled with empty value */
- if (trace->type.bit21) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
- data += sizeof(__be32);
- }
- /* opaque state snapshot */
- if (trace->type.bit22) {
- if (!sc) {
- *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE >> 8);
- } else {
- *(__be32 *)data = sc->hdr;
- data += sizeof(__be32);
- memcpy(data, sc->data, sc->len);
- }
- }
- }
- /* called with rcu_read_lock() */
- void ioam6_fill_trace_data(struct sk_buff *skb,
- struct ioam6_namespace *ns,
- struct ioam6_trace_hdr *trace,
- bool is_input)
- {
- struct ioam6_schema *sc;
- unsigned int sclen = 0;
- /* Skip if Overflow flag is set
- */
- if (trace->overflow)
- return;
- /* NodeLen does not include Opaque State Snapshot length. We need to
- * take it into account if the corresponding bit is set (bit 22) and
- * if the current IOAM namespace has an active schema attached to it
- */
- sc = rcu_dereference(ns->schema);
- if (trace->type.bit22) {
- sclen = sizeof_field(struct ioam6_schema, hdr) / 4;
- if (sc)
- sclen += sc->len / 4;
- }
- /* If there is no space remaining, we set the Overflow flag and we
- * skip without filling the trace
- */
- if (!trace->remlen || trace->remlen < trace->nodelen + sclen) {
- trace->overflow = 1;
- return;
- }
- __ioam6_fill_trace_data(skb, ns, trace, sc, sclen, is_input);
- trace->remlen -= trace->nodelen + sclen;
- }
- static int __net_init ioam6_net_init(struct net *net)
- {
- struct ioam6_pernet_data *nsdata;
- int err = -ENOMEM;
- nsdata = kzalloc_obj(*nsdata);
- if (!nsdata)
- goto out;
- mutex_init(&nsdata->lock);
- net->ipv6.ioam6_data = nsdata;
- err = rhashtable_init(&nsdata->namespaces, &rht_ns_params);
- if (err)
- goto free_nsdata;
- err = rhashtable_init(&nsdata->schemas, &rht_sc_params);
- if (err)
- goto free_rht_ns;
- out:
- return err;
- free_rht_ns:
- rhashtable_destroy(&nsdata->namespaces);
- free_nsdata:
- kfree(nsdata);
- net->ipv6.ioam6_data = NULL;
- goto out;
- }
- static void __net_exit ioam6_net_exit(struct net *net)
- {
- struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
- rhashtable_free_and_destroy(&nsdata->namespaces, ioam6_free_ns, NULL);
- rhashtable_free_and_destroy(&nsdata->schemas, ioam6_free_sc, NULL);
- kfree(nsdata);
- }
- static struct pernet_operations ioam6_net_ops = {
- .init = ioam6_net_init,
- .exit = ioam6_net_exit,
- };
- int __init ioam6_init(void)
- {
- int err = register_pernet_subsys(&ioam6_net_ops);
- if (err)
- goto out;
- err = genl_register_family(&ioam6_genl_family);
- if (err)
- goto out_unregister_pernet_subsys;
- #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
- err = ioam6_iptunnel_init();
- if (err)
- goto out_unregister_genl;
- #endif
- pr_info("In-situ OAM (IOAM) with IPv6\n");
- out:
- return err;
- #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
- out_unregister_genl:
- genl_unregister_family(&ioam6_genl_family);
- #endif
- out_unregister_pernet_subsys:
- unregister_pernet_subsys(&ioam6_net_ops);
- goto out;
- }
- void ioam6_exit(void)
- {
- #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
- ioam6_iptunnel_exit();
- #endif
- genl_unregister_family(&ioam6_genl_family);
- unregister_pernet_subsys(&ioam6_net_ops);
- }
|