19ee11f0fSJustin Iurman // SPDX-License-Identifier: GPL-2.0+
29ee11f0fSJustin Iurman /*
39ee11f0fSJustin Iurman * IPv6 IOAM implementation
49ee11f0fSJustin Iurman *
59ee11f0fSJustin Iurman * Author:
69ee11f0fSJustin Iurman * Justin Iurman <justin.iurman@uliege.be>
79ee11f0fSJustin Iurman */
89ee11f0fSJustin Iurman
99ee11f0fSJustin Iurman #include <linux/errno.h>
109ee11f0fSJustin Iurman #include <linux/types.h>
119ee11f0fSJustin Iurman #include <linux/kernel.h>
129ee11f0fSJustin Iurman #include <linux/net.h>
139ee11f0fSJustin Iurman #include <linux/ioam6.h>
148c6f6fa6SJustin Iurman #include <linux/ioam6_genl.h>
159ee11f0fSJustin Iurman #include <linux/rhashtable.h>
16b63c5478SJustin Iurman #include <linux/netdevice.h>
179ee11f0fSJustin Iurman
189ee11f0fSJustin Iurman #include <net/addrconf.h>
198c6f6fa6SJustin Iurman #include <net/genetlink.h>
209ee11f0fSJustin Iurman #include <net/ioam6.h>
21b63c5478SJustin Iurman #include <net/sch_generic.h>
229ee11f0fSJustin Iurman
ioam6_ns_release(struct ioam6_namespace * ns)239ee11f0fSJustin Iurman static void ioam6_ns_release(struct ioam6_namespace *ns)
249ee11f0fSJustin Iurman {
259ee11f0fSJustin Iurman kfree_rcu(ns, rcu);
269ee11f0fSJustin Iurman }
279ee11f0fSJustin Iurman
ioam6_sc_release(struct ioam6_schema * sc)289ee11f0fSJustin Iurman static void ioam6_sc_release(struct ioam6_schema *sc)
299ee11f0fSJustin Iurman {
309ee11f0fSJustin Iurman kfree_rcu(sc, rcu);
319ee11f0fSJustin Iurman }
329ee11f0fSJustin Iurman
ioam6_free_ns(void * ptr,void * arg)339ee11f0fSJustin Iurman static void ioam6_free_ns(void *ptr, void *arg)
349ee11f0fSJustin Iurman {
359ee11f0fSJustin Iurman struct ioam6_namespace *ns = (struct ioam6_namespace *)ptr;
369ee11f0fSJustin Iurman
379ee11f0fSJustin Iurman if (ns)
389ee11f0fSJustin Iurman ioam6_ns_release(ns);
399ee11f0fSJustin Iurman }
409ee11f0fSJustin Iurman
ioam6_free_sc(void * ptr,void * arg)419ee11f0fSJustin Iurman static void ioam6_free_sc(void *ptr, void *arg)
429ee11f0fSJustin Iurman {
439ee11f0fSJustin Iurman struct ioam6_schema *sc = (struct ioam6_schema *)ptr;
449ee11f0fSJustin Iurman
459ee11f0fSJustin Iurman if (sc)
469ee11f0fSJustin Iurman ioam6_sc_release(sc);
479ee11f0fSJustin Iurman }
489ee11f0fSJustin Iurman
ioam6_ns_cmpfn(struct rhashtable_compare_arg * arg,const void * obj)499ee11f0fSJustin Iurman static int ioam6_ns_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
509ee11f0fSJustin Iurman {
519ee11f0fSJustin Iurman const struct ioam6_namespace *ns = obj;
529ee11f0fSJustin Iurman
539ee11f0fSJustin Iurman return (ns->id != *(__be16 *)arg->key);
549ee11f0fSJustin Iurman }
559ee11f0fSJustin Iurman
ioam6_sc_cmpfn(struct rhashtable_compare_arg * arg,const void * obj)569ee11f0fSJustin Iurman static int ioam6_sc_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
579ee11f0fSJustin Iurman {
589ee11f0fSJustin Iurman const struct ioam6_schema *sc = obj;
599ee11f0fSJustin Iurman
609ee11f0fSJustin Iurman return (sc->id != *(u32 *)arg->key);
619ee11f0fSJustin Iurman }
629ee11f0fSJustin Iurman
639ee11f0fSJustin Iurman static const struct rhashtable_params rht_ns_params = {
649ee11f0fSJustin Iurman .key_len = sizeof(__be16),
659ee11f0fSJustin Iurman .key_offset = offsetof(struct ioam6_namespace, id),
669ee11f0fSJustin Iurman .head_offset = offsetof(struct ioam6_namespace, head),
679ee11f0fSJustin Iurman .automatic_shrinking = true,
689ee11f0fSJustin Iurman .obj_cmpfn = ioam6_ns_cmpfn,
699ee11f0fSJustin Iurman };
709ee11f0fSJustin Iurman
719ee11f0fSJustin Iurman static const struct rhashtable_params rht_sc_params = {
729ee11f0fSJustin Iurman .key_len = sizeof(u32),
739ee11f0fSJustin Iurman .key_offset = offsetof(struct ioam6_schema, id),
749ee11f0fSJustin Iurman .head_offset = offsetof(struct ioam6_schema, head),
759ee11f0fSJustin Iurman .automatic_shrinking = true,
769ee11f0fSJustin Iurman .obj_cmpfn = ioam6_sc_cmpfn,
779ee11f0fSJustin Iurman };
789ee11f0fSJustin Iurman
798c6f6fa6SJustin Iurman static struct genl_family ioam6_genl_family;
808c6f6fa6SJustin Iurman
818c6f6fa6SJustin Iurman static const struct nla_policy ioam6_genl_policy_addns[] = {
828c6f6fa6SJustin Iurman [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
838c6f6fa6SJustin Iurman [IOAM6_ATTR_NS_DATA] = { .type = NLA_U32 },
848c6f6fa6SJustin Iurman [IOAM6_ATTR_NS_DATA_WIDE] = { .type = NLA_U64 },
858c6f6fa6SJustin Iurman };
868c6f6fa6SJustin Iurman
878c6f6fa6SJustin Iurman static const struct nla_policy ioam6_genl_policy_delns[] = {
888c6f6fa6SJustin Iurman [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
898c6f6fa6SJustin Iurman };
908c6f6fa6SJustin Iurman
918c6f6fa6SJustin Iurman static const struct nla_policy ioam6_genl_policy_addsc[] = {
928c6f6fa6SJustin Iurman [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
938c6f6fa6SJustin Iurman [IOAM6_ATTR_SC_DATA] = { .type = NLA_BINARY,
948c6f6fa6SJustin Iurman .len = IOAM6_MAX_SCHEMA_DATA_LEN },
958c6f6fa6SJustin Iurman };
968c6f6fa6SJustin Iurman
978c6f6fa6SJustin Iurman static const struct nla_policy ioam6_genl_policy_delsc[] = {
988c6f6fa6SJustin Iurman [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
998c6f6fa6SJustin Iurman };
1008c6f6fa6SJustin Iurman
1018c6f6fa6SJustin Iurman static const struct nla_policy ioam6_genl_policy_ns_sc[] = {
1028c6f6fa6SJustin Iurman [IOAM6_ATTR_NS_ID] = { .type = NLA_U16 },
1038c6f6fa6SJustin Iurman [IOAM6_ATTR_SC_ID] = { .type = NLA_U32 },
1048c6f6fa6SJustin Iurman [IOAM6_ATTR_SC_NONE] = { .type = NLA_FLAG },
1058c6f6fa6SJustin Iurman };
1068c6f6fa6SJustin Iurman
ioam6_genl_addns(struct sk_buff * skb,struct genl_info * info)1078c6f6fa6SJustin Iurman static int ioam6_genl_addns(struct sk_buff *skb, struct genl_info *info)
1088c6f6fa6SJustin Iurman {
1098c6f6fa6SJustin Iurman struct ioam6_pernet_data *nsdata;
1108c6f6fa6SJustin Iurman struct ioam6_namespace *ns;
1118c6f6fa6SJustin Iurman u64 data64;
1128c6f6fa6SJustin Iurman u32 data32;
1138c6f6fa6SJustin Iurman __be16 id;
1148c6f6fa6SJustin Iurman int err;
1158c6f6fa6SJustin Iurman
1168c6f6fa6SJustin Iurman if (!info->attrs[IOAM6_ATTR_NS_ID])
1178c6f6fa6SJustin Iurman return -EINVAL;
1188c6f6fa6SJustin Iurman
1198c6f6fa6SJustin Iurman id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
1208c6f6fa6SJustin Iurman nsdata = ioam6_pernet(genl_info_net(info));
1218c6f6fa6SJustin Iurman
1228c6f6fa6SJustin Iurman mutex_lock(&nsdata->lock);
1238c6f6fa6SJustin Iurman
1248c6f6fa6SJustin Iurman ns = rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
1258c6f6fa6SJustin Iurman if (ns) {
1268c6f6fa6SJustin Iurman err = -EEXIST;
1278c6f6fa6SJustin Iurman goto out_unlock;
1288c6f6fa6SJustin Iurman }
1298c6f6fa6SJustin Iurman
1308c6f6fa6SJustin Iurman ns = kzalloc(sizeof(*ns), GFP_KERNEL);
1318c6f6fa6SJustin Iurman if (!ns) {
1328c6f6fa6SJustin Iurman err = -ENOMEM;
1338c6f6fa6SJustin Iurman goto out_unlock;
1348c6f6fa6SJustin Iurman }
1358c6f6fa6SJustin Iurman
1368c6f6fa6SJustin Iurman ns->id = id;
1378c6f6fa6SJustin Iurman
1388c6f6fa6SJustin Iurman if (!info->attrs[IOAM6_ATTR_NS_DATA])
1398c6f6fa6SJustin Iurman data32 = IOAM6_U32_UNAVAILABLE;
1408c6f6fa6SJustin Iurman else
1418c6f6fa6SJustin Iurman data32 = nla_get_u32(info->attrs[IOAM6_ATTR_NS_DATA]);
1428c6f6fa6SJustin Iurman
1438c6f6fa6SJustin Iurman if (!info->attrs[IOAM6_ATTR_NS_DATA_WIDE])
1448c6f6fa6SJustin Iurman data64 = IOAM6_U64_UNAVAILABLE;
1458c6f6fa6SJustin Iurman else
1468c6f6fa6SJustin Iurman data64 = nla_get_u64(info->attrs[IOAM6_ATTR_NS_DATA_WIDE]);
1478c6f6fa6SJustin Iurman
1488c6f6fa6SJustin Iurman ns->data = cpu_to_be32(data32);
1498c6f6fa6SJustin Iurman ns->data_wide = cpu_to_be64(data64);
1508c6f6fa6SJustin Iurman
1518c6f6fa6SJustin Iurman err = rhashtable_lookup_insert_fast(&nsdata->namespaces, &ns->head,
1528c6f6fa6SJustin Iurman rht_ns_params);
1538c6f6fa6SJustin Iurman if (err)
1548c6f6fa6SJustin Iurman kfree(ns);
1558c6f6fa6SJustin Iurman
1568c6f6fa6SJustin Iurman out_unlock:
1578c6f6fa6SJustin Iurman mutex_unlock(&nsdata->lock);
1588c6f6fa6SJustin Iurman return err;
1598c6f6fa6SJustin Iurman }
1608c6f6fa6SJustin Iurman
ioam6_genl_delns(struct sk_buff * skb,struct genl_info * info)1618c6f6fa6SJustin Iurman static int ioam6_genl_delns(struct sk_buff *skb, struct genl_info *info)
1628c6f6fa6SJustin Iurman {
1638c6f6fa6SJustin Iurman struct ioam6_pernet_data *nsdata;
1648c6f6fa6SJustin Iurman struct ioam6_namespace *ns;
1658c6f6fa6SJustin Iurman struct ioam6_schema *sc;
1668c6f6fa6SJustin Iurman __be16 id;
1678c6f6fa6SJustin Iurman int err;
1688c6f6fa6SJustin Iurman
1698c6f6fa6SJustin Iurman if (!info->attrs[IOAM6_ATTR_NS_ID])
1708c6f6fa6SJustin Iurman return -EINVAL;
1718c6f6fa6SJustin Iurman
1728c6f6fa6SJustin Iurman id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
1738c6f6fa6SJustin Iurman nsdata = ioam6_pernet(genl_info_net(info));
1748c6f6fa6SJustin Iurman
1758c6f6fa6SJustin Iurman mutex_lock(&nsdata->lock);
1768c6f6fa6SJustin Iurman
1778c6f6fa6SJustin Iurman ns = rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
1788c6f6fa6SJustin Iurman if (!ns) {
1798c6f6fa6SJustin Iurman err = -ENOENT;
1808c6f6fa6SJustin Iurman goto out_unlock;
1818c6f6fa6SJustin Iurman }
1828c6f6fa6SJustin Iurman
1838c6f6fa6SJustin Iurman sc = rcu_dereference_protected(ns->schema,
1848c6f6fa6SJustin Iurman lockdep_is_held(&nsdata->lock));
1858c6f6fa6SJustin Iurman
1868c6f6fa6SJustin Iurman err = rhashtable_remove_fast(&nsdata->namespaces, &ns->head,
1878c6f6fa6SJustin Iurman rht_ns_params);
1888c6f6fa6SJustin Iurman if (err)
1898c6f6fa6SJustin Iurman goto out_unlock;
1908c6f6fa6SJustin Iurman
1918c6f6fa6SJustin Iurman if (sc)
1928c6f6fa6SJustin Iurman rcu_assign_pointer(sc->ns, NULL);
1938c6f6fa6SJustin Iurman
1948c6f6fa6SJustin Iurman ioam6_ns_release(ns);
1958c6f6fa6SJustin Iurman
1968c6f6fa6SJustin Iurman out_unlock:
1978c6f6fa6SJustin Iurman mutex_unlock(&nsdata->lock);
1988c6f6fa6SJustin Iurman return err;
1998c6f6fa6SJustin Iurman }
2008c6f6fa6SJustin Iurman
__ioam6_genl_dumpns_element(struct ioam6_namespace * ns,u32 portid,u32 seq,u32 flags,struct sk_buff * skb,u8 cmd)2018c6f6fa6SJustin Iurman static int __ioam6_genl_dumpns_element(struct ioam6_namespace *ns,
2028c6f6fa6SJustin Iurman u32 portid,
2038c6f6fa6SJustin Iurman u32 seq,
2048c6f6fa6SJustin Iurman u32 flags,
2058c6f6fa6SJustin Iurman struct sk_buff *skb,
2068c6f6fa6SJustin Iurman u8 cmd)
2078c6f6fa6SJustin Iurman {
2088c6f6fa6SJustin Iurman struct ioam6_schema *sc;
2098c6f6fa6SJustin Iurman u64 data64;
2108c6f6fa6SJustin Iurman u32 data32;
2118c6f6fa6SJustin Iurman void *hdr;
2128c6f6fa6SJustin Iurman
2138c6f6fa6SJustin Iurman hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd);
2148c6f6fa6SJustin Iurman if (!hdr)
2158c6f6fa6SJustin Iurman return -ENOMEM;
2168c6f6fa6SJustin Iurman
2178c6f6fa6SJustin Iurman data32 = be32_to_cpu(ns->data);
2188c6f6fa6SJustin Iurman data64 = be64_to_cpu(ns->data_wide);
2198c6f6fa6SJustin Iurman
2208c6f6fa6SJustin Iurman if (nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id)) ||
2218c6f6fa6SJustin Iurman (data32 != IOAM6_U32_UNAVAILABLE &&
2228c6f6fa6SJustin Iurman nla_put_u32(skb, IOAM6_ATTR_NS_DATA, data32)) ||
2238c6f6fa6SJustin Iurman (data64 != IOAM6_U64_UNAVAILABLE &&
2248c6f6fa6SJustin Iurman nla_put_u64_64bit(skb, IOAM6_ATTR_NS_DATA_WIDE,
2258c6f6fa6SJustin Iurman data64, IOAM6_ATTR_PAD)))
2268c6f6fa6SJustin Iurman goto nla_put_failure;
2278c6f6fa6SJustin Iurman
2288c6f6fa6SJustin Iurman rcu_read_lock();
2298c6f6fa6SJustin Iurman
2308c6f6fa6SJustin Iurman sc = rcu_dereference(ns->schema);
2318c6f6fa6SJustin Iurman if (sc && nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id)) {
2328c6f6fa6SJustin Iurman rcu_read_unlock();
2338c6f6fa6SJustin Iurman goto nla_put_failure;
2348c6f6fa6SJustin Iurman }
2358c6f6fa6SJustin Iurman
2368c6f6fa6SJustin Iurman rcu_read_unlock();
2378c6f6fa6SJustin Iurman
2388c6f6fa6SJustin Iurman genlmsg_end(skb, hdr);
2398c6f6fa6SJustin Iurman return 0;
2408c6f6fa6SJustin Iurman
2418c6f6fa6SJustin Iurman nla_put_failure:
2428c6f6fa6SJustin Iurman genlmsg_cancel(skb, hdr);
2438c6f6fa6SJustin Iurman return -EMSGSIZE;
2448c6f6fa6SJustin Iurman }
2458c6f6fa6SJustin Iurman
ioam6_genl_dumpns_start(struct netlink_callback * cb)2468c6f6fa6SJustin Iurman static int ioam6_genl_dumpns_start(struct netlink_callback *cb)
2478c6f6fa6SJustin Iurman {
2488c6f6fa6SJustin Iurman struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk));
2498c6f6fa6SJustin Iurman struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
2508c6f6fa6SJustin Iurman
2518c6f6fa6SJustin Iurman if (!iter) {
2528c6f6fa6SJustin Iurman iter = kmalloc(sizeof(*iter), GFP_KERNEL);
2538c6f6fa6SJustin Iurman if (!iter)
2548c6f6fa6SJustin Iurman return -ENOMEM;
2558c6f6fa6SJustin Iurman
2568c6f6fa6SJustin Iurman cb->args[0] = (long)iter;
2578c6f6fa6SJustin Iurman }
2588c6f6fa6SJustin Iurman
2598c6f6fa6SJustin Iurman rhashtable_walk_enter(&nsdata->namespaces, iter);
2608c6f6fa6SJustin Iurman
2618c6f6fa6SJustin Iurman return 0;
2628c6f6fa6SJustin Iurman }
2638c6f6fa6SJustin Iurman
ioam6_genl_dumpns_done(struct netlink_callback * cb)2648c6f6fa6SJustin Iurman static int ioam6_genl_dumpns_done(struct netlink_callback *cb)
2658c6f6fa6SJustin Iurman {
2668c6f6fa6SJustin Iurman struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
2678c6f6fa6SJustin Iurman
2688c6f6fa6SJustin Iurman rhashtable_walk_exit(iter);
2698c6f6fa6SJustin Iurman kfree(iter);
2708c6f6fa6SJustin Iurman
2718c6f6fa6SJustin Iurman return 0;
2728c6f6fa6SJustin Iurman }
2738c6f6fa6SJustin Iurman
ioam6_genl_dumpns(struct sk_buff * skb,struct netlink_callback * cb)2748c6f6fa6SJustin Iurman static int ioam6_genl_dumpns(struct sk_buff *skb, struct netlink_callback *cb)
2758c6f6fa6SJustin Iurman {
2768c6f6fa6SJustin Iurman struct rhashtable_iter *iter;
2778c6f6fa6SJustin Iurman struct ioam6_namespace *ns;
2788c6f6fa6SJustin Iurman int err;
2798c6f6fa6SJustin Iurman
2808c6f6fa6SJustin Iurman iter = (struct rhashtable_iter *)cb->args[0];
2818c6f6fa6SJustin Iurman rhashtable_walk_start(iter);
2828c6f6fa6SJustin Iurman
2838c6f6fa6SJustin Iurman for (;;) {
2848c6f6fa6SJustin Iurman ns = rhashtable_walk_next(iter);
2858c6f6fa6SJustin Iurman
2868c6f6fa6SJustin Iurman if (IS_ERR(ns)) {
2878c6f6fa6SJustin Iurman if (PTR_ERR(ns) == -EAGAIN)
2888c6f6fa6SJustin Iurman continue;
2898c6f6fa6SJustin Iurman err = PTR_ERR(ns);
2908c6f6fa6SJustin Iurman goto done;
2918c6f6fa6SJustin Iurman } else if (!ns) {
2928c6f6fa6SJustin Iurman break;
2938c6f6fa6SJustin Iurman }
2948c6f6fa6SJustin Iurman
2958c6f6fa6SJustin Iurman err = __ioam6_genl_dumpns_element(ns,
2968c6f6fa6SJustin Iurman NETLINK_CB(cb->skb).portid,
2978c6f6fa6SJustin Iurman cb->nlh->nlmsg_seq,
2988c6f6fa6SJustin Iurman NLM_F_MULTI,
2998c6f6fa6SJustin Iurman skb,
3008c6f6fa6SJustin Iurman IOAM6_CMD_DUMP_NAMESPACES);
3018c6f6fa6SJustin Iurman if (err)
3028c6f6fa6SJustin Iurman goto done;
3038c6f6fa6SJustin Iurman }
3048c6f6fa6SJustin Iurman
3058c6f6fa6SJustin Iurman err = skb->len;
3068c6f6fa6SJustin Iurman
3078c6f6fa6SJustin Iurman done:
3088c6f6fa6SJustin Iurman rhashtable_walk_stop(iter);
3098c6f6fa6SJustin Iurman return err;
3108c6f6fa6SJustin Iurman }
3118c6f6fa6SJustin Iurman
ioam6_genl_addsc(struct sk_buff * skb,struct genl_info * info)3128c6f6fa6SJustin Iurman static int ioam6_genl_addsc(struct sk_buff *skb, struct genl_info *info)
3138c6f6fa6SJustin Iurman {
3148c6f6fa6SJustin Iurman struct ioam6_pernet_data *nsdata;
3158c6f6fa6SJustin Iurman int len, len_aligned, err;
3168c6f6fa6SJustin Iurman struct ioam6_schema *sc;
3178c6f6fa6SJustin Iurman u32 id;
3188c6f6fa6SJustin Iurman
3198c6f6fa6SJustin Iurman if (!info->attrs[IOAM6_ATTR_SC_ID] || !info->attrs[IOAM6_ATTR_SC_DATA])
3208c6f6fa6SJustin Iurman return -EINVAL;
3218c6f6fa6SJustin Iurman
3228c6f6fa6SJustin Iurman id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
3238c6f6fa6SJustin Iurman nsdata = ioam6_pernet(genl_info_net(info));
3248c6f6fa6SJustin Iurman
3258c6f6fa6SJustin Iurman mutex_lock(&nsdata->lock);
3268c6f6fa6SJustin Iurman
3278c6f6fa6SJustin Iurman sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params);
3288c6f6fa6SJustin Iurman if (sc) {
3298c6f6fa6SJustin Iurman err = -EEXIST;
3308c6f6fa6SJustin Iurman goto out_unlock;
3318c6f6fa6SJustin Iurman }
3328c6f6fa6SJustin Iurman
3338c6f6fa6SJustin Iurman len = nla_len(info->attrs[IOAM6_ATTR_SC_DATA]);
3348c6f6fa6SJustin Iurman len_aligned = ALIGN(len, 4);
3358c6f6fa6SJustin Iurman
3368c6f6fa6SJustin Iurman sc = kzalloc(sizeof(*sc) + len_aligned, GFP_KERNEL);
3378c6f6fa6SJustin Iurman if (!sc) {
3388c6f6fa6SJustin Iurman err = -ENOMEM;
3398c6f6fa6SJustin Iurman goto out_unlock;
3408c6f6fa6SJustin Iurman }
3418c6f6fa6SJustin Iurman
3428c6f6fa6SJustin Iurman sc->id = id;
3438c6f6fa6SJustin Iurman sc->len = len_aligned;
3448c6f6fa6SJustin Iurman sc->hdr = cpu_to_be32(sc->id | ((u8)(sc->len / 4) << 24));
3458c6f6fa6SJustin Iurman nla_memcpy(sc->data, info->attrs[IOAM6_ATTR_SC_DATA], len);
3468c6f6fa6SJustin Iurman
3478c6f6fa6SJustin Iurman err = rhashtable_lookup_insert_fast(&nsdata->schemas, &sc->head,
3488c6f6fa6SJustin Iurman rht_sc_params);
3498c6f6fa6SJustin Iurman if (err)
3508c6f6fa6SJustin Iurman goto free_sc;
3518c6f6fa6SJustin Iurman
3528c6f6fa6SJustin Iurman out_unlock:
3538c6f6fa6SJustin Iurman mutex_unlock(&nsdata->lock);
3548c6f6fa6SJustin Iurman return err;
3558c6f6fa6SJustin Iurman free_sc:
3568c6f6fa6SJustin Iurman kfree(sc);
3578c6f6fa6SJustin Iurman goto out_unlock;
3588c6f6fa6SJustin Iurman }
3598c6f6fa6SJustin Iurman
ioam6_genl_delsc(struct sk_buff * skb,struct genl_info * info)3608c6f6fa6SJustin Iurman static int ioam6_genl_delsc(struct sk_buff *skb, struct genl_info *info)
3618c6f6fa6SJustin Iurman {
3628c6f6fa6SJustin Iurman struct ioam6_pernet_data *nsdata;
3638c6f6fa6SJustin Iurman struct ioam6_namespace *ns;
3648c6f6fa6SJustin Iurman struct ioam6_schema *sc;
3658c6f6fa6SJustin Iurman int err;
3668c6f6fa6SJustin Iurman u32 id;
3678c6f6fa6SJustin Iurman
3688c6f6fa6SJustin Iurman if (!info->attrs[IOAM6_ATTR_SC_ID])
3698c6f6fa6SJustin Iurman return -EINVAL;
3708c6f6fa6SJustin Iurman
3718c6f6fa6SJustin Iurman id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
3728c6f6fa6SJustin Iurman nsdata = ioam6_pernet(genl_info_net(info));
3738c6f6fa6SJustin Iurman
3748c6f6fa6SJustin Iurman mutex_lock(&nsdata->lock);
3758c6f6fa6SJustin Iurman
3768c6f6fa6SJustin Iurman sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params);
3778c6f6fa6SJustin Iurman if (!sc) {
3788c6f6fa6SJustin Iurman err = -ENOENT;
3798c6f6fa6SJustin Iurman goto out_unlock;
3808c6f6fa6SJustin Iurman }
3818c6f6fa6SJustin Iurman
3828c6f6fa6SJustin Iurman ns = rcu_dereference_protected(sc->ns, lockdep_is_held(&nsdata->lock));
3838c6f6fa6SJustin Iurman
3848c6f6fa6SJustin Iurman err = rhashtable_remove_fast(&nsdata->schemas, &sc->head,
3858c6f6fa6SJustin Iurman rht_sc_params);
3868c6f6fa6SJustin Iurman if (err)
3878c6f6fa6SJustin Iurman goto out_unlock;
3888c6f6fa6SJustin Iurman
3898c6f6fa6SJustin Iurman if (ns)
3908c6f6fa6SJustin Iurman rcu_assign_pointer(ns->schema, NULL);
3918c6f6fa6SJustin Iurman
3928c6f6fa6SJustin Iurman ioam6_sc_release(sc);
3938c6f6fa6SJustin Iurman
3948c6f6fa6SJustin Iurman out_unlock:
3958c6f6fa6SJustin Iurman mutex_unlock(&nsdata->lock);
3968c6f6fa6SJustin Iurman return err;
3978c6f6fa6SJustin Iurman }
3988c6f6fa6SJustin Iurman
__ioam6_genl_dumpsc_element(struct ioam6_schema * sc,u32 portid,u32 seq,u32 flags,struct sk_buff * skb,u8 cmd)3998c6f6fa6SJustin Iurman static int __ioam6_genl_dumpsc_element(struct ioam6_schema *sc,
4008c6f6fa6SJustin Iurman u32 portid, u32 seq, u32 flags,
4018c6f6fa6SJustin Iurman struct sk_buff *skb, u8 cmd)
4028c6f6fa6SJustin Iurman {
4038c6f6fa6SJustin Iurman struct ioam6_namespace *ns;
4048c6f6fa6SJustin Iurman void *hdr;
4058c6f6fa6SJustin Iurman
4068c6f6fa6SJustin Iurman hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd);
4078c6f6fa6SJustin Iurman if (!hdr)
4088c6f6fa6SJustin Iurman return -ENOMEM;
4098c6f6fa6SJustin Iurman
4108c6f6fa6SJustin Iurman if (nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id) ||
4118c6f6fa6SJustin Iurman nla_put(skb, IOAM6_ATTR_SC_DATA, sc->len, sc->data))
4128c6f6fa6SJustin Iurman goto nla_put_failure;
4138c6f6fa6SJustin Iurman
4148c6f6fa6SJustin Iurman rcu_read_lock();
4158c6f6fa6SJustin Iurman
4168c6f6fa6SJustin Iurman ns = rcu_dereference(sc->ns);
4178c6f6fa6SJustin Iurman if (ns && nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id))) {
4188c6f6fa6SJustin Iurman rcu_read_unlock();
4198c6f6fa6SJustin Iurman goto nla_put_failure;
4208c6f6fa6SJustin Iurman }
4218c6f6fa6SJustin Iurman
4228c6f6fa6SJustin Iurman rcu_read_unlock();
4238c6f6fa6SJustin Iurman
4248c6f6fa6SJustin Iurman genlmsg_end(skb, hdr);
4258c6f6fa6SJustin Iurman return 0;
4268c6f6fa6SJustin Iurman
4278c6f6fa6SJustin Iurman nla_put_failure:
4288c6f6fa6SJustin Iurman genlmsg_cancel(skb, hdr);
4298c6f6fa6SJustin Iurman return -EMSGSIZE;
4308c6f6fa6SJustin Iurman }
4318c6f6fa6SJustin Iurman
ioam6_genl_dumpsc_start(struct netlink_callback * cb)4328c6f6fa6SJustin Iurman static int ioam6_genl_dumpsc_start(struct netlink_callback *cb)
4338c6f6fa6SJustin Iurman {
4348c6f6fa6SJustin Iurman struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk));
4358c6f6fa6SJustin Iurman struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
4368c6f6fa6SJustin Iurman
4378c6f6fa6SJustin Iurman if (!iter) {
4388c6f6fa6SJustin Iurman iter = kmalloc(sizeof(*iter), GFP_KERNEL);
4398c6f6fa6SJustin Iurman if (!iter)
4408c6f6fa6SJustin Iurman return -ENOMEM;
4418c6f6fa6SJustin Iurman
4428c6f6fa6SJustin Iurman cb->args[0] = (long)iter;
4438c6f6fa6SJustin Iurman }
4448c6f6fa6SJustin Iurman
4458c6f6fa6SJustin Iurman rhashtable_walk_enter(&nsdata->schemas, iter);
4468c6f6fa6SJustin Iurman
4478c6f6fa6SJustin Iurman return 0;
4488c6f6fa6SJustin Iurman }
4498c6f6fa6SJustin Iurman
ioam6_genl_dumpsc_done(struct netlink_callback * cb)4508c6f6fa6SJustin Iurman static int ioam6_genl_dumpsc_done(struct netlink_callback *cb)
4518c6f6fa6SJustin Iurman {
4528c6f6fa6SJustin Iurman struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
4538c6f6fa6SJustin Iurman
4548c6f6fa6SJustin Iurman rhashtable_walk_exit(iter);
4558c6f6fa6SJustin Iurman kfree(iter);
4568c6f6fa6SJustin Iurman
4578c6f6fa6SJustin Iurman return 0;
4588c6f6fa6SJustin Iurman }
4598c6f6fa6SJustin Iurman
ioam6_genl_dumpsc(struct sk_buff * skb,struct netlink_callback * cb)4608c6f6fa6SJustin Iurman static int ioam6_genl_dumpsc(struct sk_buff *skb, struct netlink_callback *cb)
4618c6f6fa6SJustin Iurman {
4628c6f6fa6SJustin Iurman struct rhashtable_iter *iter;
4638c6f6fa6SJustin Iurman struct ioam6_schema *sc;
4648c6f6fa6SJustin Iurman int err;
4658c6f6fa6SJustin Iurman
4668c6f6fa6SJustin Iurman iter = (struct rhashtable_iter *)cb->args[0];
4678c6f6fa6SJustin Iurman rhashtable_walk_start(iter);
4688c6f6fa6SJustin Iurman
4698c6f6fa6SJustin Iurman for (;;) {
4708c6f6fa6SJustin Iurman sc = rhashtable_walk_next(iter);
4718c6f6fa6SJustin Iurman
4728c6f6fa6SJustin Iurman if (IS_ERR(sc)) {
4738c6f6fa6SJustin Iurman if (PTR_ERR(sc) == -EAGAIN)
4748c6f6fa6SJustin Iurman continue;
4758c6f6fa6SJustin Iurman err = PTR_ERR(sc);
4768c6f6fa6SJustin Iurman goto done;
4778c6f6fa6SJustin Iurman } else if (!sc) {
4788c6f6fa6SJustin Iurman break;
4798c6f6fa6SJustin Iurman }
4808c6f6fa6SJustin Iurman
4818c6f6fa6SJustin Iurman err = __ioam6_genl_dumpsc_element(sc,
4828c6f6fa6SJustin Iurman NETLINK_CB(cb->skb).portid,
4838c6f6fa6SJustin Iurman cb->nlh->nlmsg_seq,
4848c6f6fa6SJustin Iurman NLM_F_MULTI,
4858c6f6fa6SJustin Iurman skb,
4868c6f6fa6SJustin Iurman IOAM6_CMD_DUMP_SCHEMAS);
4878c6f6fa6SJustin Iurman if (err)
4888c6f6fa6SJustin Iurman goto done;
4898c6f6fa6SJustin Iurman }
4908c6f6fa6SJustin Iurman
4918c6f6fa6SJustin Iurman err = skb->len;
4928c6f6fa6SJustin Iurman
4938c6f6fa6SJustin Iurman done:
4948c6f6fa6SJustin Iurman rhashtable_walk_stop(iter);
4958c6f6fa6SJustin Iurman return err;
4968c6f6fa6SJustin Iurman }
4978c6f6fa6SJustin Iurman
ioam6_genl_ns_set_schema(struct sk_buff * skb,struct genl_info * info)4988c6f6fa6SJustin Iurman static int ioam6_genl_ns_set_schema(struct sk_buff *skb, struct genl_info *info)
4998c6f6fa6SJustin Iurman {
5008c6f6fa6SJustin Iurman struct ioam6_namespace *ns, *ns_ref;
5018c6f6fa6SJustin Iurman struct ioam6_schema *sc, *sc_ref;
5028c6f6fa6SJustin Iurman struct ioam6_pernet_data *nsdata;
5038c6f6fa6SJustin Iurman __be16 ns_id;
5048c6f6fa6SJustin Iurman u32 sc_id;
5058c6f6fa6SJustin Iurman int err;
5068c6f6fa6SJustin Iurman
5078c6f6fa6SJustin Iurman if (!info->attrs[IOAM6_ATTR_NS_ID] ||
5088c6f6fa6SJustin Iurman (!info->attrs[IOAM6_ATTR_SC_ID] &&
5098c6f6fa6SJustin Iurman !info->attrs[IOAM6_ATTR_SC_NONE]))
5108c6f6fa6SJustin Iurman return -EINVAL;
5118c6f6fa6SJustin Iurman
5128c6f6fa6SJustin Iurman ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
5138c6f6fa6SJustin Iurman nsdata = ioam6_pernet(genl_info_net(info));
5148c6f6fa6SJustin Iurman
5158c6f6fa6SJustin Iurman mutex_lock(&nsdata->lock);
5168c6f6fa6SJustin Iurman
5178c6f6fa6SJustin Iurman ns = rhashtable_lookup_fast(&nsdata->namespaces, &ns_id, rht_ns_params);
5188c6f6fa6SJustin Iurman if (!ns) {
5198c6f6fa6SJustin Iurman err = -ENOENT;
5208c6f6fa6SJustin Iurman goto out_unlock;
5218c6f6fa6SJustin Iurman }
5228c6f6fa6SJustin Iurman
5238c6f6fa6SJustin Iurman if (info->attrs[IOAM6_ATTR_SC_NONE]) {
5248c6f6fa6SJustin Iurman sc = NULL;
5258c6f6fa6SJustin Iurman } else {
5268c6f6fa6SJustin Iurman sc_id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
5278c6f6fa6SJustin Iurman sc = rhashtable_lookup_fast(&nsdata->schemas, &sc_id,
5288c6f6fa6SJustin Iurman rht_sc_params);
5298c6f6fa6SJustin Iurman if (!sc) {
5308c6f6fa6SJustin Iurman err = -ENOENT;
5318c6f6fa6SJustin Iurman goto out_unlock;
5328c6f6fa6SJustin Iurman }
5338c6f6fa6SJustin Iurman }
5348c6f6fa6SJustin Iurman
5358c6f6fa6SJustin Iurman sc_ref = rcu_dereference_protected(ns->schema,
5368c6f6fa6SJustin Iurman lockdep_is_held(&nsdata->lock));
5378c6f6fa6SJustin Iurman if (sc_ref)
5388c6f6fa6SJustin Iurman rcu_assign_pointer(sc_ref->ns, NULL);
5398c6f6fa6SJustin Iurman rcu_assign_pointer(ns->schema, sc);
5408c6f6fa6SJustin Iurman
5418c6f6fa6SJustin Iurman if (sc) {
5428c6f6fa6SJustin Iurman ns_ref = rcu_dereference_protected(sc->ns,
5438c6f6fa6SJustin Iurman lockdep_is_held(&nsdata->lock));
5448c6f6fa6SJustin Iurman if (ns_ref)
5458c6f6fa6SJustin Iurman rcu_assign_pointer(ns_ref->schema, NULL);
5468c6f6fa6SJustin Iurman rcu_assign_pointer(sc->ns, ns);
5478c6f6fa6SJustin Iurman }
5488c6f6fa6SJustin Iurman
5498c6f6fa6SJustin Iurman err = 0;
5508c6f6fa6SJustin Iurman
5518c6f6fa6SJustin Iurman out_unlock:
5528c6f6fa6SJustin Iurman mutex_unlock(&nsdata->lock);
5538c6f6fa6SJustin Iurman return err;
5548c6f6fa6SJustin Iurman }
5558c6f6fa6SJustin Iurman
5568c6f6fa6SJustin Iurman static const struct genl_ops ioam6_genl_ops[] = {
5578c6f6fa6SJustin Iurman {
5588c6f6fa6SJustin Iurman .cmd = IOAM6_CMD_ADD_NAMESPACE,
5598c6f6fa6SJustin Iurman .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5608c6f6fa6SJustin Iurman .doit = ioam6_genl_addns,
5618c6f6fa6SJustin Iurman .flags = GENL_ADMIN_PERM,
5628c6f6fa6SJustin Iurman .policy = ioam6_genl_policy_addns,
5638c6f6fa6SJustin Iurman .maxattr = ARRAY_SIZE(ioam6_genl_policy_addns) - 1,
5648c6f6fa6SJustin Iurman },
5658c6f6fa6SJustin Iurman {
5668c6f6fa6SJustin Iurman .cmd = IOAM6_CMD_DEL_NAMESPACE,
5678c6f6fa6SJustin Iurman .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5688c6f6fa6SJustin Iurman .doit = ioam6_genl_delns,
5698c6f6fa6SJustin Iurman .flags = GENL_ADMIN_PERM,
5708c6f6fa6SJustin Iurman .policy = ioam6_genl_policy_delns,
5718c6f6fa6SJustin Iurman .maxattr = ARRAY_SIZE(ioam6_genl_policy_delns) - 1,
5728c6f6fa6SJustin Iurman },
5738c6f6fa6SJustin Iurman {
5748c6f6fa6SJustin Iurman .cmd = IOAM6_CMD_DUMP_NAMESPACES,
5758c6f6fa6SJustin Iurman .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5768c6f6fa6SJustin Iurman .start = ioam6_genl_dumpns_start,
5778c6f6fa6SJustin Iurman .dumpit = ioam6_genl_dumpns,
5788c6f6fa6SJustin Iurman .done = ioam6_genl_dumpns_done,
5798c6f6fa6SJustin Iurman .flags = GENL_ADMIN_PERM,
5808c6f6fa6SJustin Iurman },
5818c6f6fa6SJustin Iurman {
5828c6f6fa6SJustin Iurman .cmd = IOAM6_CMD_ADD_SCHEMA,
5838c6f6fa6SJustin Iurman .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5848c6f6fa6SJustin Iurman .doit = ioam6_genl_addsc,
5858c6f6fa6SJustin Iurman .flags = GENL_ADMIN_PERM,
5868c6f6fa6SJustin Iurman .policy = ioam6_genl_policy_addsc,
5878c6f6fa6SJustin Iurman .maxattr = ARRAY_SIZE(ioam6_genl_policy_addsc) - 1,
5888c6f6fa6SJustin Iurman },
5898c6f6fa6SJustin Iurman {
5908c6f6fa6SJustin Iurman .cmd = IOAM6_CMD_DEL_SCHEMA,
5918c6f6fa6SJustin Iurman .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5928c6f6fa6SJustin Iurman .doit = ioam6_genl_delsc,
5938c6f6fa6SJustin Iurman .flags = GENL_ADMIN_PERM,
5948c6f6fa6SJustin Iurman .policy = ioam6_genl_policy_delsc,
5958c6f6fa6SJustin Iurman .maxattr = ARRAY_SIZE(ioam6_genl_policy_delsc) - 1,
5968c6f6fa6SJustin Iurman },
5978c6f6fa6SJustin Iurman {
5988c6f6fa6SJustin Iurman .cmd = IOAM6_CMD_DUMP_SCHEMAS,
5998c6f6fa6SJustin Iurman .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6008c6f6fa6SJustin Iurman .start = ioam6_genl_dumpsc_start,
6018c6f6fa6SJustin Iurman .dumpit = ioam6_genl_dumpsc,
6028c6f6fa6SJustin Iurman .done = ioam6_genl_dumpsc_done,
6038c6f6fa6SJustin Iurman .flags = GENL_ADMIN_PERM,
6048c6f6fa6SJustin Iurman },
6058c6f6fa6SJustin Iurman {
6068c6f6fa6SJustin Iurman .cmd = IOAM6_CMD_NS_SET_SCHEMA,
6078c6f6fa6SJustin Iurman .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6088c6f6fa6SJustin Iurman .doit = ioam6_genl_ns_set_schema,
6098c6f6fa6SJustin Iurman .flags = GENL_ADMIN_PERM,
6108c6f6fa6SJustin Iurman .policy = ioam6_genl_policy_ns_sc,
6118c6f6fa6SJustin Iurman .maxattr = ARRAY_SIZE(ioam6_genl_policy_ns_sc) - 1,
6128c6f6fa6SJustin Iurman },
6138c6f6fa6SJustin Iurman };
6148c6f6fa6SJustin Iurman
6158c6f6fa6SJustin Iurman static struct genl_family ioam6_genl_family __ro_after_init = {
6168c6f6fa6SJustin Iurman .name = IOAM6_GENL_NAME,
6178c6f6fa6SJustin Iurman .version = IOAM6_GENL_VERSION,
6188c6f6fa6SJustin Iurman .netnsok = true,
6198c6f6fa6SJustin Iurman .parallel_ops = true,
6208c6f6fa6SJustin Iurman .ops = ioam6_genl_ops,
6218c6f6fa6SJustin Iurman .n_ops = ARRAY_SIZE(ioam6_genl_ops),
622*9c5d03d3SJakub Kicinski .resv_start_op = IOAM6_CMD_NS_SET_SCHEMA + 1,
6238c6f6fa6SJustin Iurman .module = THIS_MODULE,
6248c6f6fa6SJustin Iurman };
6258c6f6fa6SJustin Iurman
ioam6_namespace(struct net * net,__be16 id)6269ee11f0fSJustin Iurman struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id)
6279ee11f0fSJustin Iurman {
6289ee11f0fSJustin Iurman struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
6299ee11f0fSJustin Iurman
6309ee11f0fSJustin Iurman return rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
6319ee11f0fSJustin Iurman }
6329ee11f0fSJustin Iurman
__ioam6_fill_trace_data(struct sk_buff * skb,struct ioam6_namespace * ns,struct ioam6_trace_hdr * trace,struct ioam6_schema * sc,u8 sclen,bool is_input)6339ee11f0fSJustin Iurman static void __ioam6_fill_trace_data(struct sk_buff *skb,
6349ee11f0fSJustin Iurman struct ioam6_namespace *ns,
6359ee11f0fSJustin Iurman struct ioam6_trace_hdr *trace,
6369ee11f0fSJustin Iurman struct ioam6_schema *sc,
63752d03786SJustin Iurman u8 sclen, bool is_input)
6389ee11f0fSJustin Iurman {
639b6561f84SMartin KaFai Lau struct timespec64 ts;
640b6561f84SMartin KaFai Lau ktime_t tstamp;
6419ee11f0fSJustin Iurman u64 raw64;
6429ee11f0fSJustin Iurman u32 raw32;
6439ee11f0fSJustin Iurman u16 raw16;
6449ee11f0fSJustin Iurman u8 *data;
6459ee11f0fSJustin Iurman u8 byte;
6469ee11f0fSJustin Iurman
6479ee11f0fSJustin Iurman data = trace->data + trace->remlen * 4 - trace->nodelen * 4 - sclen * 4;
6489ee11f0fSJustin Iurman
6499ee11f0fSJustin Iurman /* hop_lim and node_id */
6509ee11f0fSJustin Iurman if (trace->type.bit0) {
6519ee11f0fSJustin Iurman byte = ipv6_hdr(skb)->hop_limit;
65252d03786SJustin Iurman if (is_input)
6539ee11f0fSJustin Iurman byte--;
6549ee11f0fSJustin Iurman
6553edede08SJustin Iurman raw32 = dev_net(skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id;
6569ee11f0fSJustin Iurman
6579ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32((byte << 24) | raw32);
6589ee11f0fSJustin Iurman data += sizeof(__be32);
6599ee11f0fSJustin Iurman }
6609ee11f0fSJustin Iurman
6619ee11f0fSJustin Iurman /* ingress_if_id and egress_if_id */
6629ee11f0fSJustin Iurman if (trace->type.bit1) {
6639ee11f0fSJustin Iurman if (!skb->dev)
6649ee11f0fSJustin Iurman raw16 = IOAM6_U16_UNAVAILABLE;
6659ee11f0fSJustin Iurman else
6669ee11f0fSJustin Iurman raw16 = (__force u16)__in6_dev_get(skb->dev)->cnf.ioam6_id;
6679ee11f0fSJustin Iurman
6689ee11f0fSJustin Iurman *(__be16 *)data = cpu_to_be16(raw16);
6699ee11f0fSJustin Iurman data += sizeof(__be16);
6709ee11f0fSJustin Iurman
6719ee11f0fSJustin Iurman if (skb_dst(skb)->dev->flags & IFF_LOOPBACK)
6729ee11f0fSJustin Iurman raw16 = IOAM6_U16_UNAVAILABLE;
6739ee11f0fSJustin Iurman else
6749ee11f0fSJustin Iurman raw16 = (__force u16)__in6_dev_get(skb_dst(skb)->dev)->cnf.ioam6_id;
6759ee11f0fSJustin Iurman
6769ee11f0fSJustin Iurman *(__be16 *)data = cpu_to_be16(raw16);
6779ee11f0fSJustin Iurman data += sizeof(__be16);
6789ee11f0fSJustin Iurman }
6799ee11f0fSJustin Iurman
6809ee11f0fSJustin Iurman /* timestamp seconds */
6819ee11f0fSJustin Iurman if (trace->type.bit2) {
6823edede08SJustin Iurman if (!skb->dev) {
6833edede08SJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
6843edede08SJustin Iurman } else {
685b6561f84SMartin KaFai Lau tstamp = skb_tstamp_cond(skb, true);
686b6561f84SMartin KaFai Lau ts = ktime_to_timespec64(tstamp);
6879ee11f0fSJustin Iurman
6889ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32((u32)ts.tv_sec);
6893edede08SJustin Iurman }
6909ee11f0fSJustin Iurman data += sizeof(__be32);
6919ee11f0fSJustin Iurman }
6929ee11f0fSJustin Iurman
6939ee11f0fSJustin Iurman /* timestamp subseconds */
6949ee11f0fSJustin Iurman if (trace->type.bit3) {
6953edede08SJustin Iurman if (!skb->dev) {
6963edede08SJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
6973edede08SJustin Iurman } else {
698b6561f84SMartin KaFai Lau if (!trace->type.bit2) {
699b6561f84SMartin KaFai Lau tstamp = skb_tstamp_cond(skb, true);
700b6561f84SMartin KaFai Lau ts = ktime_to_timespec64(tstamp);
701b6561f84SMartin KaFai Lau }
7029ee11f0fSJustin Iurman
703b6561f84SMartin KaFai Lau *(__be32 *)data = cpu_to_be32((u32)(ts.tv_nsec / NSEC_PER_USEC));
7043edede08SJustin Iurman }
7059ee11f0fSJustin Iurman data += sizeof(__be32);
7069ee11f0fSJustin Iurman }
7079ee11f0fSJustin Iurman
7089ee11f0fSJustin Iurman /* transit delay */
7099ee11f0fSJustin Iurman if (trace->type.bit4) {
7109ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7119ee11f0fSJustin Iurman data += sizeof(__be32);
7129ee11f0fSJustin Iurman }
7139ee11f0fSJustin Iurman
7149ee11f0fSJustin Iurman /* namespace data */
7159ee11f0fSJustin Iurman if (trace->type.bit5) {
7169ee11f0fSJustin Iurman *(__be32 *)data = ns->data;
7179ee11f0fSJustin Iurman data += sizeof(__be32);
7189ee11f0fSJustin Iurman }
7199ee11f0fSJustin Iurman
7209ee11f0fSJustin Iurman /* queue depth */
7219ee11f0fSJustin Iurman if (trace->type.bit6) {
722b63c5478SJustin Iurman struct netdev_queue *queue;
723b63c5478SJustin Iurman struct Qdisc *qdisc;
724b63c5478SJustin Iurman __u32 qlen, backlog;
725b63c5478SJustin Iurman
726b63c5478SJustin Iurman if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) {
7279ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
728b63c5478SJustin Iurman } else {
729b63c5478SJustin Iurman queue = skb_get_tx_queue(skb_dst(skb)->dev, skb);
730b63c5478SJustin Iurman qdisc = rcu_dereference(queue->qdisc);
731b63c5478SJustin Iurman qdisc_qstats_qlen_backlog(qdisc, &qlen, &backlog);
732b63c5478SJustin Iurman
733b63c5478SJustin Iurman *(__be32 *)data = cpu_to_be32(backlog);
734b63c5478SJustin Iurman }
7359ee11f0fSJustin Iurman data += sizeof(__be32);
7369ee11f0fSJustin Iurman }
7379ee11f0fSJustin Iurman
7389ee11f0fSJustin Iurman /* checksum complement */
7399ee11f0fSJustin Iurman if (trace->type.bit7) {
7409ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7419ee11f0fSJustin Iurman data += sizeof(__be32);
7429ee11f0fSJustin Iurman }
7439ee11f0fSJustin Iurman
7449ee11f0fSJustin Iurman /* hop_lim and node_id (wide) */
7459ee11f0fSJustin Iurman if (trace->type.bit8) {
7469ee11f0fSJustin Iurman byte = ipv6_hdr(skb)->hop_limit;
74752d03786SJustin Iurman if (is_input)
7489ee11f0fSJustin Iurman byte--;
7499ee11f0fSJustin Iurman
7503edede08SJustin Iurman raw64 = dev_net(skb_dst(skb)->dev)->ipv6.sysctl.ioam6_id_wide;
7519ee11f0fSJustin Iurman
7529ee11f0fSJustin Iurman *(__be64 *)data = cpu_to_be64(((u64)byte << 56) | raw64);
7539ee11f0fSJustin Iurman data += sizeof(__be64);
7549ee11f0fSJustin Iurman }
7559ee11f0fSJustin Iurman
7569ee11f0fSJustin Iurman /* ingress_if_id and egress_if_id (wide) */
7579ee11f0fSJustin Iurman if (trace->type.bit9) {
7589ee11f0fSJustin Iurman if (!skb->dev)
7599ee11f0fSJustin Iurman raw32 = IOAM6_U32_UNAVAILABLE;
7609ee11f0fSJustin Iurman else
7619ee11f0fSJustin Iurman raw32 = __in6_dev_get(skb->dev)->cnf.ioam6_id_wide;
7629ee11f0fSJustin Iurman
7639ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32(raw32);
7649ee11f0fSJustin Iurman data += sizeof(__be32);
7659ee11f0fSJustin Iurman
7669ee11f0fSJustin Iurman if (skb_dst(skb)->dev->flags & IFF_LOOPBACK)
7679ee11f0fSJustin Iurman raw32 = IOAM6_U32_UNAVAILABLE;
7689ee11f0fSJustin Iurman else
7699ee11f0fSJustin Iurman raw32 = __in6_dev_get(skb_dst(skb)->dev)->cnf.ioam6_id_wide;
7709ee11f0fSJustin Iurman
7719ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32(raw32);
7729ee11f0fSJustin Iurman data += sizeof(__be32);
7739ee11f0fSJustin Iurman }
7749ee11f0fSJustin Iurman
7759ee11f0fSJustin Iurman /* namespace data (wide) */
7769ee11f0fSJustin Iurman if (trace->type.bit10) {
7779ee11f0fSJustin Iurman *(__be64 *)data = ns->data_wide;
7789ee11f0fSJustin Iurman data += sizeof(__be64);
7799ee11f0fSJustin Iurman }
7809ee11f0fSJustin Iurman
7819ee11f0fSJustin Iurman /* buffer occupancy */
7829ee11f0fSJustin Iurman if (trace->type.bit11) {
7839ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7849ee11f0fSJustin Iurman data += sizeof(__be32);
7859ee11f0fSJustin Iurman }
7869ee11f0fSJustin Iurman
7872bbc977cSJustin Iurman /* bit12 undefined: filled with empty value */
7882bbc977cSJustin Iurman if (trace->type.bit12) {
7892bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7902bbc977cSJustin Iurman data += sizeof(__be32);
7912bbc977cSJustin Iurman }
7922bbc977cSJustin Iurman
7932bbc977cSJustin Iurman /* bit13 undefined: filled with empty value */
7942bbc977cSJustin Iurman if (trace->type.bit13) {
7952bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7962bbc977cSJustin Iurman data += sizeof(__be32);
7972bbc977cSJustin Iurman }
7982bbc977cSJustin Iurman
7992bbc977cSJustin Iurman /* bit14 undefined: filled with empty value */
8002bbc977cSJustin Iurman if (trace->type.bit14) {
8012bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8022bbc977cSJustin Iurman data += sizeof(__be32);
8032bbc977cSJustin Iurman }
8042bbc977cSJustin Iurman
8052bbc977cSJustin Iurman /* bit15 undefined: filled with empty value */
8062bbc977cSJustin Iurman if (trace->type.bit15) {
8072bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8082bbc977cSJustin Iurman data += sizeof(__be32);
8092bbc977cSJustin Iurman }
8102bbc977cSJustin Iurman
8112bbc977cSJustin Iurman /* bit16 undefined: filled with empty value */
8122bbc977cSJustin Iurman if (trace->type.bit16) {
8132bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8142bbc977cSJustin Iurman data += sizeof(__be32);
8152bbc977cSJustin Iurman }
8162bbc977cSJustin Iurman
8172bbc977cSJustin Iurman /* bit17 undefined: filled with empty value */
8182bbc977cSJustin Iurman if (trace->type.bit17) {
8192bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8202bbc977cSJustin Iurman data += sizeof(__be32);
8212bbc977cSJustin Iurman }
8222bbc977cSJustin Iurman
8232bbc977cSJustin Iurman /* bit18 undefined: filled with empty value */
8242bbc977cSJustin Iurman if (trace->type.bit18) {
8252bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8262bbc977cSJustin Iurman data += sizeof(__be32);
8272bbc977cSJustin Iurman }
8282bbc977cSJustin Iurman
8292bbc977cSJustin Iurman /* bit19 undefined: filled with empty value */
8302bbc977cSJustin Iurman if (trace->type.bit19) {
8312bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8322bbc977cSJustin Iurman data += sizeof(__be32);
8332bbc977cSJustin Iurman }
8342bbc977cSJustin Iurman
8352bbc977cSJustin Iurman /* bit20 undefined: filled with empty value */
8362bbc977cSJustin Iurman if (trace->type.bit20) {
8372bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8382bbc977cSJustin Iurman data += sizeof(__be32);
8392bbc977cSJustin Iurman }
8402bbc977cSJustin Iurman
8412bbc977cSJustin Iurman /* bit21 undefined: filled with empty value */
8422bbc977cSJustin Iurman if (trace->type.bit21) {
8432bbc977cSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8442bbc977cSJustin Iurman data += sizeof(__be32);
8452bbc977cSJustin Iurman }
8462bbc977cSJustin Iurman
8479ee11f0fSJustin Iurman /* opaque state snapshot */
8489ee11f0fSJustin Iurman if (trace->type.bit22) {
8499ee11f0fSJustin Iurman if (!sc) {
8509ee11f0fSJustin Iurman *(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE >> 8);
8519ee11f0fSJustin Iurman } else {
8529ee11f0fSJustin Iurman *(__be32 *)data = sc->hdr;
8539ee11f0fSJustin Iurman data += sizeof(__be32);
8549ee11f0fSJustin Iurman
8559ee11f0fSJustin Iurman memcpy(data, sc->data, sc->len);
8569ee11f0fSJustin Iurman }
8579ee11f0fSJustin Iurman }
8589ee11f0fSJustin Iurman }
8599ee11f0fSJustin Iurman
8609ee11f0fSJustin Iurman /* called with rcu_read_lock() */
ioam6_fill_trace_data(struct sk_buff * skb,struct ioam6_namespace * ns,struct ioam6_trace_hdr * trace,bool is_input)8619ee11f0fSJustin Iurman void ioam6_fill_trace_data(struct sk_buff *skb,
8629ee11f0fSJustin Iurman struct ioam6_namespace *ns,
86352d03786SJustin Iurman struct ioam6_trace_hdr *trace,
86452d03786SJustin Iurman bool is_input)
8659ee11f0fSJustin Iurman {
8669ee11f0fSJustin Iurman struct ioam6_schema *sc;
8679ee11f0fSJustin Iurman u8 sclen = 0;
8689ee11f0fSJustin Iurman
8692bbc977cSJustin Iurman /* Skip if Overflow flag is set
8709ee11f0fSJustin Iurman */
8712bbc977cSJustin Iurman if (trace->overflow)
8729ee11f0fSJustin Iurman return;
8739ee11f0fSJustin Iurman
8749ee11f0fSJustin Iurman /* NodeLen does not include Opaque State Snapshot length. We need to
8759ee11f0fSJustin Iurman * take it into account if the corresponding bit is set (bit 22) and
8769ee11f0fSJustin Iurman * if the current IOAM namespace has an active schema attached to it
8779ee11f0fSJustin Iurman */
8789ee11f0fSJustin Iurman sc = rcu_dereference(ns->schema);
8799ee11f0fSJustin Iurman if (trace->type.bit22) {
8809ee11f0fSJustin Iurman sclen = sizeof_field(struct ioam6_schema, hdr) / 4;
8819ee11f0fSJustin Iurman
8829ee11f0fSJustin Iurman if (sc)
8839ee11f0fSJustin Iurman sclen += sc->len / 4;
8849ee11f0fSJustin Iurman }
8859ee11f0fSJustin Iurman
8869ee11f0fSJustin Iurman /* If there is no space remaining, we set the Overflow flag and we
8879ee11f0fSJustin Iurman * skip without filling the trace
8889ee11f0fSJustin Iurman */
8899ee11f0fSJustin Iurman if (!trace->remlen || trace->remlen < trace->nodelen + sclen) {
8909ee11f0fSJustin Iurman trace->overflow = 1;
8919ee11f0fSJustin Iurman return;
8929ee11f0fSJustin Iurman }
8939ee11f0fSJustin Iurman
89452d03786SJustin Iurman __ioam6_fill_trace_data(skb, ns, trace, sc, sclen, is_input);
8959ee11f0fSJustin Iurman trace->remlen -= trace->nodelen + sclen;
8969ee11f0fSJustin Iurman }
8979ee11f0fSJustin Iurman
ioam6_net_init(struct net * net)8989ee11f0fSJustin Iurman static int __net_init ioam6_net_init(struct net *net)
8999ee11f0fSJustin Iurman {
9009ee11f0fSJustin Iurman struct ioam6_pernet_data *nsdata;
9019ee11f0fSJustin Iurman int err = -ENOMEM;
9029ee11f0fSJustin Iurman
9039ee11f0fSJustin Iurman nsdata = kzalloc(sizeof(*nsdata), GFP_KERNEL);
9049ee11f0fSJustin Iurman if (!nsdata)
9059ee11f0fSJustin Iurman goto out;
9069ee11f0fSJustin Iurman
9079ee11f0fSJustin Iurman mutex_init(&nsdata->lock);
9089ee11f0fSJustin Iurman net->ipv6.ioam6_data = nsdata;
9099ee11f0fSJustin Iurman
9109ee11f0fSJustin Iurman err = rhashtable_init(&nsdata->namespaces, &rht_ns_params);
9119ee11f0fSJustin Iurman if (err)
9129ee11f0fSJustin Iurman goto free_nsdata;
9139ee11f0fSJustin Iurman
9149ee11f0fSJustin Iurman err = rhashtable_init(&nsdata->schemas, &rht_sc_params);
9159ee11f0fSJustin Iurman if (err)
9169ee11f0fSJustin Iurman goto free_rht_ns;
9179ee11f0fSJustin Iurman
9189ee11f0fSJustin Iurman out:
9199ee11f0fSJustin Iurman return err;
9209ee11f0fSJustin Iurman free_rht_ns:
9219ee11f0fSJustin Iurman rhashtable_destroy(&nsdata->namespaces);
9229ee11f0fSJustin Iurman free_nsdata:
9239ee11f0fSJustin Iurman kfree(nsdata);
9249ee11f0fSJustin Iurman net->ipv6.ioam6_data = NULL;
9259ee11f0fSJustin Iurman goto out;
9269ee11f0fSJustin Iurman }
9279ee11f0fSJustin Iurman
ioam6_net_exit(struct net * net)9289ee11f0fSJustin Iurman static void __net_exit ioam6_net_exit(struct net *net)
9299ee11f0fSJustin Iurman {
9309ee11f0fSJustin Iurman struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
9319ee11f0fSJustin Iurman
9329ee11f0fSJustin Iurman rhashtable_free_and_destroy(&nsdata->namespaces, ioam6_free_ns, NULL);
9339ee11f0fSJustin Iurman rhashtable_free_and_destroy(&nsdata->schemas, ioam6_free_sc, NULL);
9349ee11f0fSJustin Iurman
9359ee11f0fSJustin Iurman kfree(nsdata);
9369ee11f0fSJustin Iurman }
9379ee11f0fSJustin Iurman
9389ee11f0fSJustin Iurman static struct pernet_operations ioam6_net_ops = {
9399ee11f0fSJustin Iurman .init = ioam6_net_init,
9409ee11f0fSJustin Iurman .exit = ioam6_net_exit,
9419ee11f0fSJustin Iurman };
9429ee11f0fSJustin Iurman
ioam6_init(void)9439ee11f0fSJustin Iurman int __init ioam6_init(void)
9449ee11f0fSJustin Iurman {
9459ee11f0fSJustin Iurman int err = register_pernet_subsys(&ioam6_net_ops);
9469ee11f0fSJustin Iurman if (err)
9478c6f6fa6SJustin Iurman goto out;
9488c6f6fa6SJustin Iurman
9498c6f6fa6SJustin Iurman err = genl_register_family(&ioam6_genl_family);
9508c6f6fa6SJustin Iurman if (err)
9518c6f6fa6SJustin Iurman goto out_unregister_pernet_subsys;
9529ee11f0fSJustin Iurman
9533edede08SJustin Iurman #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
9543edede08SJustin Iurman err = ioam6_iptunnel_init();
9553edede08SJustin Iurman if (err)
9563edede08SJustin Iurman goto out_unregister_genl;
9573edede08SJustin Iurman #endif
9583edede08SJustin Iurman
9599ee11f0fSJustin Iurman pr_info("In-situ OAM (IOAM) with IPv6\n");
9608c6f6fa6SJustin Iurman
9618c6f6fa6SJustin Iurman out:
9628c6f6fa6SJustin Iurman return err;
9633edede08SJustin Iurman #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
9643edede08SJustin Iurman out_unregister_genl:
9653edede08SJustin Iurman genl_unregister_family(&ioam6_genl_family);
9663edede08SJustin Iurman #endif
9678c6f6fa6SJustin Iurman out_unregister_pernet_subsys:
9688c6f6fa6SJustin Iurman unregister_pernet_subsys(&ioam6_net_ops);
9698c6f6fa6SJustin Iurman goto out;
9709ee11f0fSJustin Iurman }
9719ee11f0fSJustin Iurman
ioam6_exit(void)9729ee11f0fSJustin Iurman void ioam6_exit(void)
9739ee11f0fSJustin Iurman {
9743edede08SJustin Iurman #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
9753edede08SJustin Iurman ioam6_iptunnel_exit();
9763edede08SJustin Iurman #endif
9778c6f6fa6SJustin Iurman genl_unregister_family(&ioam6_genl_family);
9789ee11f0fSJustin Iurman unregister_pernet_subsys(&ioam6_net_ops);
9799ee11f0fSJustin Iurman }
980