xref: /linux/net/ipv6/ioam6.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
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 
138a885a6b2SJohannes Berg 	data32 = nla_get_u32_default(info->attrs[IOAM6_ATTR_NS_DATA],
139a885a6b2SJohannes Berg 				     IOAM6_U32_UNAVAILABLE);
1408c6f6fa6SJustin Iurman 
141a885a6b2SJohannes Berg 	data64 = nla_get_u64_default(info->attrs[IOAM6_ATTR_NS_DATA_WIDE],
142a885a6b2SJohannes Berg 				     IOAM6_U64_UNAVAILABLE);
1438c6f6fa6SJustin Iurman 
1448c6f6fa6SJustin Iurman 	ns->data = cpu_to_be32(data32);
1458c6f6fa6SJustin Iurman 	ns->data_wide = cpu_to_be64(data64);
1468c6f6fa6SJustin Iurman 
1478c6f6fa6SJustin Iurman 	err = rhashtable_lookup_insert_fast(&nsdata->namespaces, &ns->head,
1488c6f6fa6SJustin Iurman 					    rht_ns_params);
1498c6f6fa6SJustin Iurman 	if (err)
1508c6f6fa6SJustin Iurman 		kfree(ns);
1518c6f6fa6SJustin Iurman 
1528c6f6fa6SJustin Iurman out_unlock:
1538c6f6fa6SJustin Iurman 	mutex_unlock(&nsdata->lock);
1548c6f6fa6SJustin Iurman 	return err;
1558c6f6fa6SJustin Iurman }
1568c6f6fa6SJustin Iurman 
ioam6_genl_delns(struct sk_buff * skb,struct genl_info * info)1578c6f6fa6SJustin Iurman static int ioam6_genl_delns(struct sk_buff *skb, struct genl_info *info)
1588c6f6fa6SJustin Iurman {
1598c6f6fa6SJustin Iurman 	struct ioam6_pernet_data *nsdata;
1608c6f6fa6SJustin Iurman 	struct ioam6_namespace *ns;
1618c6f6fa6SJustin Iurman 	struct ioam6_schema *sc;
1628c6f6fa6SJustin Iurman 	__be16 id;
1638c6f6fa6SJustin Iurman 	int err;
1648c6f6fa6SJustin Iurman 
1658c6f6fa6SJustin Iurman 	if (!info->attrs[IOAM6_ATTR_NS_ID])
1668c6f6fa6SJustin Iurman 		return -EINVAL;
1678c6f6fa6SJustin Iurman 
1688c6f6fa6SJustin Iurman 	id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
1698c6f6fa6SJustin Iurman 	nsdata = ioam6_pernet(genl_info_net(info));
1708c6f6fa6SJustin Iurman 
1718c6f6fa6SJustin Iurman 	mutex_lock(&nsdata->lock);
1728c6f6fa6SJustin Iurman 
1738c6f6fa6SJustin Iurman 	ns = rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
1748c6f6fa6SJustin Iurman 	if (!ns) {
1758c6f6fa6SJustin Iurman 		err = -ENOENT;
1768c6f6fa6SJustin Iurman 		goto out_unlock;
1778c6f6fa6SJustin Iurman 	}
1788c6f6fa6SJustin Iurman 
1798c6f6fa6SJustin Iurman 	sc = rcu_dereference_protected(ns->schema,
1808c6f6fa6SJustin Iurman 				       lockdep_is_held(&nsdata->lock));
1818c6f6fa6SJustin Iurman 
1828c6f6fa6SJustin Iurman 	err = rhashtable_remove_fast(&nsdata->namespaces, &ns->head,
1838c6f6fa6SJustin Iurman 				     rht_ns_params);
1848c6f6fa6SJustin Iurman 	if (err)
1858c6f6fa6SJustin Iurman 		goto out_unlock;
1868c6f6fa6SJustin Iurman 
1878c6f6fa6SJustin Iurman 	if (sc)
1888c6f6fa6SJustin Iurman 		rcu_assign_pointer(sc->ns, NULL);
1898c6f6fa6SJustin Iurman 
1908c6f6fa6SJustin Iurman 	ioam6_ns_release(ns);
1918c6f6fa6SJustin Iurman 
1928c6f6fa6SJustin Iurman out_unlock:
1938c6f6fa6SJustin Iurman 	mutex_unlock(&nsdata->lock);
1948c6f6fa6SJustin Iurman 	return err;
1958c6f6fa6SJustin Iurman }
1968c6f6fa6SJustin Iurman 
__ioam6_genl_dumpns_element(struct ioam6_namespace * ns,u32 portid,u32 seq,u32 flags,struct sk_buff * skb,u8 cmd)1978c6f6fa6SJustin Iurman static int __ioam6_genl_dumpns_element(struct ioam6_namespace *ns,
1988c6f6fa6SJustin Iurman 				       u32 portid,
1998c6f6fa6SJustin Iurman 				       u32 seq,
2008c6f6fa6SJustin Iurman 				       u32 flags,
2018c6f6fa6SJustin Iurman 				       struct sk_buff *skb,
2028c6f6fa6SJustin Iurman 				       u8 cmd)
2038c6f6fa6SJustin Iurman {
2048c6f6fa6SJustin Iurman 	struct ioam6_schema *sc;
2058c6f6fa6SJustin Iurman 	u64 data64;
2068c6f6fa6SJustin Iurman 	u32 data32;
2078c6f6fa6SJustin Iurman 	void *hdr;
2088c6f6fa6SJustin Iurman 
2098c6f6fa6SJustin Iurman 	hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd);
2108c6f6fa6SJustin Iurman 	if (!hdr)
2118c6f6fa6SJustin Iurman 		return -ENOMEM;
2128c6f6fa6SJustin Iurman 
2138c6f6fa6SJustin Iurman 	data32 = be32_to_cpu(ns->data);
2148c6f6fa6SJustin Iurman 	data64 = be64_to_cpu(ns->data_wide);
2158c6f6fa6SJustin Iurman 
2168c6f6fa6SJustin Iurman 	if (nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id)) ||
2178c6f6fa6SJustin Iurman 	    (data32 != IOAM6_U32_UNAVAILABLE &&
2188c6f6fa6SJustin Iurman 	     nla_put_u32(skb, IOAM6_ATTR_NS_DATA, data32)) ||
2198c6f6fa6SJustin Iurman 	    (data64 != IOAM6_U64_UNAVAILABLE &&
2208c6f6fa6SJustin Iurman 	     nla_put_u64_64bit(skb, IOAM6_ATTR_NS_DATA_WIDE,
2218c6f6fa6SJustin Iurman 			       data64, IOAM6_ATTR_PAD)))
2228c6f6fa6SJustin Iurman 		goto nla_put_failure;
2238c6f6fa6SJustin Iurman 
2248c6f6fa6SJustin Iurman 	rcu_read_lock();
2258c6f6fa6SJustin Iurman 
2268c6f6fa6SJustin Iurman 	sc = rcu_dereference(ns->schema);
2278c6f6fa6SJustin Iurman 	if (sc && nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id)) {
2288c6f6fa6SJustin Iurman 		rcu_read_unlock();
2298c6f6fa6SJustin Iurman 		goto nla_put_failure;
2308c6f6fa6SJustin Iurman 	}
2318c6f6fa6SJustin Iurman 
2328c6f6fa6SJustin Iurman 	rcu_read_unlock();
2338c6f6fa6SJustin Iurman 
2348c6f6fa6SJustin Iurman 	genlmsg_end(skb, hdr);
2358c6f6fa6SJustin Iurman 	return 0;
2368c6f6fa6SJustin Iurman 
2378c6f6fa6SJustin Iurman nla_put_failure:
2388c6f6fa6SJustin Iurman 	genlmsg_cancel(skb, hdr);
2398c6f6fa6SJustin Iurman 	return -EMSGSIZE;
2408c6f6fa6SJustin Iurman }
2418c6f6fa6SJustin Iurman 
ioam6_genl_dumpns_start(struct netlink_callback * cb)2428c6f6fa6SJustin Iurman static int ioam6_genl_dumpns_start(struct netlink_callback *cb)
2438c6f6fa6SJustin Iurman {
2448c6f6fa6SJustin Iurman 	struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk));
2458c6f6fa6SJustin Iurman 	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
2468c6f6fa6SJustin Iurman 
2478c6f6fa6SJustin Iurman 	if (!iter) {
2488c6f6fa6SJustin Iurman 		iter = kmalloc(sizeof(*iter), GFP_KERNEL);
2498c6f6fa6SJustin Iurman 		if (!iter)
2508c6f6fa6SJustin Iurman 			return -ENOMEM;
2518c6f6fa6SJustin Iurman 
2528c6f6fa6SJustin Iurman 		cb->args[0] = (long)iter;
2538c6f6fa6SJustin Iurman 	}
2548c6f6fa6SJustin Iurman 
2558c6f6fa6SJustin Iurman 	rhashtable_walk_enter(&nsdata->namespaces, iter);
2568c6f6fa6SJustin Iurman 
2578c6f6fa6SJustin Iurman 	return 0;
2588c6f6fa6SJustin Iurman }
2598c6f6fa6SJustin Iurman 
ioam6_genl_dumpns_done(struct netlink_callback * cb)2608c6f6fa6SJustin Iurman static int ioam6_genl_dumpns_done(struct netlink_callback *cb)
2618c6f6fa6SJustin Iurman {
2628c6f6fa6SJustin Iurman 	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
2638c6f6fa6SJustin Iurman 
2648c6f6fa6SJustin Iurman 	rhashtable_walk_exit(iter);
2658c6f6fa6SJustin Iurman 	kfree(iter);
2668c6f6fa6SJustin Iurman 
2678c6f6fa6SJustin Iurman 	return 0;
2688c6f6fa6SJustin Iurman }
2698c6f6fa6SJustin Iurman 
ioam6_genl_dumpns(struct sk_buff * skb,struct netlink_callback * cb)2708c6f6fa6SJustin Iurman static int ioam6_genl_dumpns(struct sk_buff *skb, struct netlink_callback *cb)
2718c6f6fa6SJustin Iurman {
2728c6f6fa6SJustin Iurman 	struct rhashtable_iter *iter;
2738c6f6fa6SJustin Iurman 	struct ioam6_namespace *ns;
2748c6f6fa6SJustin Iurman 	int err;
2758c6f6fa6SJustin Iurman 
2768c6f6fa6SJustin Iurman 	iter = (struct rhashtable_iter *)cb->args[0];
2778c6f6fa6SJustin Iurman 	rhashtable_walk_start(iter);
2788c6f6fa6SJustin Iurman 
2798c6f6fa6SJustin Iurman 	for (;;) {
2808c6f6fa6SJustin Iurman 		ns = rhashtable_walk_next(iter);
2818c6f6fa6SJustin Iurman 
2828c6f6fa6SJustin Iurman 		if (IS_ERR(ns)) {
2838c6f6fa6SJustin Iurman 			if (PTR_ERR(ns) == -EAGAIN)
2848c6f6fa6SJustin Iurman 				continue;
2858c6f6fa6SJustin Iurman 			err = PTR_ERR(ns);
2868c6f6fa6SJustin Iurman 			goto done;
2878c6f6fa6SJustin Iurman 		} else if (!ns) {
2888c6f6fa6SJustin Iurman 			break;
2898c6f6fa6SJustin Iurman 		}
2908c6f6fa6SJustin Iurman 
2918c6f6fa6SJustin Iurman 		err = __ioam6_genl_dumpns_element(ns,
2928c6f6fa6SJustin Iurman 						  NETLINK_CB(cb->skb).portid,
2938c6f6fa6SJustin Iurman 						  cb->nlh->nlmsg_seq,
2948c6f6fa6SJustin Iurman 						  NLM_F_MULTI,
2958c6f6fa6SJustin Iurman 						  skb,
2968c6f6fa6SJustin Iurman 						  IOAM6_CMD_DUMP_NAMESPACES);
2978c6f6fa6SJustin Iurman 		if (err)
2988c6f6fa6SJustin Iurman 			goto done;
2998c6f6fa6SJustin Iurman 	}
3008c6f6fa6SJustin Iurman 
3018c6f6fa6SJustin Iurman 	err = skb->len;
3028c6f6fa6SJustin Iurman 
3038c6f6fa6SJustin Iurman done:
3048c6f6fa6SJustin Iurman 	rhashtable_walk_stop(iter);
3058c6f6fa6SJustin Iurman 	return err;
3068c6f6fa6SJustin Iurman }
3078c6f6fa6SJustin Iurman 
ioam6_genl_addsc(struct sk_buff * skb,struct genl_info * info)3088c6f6fa6SJustin Iurman static int ioam6_genl_addsc(struct sk_buff *skb, struct genl_info *info)
3098c6f6fa6SJustin Iurman {
3108c6f6fa6SJustin Iurman 	struct ioam6_pernet_data *nsdata;
3118c6f6fa6SJustin Iurman 	int len, len_aligned, err;
3128c6f6fa6SJustin Iurman 	struct ioam6_schema *sc;
3138c6f6fa6SJustin Iurman 	u32 id;
3148c6f6fa6SJustin Iurman 
3158c6f6fa6SJustin Iurman 	if (!info->attrs[IOAM6_ATTR_SC_ID] || !info->attrs[IOAM6_ATTR_SC_DATA])
3168c6f6fa6SJustin Iurman 		return -EINVAL;
3178c6f6fa6SJustin Iurman 
3188c6f6fa6SJustin Iurman 	id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
3198c6f6fa6SJustin Iurman 	nsdata = ioam6_pernet(genl_info_net(info));
3208c6f6fa6SJustin Iurman 
3218c6f6fa6SJustin Iurman 	mutex_lock(&nsdata->lock);
3228c6f6fa6SJustin Iurman 
3238c6f6fa6SJustin Iurman 	sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params);
3248c6f6fa6SJustin Iurman 	if (sc) {
3258c6f6fa6SJustin Iurman 		err = -EEXIST;
3268c6f6fa6SJustin Iurman 		goto out_unlock;
3278c6f6fa6SJustin Iurman 	}
3288c6f6fa6SJustin Iurman 
3298c6f6fa6SJustin Iurman 	len = nla_len(info->attrs[IOAM6_ATTR_SC_DATA]);
3308c6f6fa6SJustin Iurman 	len_aligned = ALIGN(len, 4);
3318c6f6fa6SJustin Iurman 
3328c6f6fa6SJustin Iurman 	sc = kzalloc(sizeof(*sc) + len_aligned, GFP_KERNEL);
3338c6f6fa6SJustin Iurman 	if (!sc) {
3348c6f6fa6SJustin Iurman 		err = -ENOMEM;
3358c6f6fa6SJustin Iurman 		goto out_unlock;
3368c6f6fa6SJustin Iurman 	}
3378c6f6fa6SJustin Iurman 
3388c6f6fa6SJustin Iurman 	sc->id = id;
3398c6f6fa6SJustin Iurman 	sc->len = len_aligned;
3408c6f6fa6SJustin Iurman 	sc->hdr = cpu_to_be32(sc->id | ((u8)(sc->len / 4) << 24));
3418c6f6fa6SJustin Iurman 	nla_memcpy(sc->data, info->attrs[IOAM6_ATTR_SC_DATA], len);
3428c6f6fa6SJustin Iurman 
3438c6f6fa6SJustin Iurman 	err = rhashtable_lookup_insert_fast(&nsdata->schemas, &sc->head,
3448c6f6fa6SJustin Iurman 					    rht_sc_params);
3458c6f6fa6SJustin Iurman 	if (err)
3468c6f6fa6SJustin Iurman 		goto free_sc;
3478c6f6fa6SJustin Iurman 
3488c6f6fa6SJustin Iurman out_unlock:
3498c6f6fa6SJustin Iurman 	mutex_unlock(&nsdata->lock);
3508c6f6fa6SJustin Iurman 	return err;
3518c6f6fa6SJustin Iurman free_sc:
3528c6f6fa6SJustin Iurman 	kfree(sc);
3538c6f6fa6SJustin Iurman 	goto out_unlock;
3548c6f6fa6SJustin Iurman }
3558c6f6fa6SJustin Iurman 
ioam6_genl_delsc(struct sk_buff * skb,struct genl_info * info)3568c6f6fa6SJustin Iurman static int ioam6_genl_delsc(struct sk_buff *skb, struct genl_info *info)
3578c6f6fa6SJustin Iurman {
3588c6f6fa6SJustin Iurman 	struct ioam6_pernet_data *nsdata;
3598c6f6fa6SJustin Iurman 	struct ioam6_namespace *ns;
3608c6f6fa6SJustin Iurman 	struct ioam6_schema *sc;
3618c6f6fa6SJustin Iurman 	int err;
3628c6f6fa6SJustin Iurman 	u32 id;
3638c6f6fa6SJustin Iurman 
3648c6f6fa6SJustin Iurman 	if (!info->attrs[IOAM6_ATTR_SC_ID])
3658c6f6fa6SJustin Iurman 		return -EINVAL;
3668c6f6fa6SJustin Iurman 
3678c6f6fa6SJustin Iurman 	id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
3688c6f6fa6SJustin Iurman 	nsdata = ioam6_pernet(genl_info_net(info));
3698c6f6fa6SJustin Iurman 
3708c6f6fa6SJustin Iurman 	mutex_lock(&nsdata->lock);
3718c6f6fa6SJustin Iurman 
3728c6f6fa6SJustin Iurman 	sc = rhashtable_lookup_fast(&nsdata->schemas, &id, rht_sc_params);
3738c6f6fa6SJustin Iurman 	if (!sc) {
3748c6f6fa6SJustin Iurman 		err = -ENOENT;
3758c6f6fa6SJustin Iurman 		goto out_unlock;
3768c6f6fa6SJustin Iurman 	}
3778c6f6fa6SJustin Iurman 
3788c6f6fa6SJustin Iurman 	ns = rcu_dereference_protected(sc->ns, lockdep_is_held(&nsdata->lock));
3798c6f6fa6SJustin Iurman 
3808c6f6fa6SJustin Iurman 	err = rhashtable_remove_fast(&nsdata->schemas, &sc->head,
3818c6f6fa6SJustin Iurman 				     rht_sc_params);
3828c6f6fa6SJustin Iurman 	if (err)
3838c6f6fa6SJustin Iurman 		goto out_unlock;
3848c6f6fa6SJustin Iurman 
3858c6f6fa6SJustin Iurman 	if (ns)
3868c6f6fa6SJustin Iurman 		rcu_assign_pointer(ns->schema, NULL);
3878c6f6fa6SJustin Iurman 
3888c6f6fa6SJustin Iurman 	ioam6_sc_release(sc);
3898c6f6fa6SJustin Iurman 
3908c6f6fa6SJustin Iurman out_unlock:
3918c6f6fa6SJustin Iurman 	mutex_unlock(&nsdata->lock);
3928c6f6fa6SJustin Iurman 	return err;
3938c6f6fa6SJustin Iurman }
3948c6f6fa6SJustin Iurman 
__ioam6_genl_dumpsc_element(struct ioam6_schema * sc,u32 portid,u32 seq,u32 flags,struct sk_buff * skb,u8 cmd)3958c6f6fa6SJustin Iurman static int __ioam6_genl_dumpsc_element(struct ioam6_schema *sc,
3968c6f6fa6SJustin Iurman 				       u32 portid, u32 seq, u32 flags,
3978c6f6fa6SJustin Iurman 				       struct sk_buff *skb, u8 cmd)
3988c6f6fa6SJustin Iurman {
3998c6f6fa6SJustin Iurman 	struct ioam6_namespace *ns;
4008c6f6fa6SJustin Iurman 	void *hdr;
4018c6f6fa6SJustin Iurman 
4028c6f6fa6SJustin Iurman 	hdr = genlmsg_put(skb, portid, seq, &ioam6_genl_family, flags, cmd);
4038c6f6fa6SJustin Iurman 	if (!hdr)
4048c6f6fa6SJustin Iurman 		return -ENOMEM;
4058c6f6fa6SJustin Iurman 
4068c6f6fa6SJustin Iurman 	if (nla_put_u32(skb, IOAM6_ATTR_SC_ID, sc->id) ||
4078c6f6fa6SJustin Iurman 	    nla_put(skb, IOAM6_ATTR_SC_DATA, sc->len, sc->data))
4088c6f6fa6SJustin Iurman 		goto nla_put_failure;
4098c6f6fa6SJustin Iurman 
4108c6f6fa6SJustin Iurman 	rcu_read_lock();
4118c6f6fa6SJustin Iurman 
4128c6f6fa6SJustin Iurman 	ns = rcu_dereference(sc->ns);
4138c6f6fa6SJustin Iurman 	if (ns && nla_put_u16(skb, IOAM6_ATTR_NS_ID, be16_to_cpu(ns->id))) {
4148c6f6fa6SJustin Iurman 		rcu_read_unlock();
4158c6f6fa6SJustin Iurman 		goto nla_put_failure;
4168c6f6fa6SJustin Iurman 	}
4178c6f6fa6SJustin Iurman 
4188c6f6fa6SJustin Iurman 	rcu_read_unlock();
4198c6f6fa6SJustin Iurman 
4208c6f6fa6SJustin Iurman 	genlmsg_end(skb, hdr);
4218c6f6fa6SJustin Iurman 	return 0;
4228c6f6fa6SJustin Iurman 
4238c6f6fa6SJustin Iurman nla_put_failure:
4248c6f6fa6SJustin Iurman 	genlmsg_cancel(skb, hdr);
4258c6f6fa6SJustin Iurman 	return -EMSGSIZE;
4268c6f6fa6SJustin Iurman }
4278c6f6fa6SJustin Iurman 
ioam6_genl_dumpsc_start(struct netlink_callback * cb)4288c6f6fa6SJustin Iurman static int ioam6_genl_dumpsc_start(struct netlink_callback *cb)
4298c6f6fa6SJustin Iurman {
4308c6f6fa6SJustin Iurman 	struct ioam6_pernet_data *nsdata = ioam6_pernet(sock_net(cb->skb->sk));
4318c6f6fa6SJustin Iurman 	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
4328c6f6fa6SJustin Iurman 
4338c6f6fa6SJustin Iurman 	if (!iter) {
4348c6f6fa6SJustin Iurman 		iter = kmalloc(sizeof(*iter), GFP_KERNEL);
4358c6f6fa6SJustin Iurman 		if (!iter)
4368c6f6fa6SJustin Iurman 			return -ENOMEM;
4378c6f6fa6SJustin Iurman 
4388c6f6fa6SJustin Iurman 		cb->args[0] = (long)iter;
4398c6f6fa6SJustin Iurman 	}
4408c6f6fa6SJustin Iurman 
4418c6f6fa6SJustin Iurman 	rhashtable_walk_enter(&nsdata->schemas, iter);
4428c6f6fa6SJustin Iurman 
4438c6f6fa6SJustin Iurman 	return 0;
4448c6f6fa6SJustin Iurman }
4458c6f6fa6SJustin Iurman 
ioam6_genl_dumpsc_done(struct netlink_callback * cb)4468c6f6fa6SJustin Iurman static int ioam6_genl_dumpsc_done(struct netlink_callback *cb)
4478c6f6fa6SJustin Iurman {
4488c6f6fa6SJustin Iurman 	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
4498c6f6fa6SJustin Iurman 
4508c6f6fa6SJustin Iurman 	rhashtable_walk_exit(iter);
4518c6f6fa6SJustin Iurman 	kfree(iter);
4528c6f6fa6SJustin Iurman 
4538c6f6fa6SJustin Iurman 	return 0;
4548c6f6fa6SJustin Iurman }
4558c6f6fa6SJustin Iurman 
ioam6_genl_dumpsc(struct sk_buff * skb,struct netlink_callback * cb)4568c6f6fa6SJustin Iurman static int ioam6_genl_dumpsc(struct sk_buff *skb, struct netlink_callback *cb)
4578c6f6fa6SJustin Iurman {
4588c6f6fa6SJustin Iurman 	struct rhashtable_iter *iter;
4598c6f6fa6SJustin Iurman 	struct ioam6_schema *sc;
4608c6f6fa6SJustin Iurman 	int err;
4618c6f6fa6SJustin Iurman 
4628c6f6fa6SJustin Iurman 	iter = (struct rhashtable_iter *)cb->args[0];
4638c6f6fa6SJustin Iurman 	rhashtable_walk_start(iter);
4648c6f6fa6SJustin Iurman 
4658c6f6fa6SJustin Iurman 	for (;;) {
4668c6f6fa6SJustin Iurman 		sc = rhashtable_walk_next(iter);
4678c6f6fa6SJustin Iurman 
4688c6f6fa6SJustin Iurman 		if (IS_ERR(sc)) {
4698c6f6fa6SJustin Iurman 			if (PTR_ERR(sc) == -EAGAIN)
4708c6f6fa6SJustin Iurman 				continue;
4718c6f6fa6SJustin Iurman 			err = PTR_ERR(sc);
4728c6f6fa6SJustin Iurman 			goto done;
4738c6f6fa6SJustin Iurman 		} else if (!sc) {
4748c6f6fa6SJustin Iurman 			break;
4758c6f6fa6SJustin Iurman 		}
4768c6f6fa6SJustin Iurman 
4778c6f6fa6SJustin Iurman 		err = __ioam6_genl_dumpsc_element(sc,
4788c6f6fa6SJustin Iurman 						  NETLINK_CB(cb->skb).portid,
4798c6f6fa6SJustin Iurman 						  cb->nlh->nlmsg_seq,
4808c6f6fa6SJustin Iurman 						  NLM_F_MULTI,
4818c6f6fa6SJustin Iurman 						  skb,
4828c6f6fa6SJustin Iurman 						  IOAM6_CMD_DUMP_SCHEMAS);
4838c6f6fa6SJustin Iurman 		if (err)
4848c6f6fa6SJustin Iurman 			goto done;
4858c6f6fa6SJustin Iurman 	}
4868c6f6fa6SJustin Iurman 
4878c6f6fa6SJustin Iurman 	err = skb->len;
4888c6f6fa6SJustin Iurman 
4898c6f6fa6SJustin Iurman done:
4908c6f6fa6SJustin Iurman 	rhashtable_walk_stop(iter);
4918c6f6fa6SJustin Iurman 	return err;
4928c6f6fa6SJustin Iurman }
4938c6f6fa6SJustin Iurman 
ioam6_genl_ns_set_schema(struct sk_buff * skb,struct genl_info * info)4948c6f6fa6SJustin Iurman static int ioam6_genl_ns_set_schema(struct sk_buff *skb, struct genl_info *info)
4958c6f6fa6SJustin Iurman {
4968c6f6fa6SJustin Iurman 	struct ioam6_namespace *ns, *ns_ref;
4978c6f6fa6SJustin Iurman 	struct ioam6_schema *sc, *sc_ref;
4988c6f6fa6SJustin Iurman 	struct ioam6_pernet_data *nsdata;
4998c6f6fa6SJustin Iurman 	__be16 ns_id;
5008c6f6fa6SJustin Iurman 	u32 sc_id;
5018c6f6fa6SJustin Iurman 	int err;
5028c6f6fa6SJustin Iurman 
5038c6f6fa6SJustin Iurman 	if (!info->attrs[IOAM6_ATTR_NS_ID] ||
5048c6f6fa6SJustin Iurman 	    (!info->attrs[IOAM6_ATTR_SC_ID] &&
5058c6f6fa6SJustin Iurman 	     !info->attrs[IOAM6_ATTR_SC_NONE]))
5068c6f6fa6SJustin Iurman 		return -EINVAL;
5078c6f6fa6SJustin Iurman 
5088c6f6fa6SJustin Iurman 	ns_id = cpu_to_be16(nla_get_u16(info->attrs[IOAM6_ATTR_NS_ID]));
5098c6f6fa6SJustin Iurman 	nsdata = ioam6_pernet(genl_info_net(info));
5108c6f6fa6SJustin Iurman 
5118c6f6fa6SJustin Iurman 	mutex_lock(&nsdata->lock);
5128c6f6fa6SJustin Iurman 
5138c6f6fa6SJustin Iurman 	ns = rhashtable_lookup_fast(&nsdata->namespaces, &ns_id, rht_ns_params);
5148c6f6fa6SJustin Iurman 	if (!ns) {
5158c6f6fa6SJustin Iurman 		err = -ENOENT;
5168c6f6fa6SJustin Iurman 		goto out_unlock;
5178c6f6fa6SJustin Iurman 	}
5188c6f6fa6SJustin Iurman 
5198c6f6fa6SJustin Iurman 	if (info->attrs[IOAM6_ATTR_SC_NONE]) {
5208c6f6fa6SJustin Iurman 		sc = NULL;
5218c6f6fa6SJustin Iurman 	} else {
5228c6f6fa6SJustin Iurman 		sc_id = nla_get_u32(info->attrs[IOAM6_ATTR_SC_ID]);
5238c6f6fa6SJustin Iurman 		sc = rhashtable_lookup_fast(&nsdata->schemas, &sc_id,
5248c6f6fa6SJustin Iurman 					    rht_sc_params);
5258c6f6fa6SJustin Iurman 		if (!sc) {
5268c6f6fa6SJustin Iurman 			err = -ENOENT;
5278c6f6fa6SJustin Iurman 			goto out_unlock;
5288c6f6fa6SJustin Iurman 		}
5298c6f6fa6SJustin Iurman 	}
5308c6f6fa6SJustin Iurman 
5318c6f6fa6SJustin Iurman 	sc_ref = rcu_dereference_protected(ns->schema,
5328c6f6fa6SJustin Iurman 					   lockdep_is_held(&nsdata->lock));
5338c6f6fa6SJustin Iurman 	if (sc_ref)
5348c6f6fa6SJustin Iurman 		rcu_assign_pointer(sc_ref->ns, NULL);
5358c6f6fa6SJustin Iurman 	rcu_assign_pointer(ns->schema, sc);
5368c6f6fa6SJustin Iurman 
5378c6f6fa6SJustin Iurman 	if (sc) {
5388c6f6fa6SJustin Iurman 		ns_ref = rcu_dereference_protected(sc->ns,
5398c6f6fa6SJustin Iurman 						   lockdep_is_held(&nsdata->lock));
5408c6f6fa6SJustin Iurman 		if (ns_ref)
5418c6f6fa6SJustin Iurman 			rcu_assign_pointer(ns_ref->schema, NULL);
5428c6f6fa6SJustin Iurman 		rcu_assign_pointer(sc->ns, ns);
5438c6f6fa6SJustin Iurman 	}
5448c6f6fa6SJustin Iurman 
5458c6f6fa6SJustin Iurman 	err = 0;
5468c6f6fa6SJustin Iurman 
5478c6f6fa6SJustin Iurman out_unlock:
5488c6f6fa6SJustin Iurman 	mutex_unlock(&nsdata->lock);
5498c6f6fa6SJustin Iurman 	return err;
5508c6f6fa6SJustin Iurman }
5518c6f6fa6SJustin Iurman 
5528c6f6fa6SJustin Iurman static const struct genl_ops ioam6_genl_ops[] = {
5538c6f6fa6SJustin Iurman 	{
5548c6f6fa6SJustin Iurman 		.cmd	= IOAM6_CMD_ADD_NAMESPACE,
5558c6f6fa6SJustin Iurman 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5568c6f6fa6SJustin Iurman 		.doit	= ioam6_genl_addns,
5578c6f6fa6SJustin Iurman 		.flags	= GENL_ADMIN_PERM,
5588c6f6fa6SJustin Iurman 		.policy	= ioam6_genl_policy_addns,
5598c6f6fa6SJustin Iurman 		.maxattr = ARRAY_SIZE(ioam6_genl_policy_addns) - 1,
5608c6f6fa6SJustin Iurman 	},
5618c6f6fa6SJustin Iurman 	{
5628c6f6fa6SJustin Iurman 		.cmd	= IOAM6_CMD_DEL_NAMESPACE,
5638c6f6fa6SJustin Iurman 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5648c6f6fa6SJustin Iurman 		.doit	= ioam6_genl_delns,
5658c6f6fa6SJustin Iurman 		.flags	= GENL_ADMIN_PERM,
5668c6f6fa6SJustin Iurman 		.policy	= ioam6_genl_policy_delns,
5678c6f6fa6SJustin Iurman 		.maxattr = ARRAY_SIZE(ioam6_genl_policy_delns) - 1,
5688c6f6fa6SJustin Iurman 	},
5698c6f6fa6SJustin Iurman 	{
5708c6f6fa6SJustin Iurman 		.cmd	= IOAM6_CMD_DUMP_NAMESPACES,
5718c6f6fa6SJustin Iurman 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5728c6f6fa6SJustin Iurman 		.start	= ioam6_genl_dumpns_start,
5738c6f6fa6SJustin Iurman 		.dumpit	= ioam6_genl_dumpns,
5748c6f6fa6SJustin Iurman 		.done	= ioam6_genl_dumpns_done,
5758c6f6fa6SJustin Iurman 		.flags	= GENL_ADMIN_PERM,
5768c6f6fa6SJustin Iurman 	},
5778c6f6fa6SJustin Iurman 	{
5788c6f6fa6SJustin Iurman 		.cmd	= IOAM6_CMD_ADD_SCHEMA,
5798c6f6fa6SJustin Iurman 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5808c6f6fa6SJustin Iurman 		.doit	= ioam6_genl_addsc,
5818c6f6fa6SJustin Iurman 		.flags	= GENL_ADMIN_PERM,
5828c6f6fa6SJustin Iurman 		.policy	= ioam6_genl_policy_addsc,
5838c6f6fa6SJustin Iurman 		.maxattr = ARRAY_SIZE(ioam6_genl_policy_addsc) - 1,
5848c6f6fa6SJustin Iurman 	},
5858c6f6fa6SJustin Iurman 	{
5868c6f6fa6SJustin Iurman 		.cmd	= IOAM6_CMD_DEL_SCHEMA,
5878c6f6fa6SJustin Iurman 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5888c6f6fa6SJustin Iurman 		.doit	= ioam6_genl_delsc,
5898c6f6fa6SJustin Iurman 		.flags	= GENL_ADMIN_PERM,
5908c6f6fa6SJustin Iurman 		.policy	= ioam6_genl_policy_delsc,
5918c6f6fa6SJustin Iurman 		.maxattr = ARRAY_SIZE(ioam6_genl_policy_delsc) - 1,
5928c6f6fa6SJustin Iurman 	},
5938c6f6fa6SJustin Iurman 	{
5948c6f6fa6SJustin Iurman 		.cmd	= IOAM6_CMD_DUMP_SCHEMAS,
5958c6f6fa6SJustin Iurman 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
5968c6f6fa6SJustin Iurman 		.start	= ioam6_genl_dumpsc_start,
5978c6f6fa6SJustin Iurman 		.dumpit	= ioam6_genl_dumpsc,
5988c6f6fa6SJustin Iurman 		.done	= ioam6_genl_dumpsc_done,
5998c6f6fa6SJustin Iurman 		.flags	= GENL_ADMIN_PERM,
6008c6f6fa6SJustin Iurman 	},
6018c6f6fa6SJustin Iurman 	{
6028c6f6fa6SJustin Iurman 		.cmd	= IOAM6_CMD_NS_SET_SCHEMA,
6038c6f6fa6SJustin Iurman 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
6048c6f6fa6SJustin Iurman 		.doit	= ioam6_genl_ns_set_schema,
6058c6f6fa6SJustin Iurman 		.flags	= GENL_ADMIN_PERM,
6068c6f6fa6SJustin Iurman 		.policy	= ioam6_genl_policy_ns_sc,
6078c6f6fa6SJustin Iurman 		.maxattr = ARRAY_SIZE(ioam6_genl_policy_ns_sc) - 1,
6088c6f6fa6SJustin Iurman 	},
6098c6f6fa6SJustin Iurman };
6108c6f6fa6SJustin Iurman 
61167c8e4bbSJustin Iurman #define IOAM6_GENL_EV_GRP_OFFSET 0
61267c8e4bbSJustin Iurman 
61367c8e4bbSJustin Iurman static const struct genl_multicast_group ioam6_mcgrps[] = {
61467c8e4bbSJustin Iurman 	[IOAM6_GENL_EV_GRP_OFFSET] = { .name = IOAM6_GENL_EV_GRP_NAME,
61567c8e4bbSJustin Iurman 				       .flags = GENL_MCAST_CAP_NET_ADMIN },
61667c8e4bbSJustin Iurman };
61767c8e4bbSJustin Iurman 
ioam6_event_put_trace(struct sk_buff * skb,struct ioam6_trace_hdr * trace,unsigned int len)61867c8e4bbSJustin Iurman static int ioam6_event_put_trace(struct sk_buff *skb,
61967c8e4bbSJustin Iurman 				 struct ioam6_trace_hdr *trace,
62067c8e4bbSJustin Iurman 				 unsigned int len)
62167c8e4bbSJustin Iurman {
62267c8e4bbSJustin Iurman 	if (nla_put_u16(skb, IOAM6_EVENT_ATTR_TRACE_NAMESPACE,
62367c8e4bbSJustin Iurman 			be16_to_cpu(trace->namespace_id)) ||
62467c8e4bbSJustin Iurman 	    nla_put_u8(skb, IOAM6_EVENT_ATTR_TRACE_NODELEN, trace->nodelen) ||
62567c8e4bbSJustin Iurman 	    nla_put_u32(skb, IOAM6_EVENT_ATTR_TRACE_TYPE,
62667c8e4bbSJustin Iurman 			be32_to_cpu(trace->type_be32)) ||
62767c8e4bbSJustin Iurman 	    nla_put(skb, IOAM6_EVENT_ATTR_TRACE_DATA,
62867c8e4bbSJustin Iurman 		    len - sizeof(struct ioam6_trace_hdr) - trace->remlen * 4,
62967c8e4bbSJustin Iurman 		    trace->data + trace->remlen * 4))
63067c8e4bbSJustin Iurman 		return 1;
63167c8e4bbSJustin Iurman 
63267c8e4bbSJustin Iurman 	return 0;
63367c8e4bbSJustin Iurman }
63467c8e4bbSJustin Iurman 
ioam6_event(enum ioam6_event_type type,struct net * net,gfp_t gfp,void * opt,unsigned int opt_len)63567c8e4bbSJustin Iurman void ioam6_event(enum ioam6_event_type type, struct net *net, gfp_t gfp,
63667c8e4bbSJustin Iurman 		 void *opt, unsigned int opt_len)
63767c8e4bbSJustin Iurman {
63867c8e4bbSJustin Iurman 	struct nlmsghdr *nlh;
63967c8e4bbSJustin Iurman 	struct sk_buff *skb;
64067c8e4bbSJustin Iurman 
64167c8e4bbSJustin Iurman 	if (!genl_has_listeners(&ioam6_genl_family, net,
64267c8e4bbSJustin Iurman 				IOAM6_GENL_EV_GRP_OFFSET))
64367c8e4bbSJustin Iurman 		return;
64467c8e4bbSJustin Iurman 
64567c8e4bbSJustin Iurman 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
64667c8e4bbSJustin Iurman 	if (!skb)
64767c8e4bbSJustin Iurman 		return;
64867c8e4bbSJustin Iurman 
64967c8e4bbSJustin Iurman 	nlh = genlmsg_put(skb, 0, 0, &ioam6_genl_family, 0, type);
65067c8e4bbSJustin Iurman 	if (!nlh)
65167c8e4bbSJustin Iurman 		goto nla_put_failure;
65267c8e4bbSJustin Iurman 
65367c8e4bbSJustin Iurman 	switch (type) {
65467c8e4bbSJustin Iurman 	case IOAM6_EVENT_UNSPEC:
65567c8e4bbSJustin Iurman 		WARN_ON_ONCE(1);
65667c8e4bbSJustin Iurman 		break;
65767c8e4bbSJustin Iurman 	case IOAM6_EVENT_TRACE:
65867c8e4bbSJustin Iurman 		if (ioam6_event_put_trace(skb, (struct ioam6_trace_hdr *)opt,
65967c8e4bbSJustin Iurman 					  opt_len))
66067c8e4bbSJustin Iurman 			goto nla_put_failure;
66167c8e4bbSJustin Iurman 		break;
66267c8e4bbSJustin Iurman 	}
66367c8e4bbSJustin Iurman 
66467c8e4bbSJustin Iurman 	genlmsg_end(skb, nlh);
66567c8e4bbSJustin Iurman 	genlmsg_multicast_netns(&ioam6_genl_family, net, skb, 0,
66667c8e4bbSJustin Iurman 				IOAM6_GENL_EV_GRP_OFFSET, gfp);
66767c8e4bbSJustin Iurman 	return;
66867c8e4bbSJustin Iurman 
66967c8e4bbSJustin Iurman nla_put_failure:
67067c8e4bbSJustin Iurman 	nlmsg_free(skb);
67167c8e4bbSJustin Iurman }
67267c8e4bbSJustin Iurman 
6738c6f6fa6SJustin Iurman static struct genl_family ioam6_genl_family __ro_after_init = {
6748c6f6fa6SJustin Iurman 	.name		= IOAM6_GENL_NAME,
6758c6f6fa6SJustin Iurman 	.version	= IOAM6_GENL_VERSION,
6768c6f6fa6SJustin Iurman 	.netnsok	= true,
6778c6f6fa6SJustin Iurman 	.parallel_ops	= true,
6788c6f6fa6SJustin Iurman 	.ops		= ioam6_genl_ops,
6798c6f6fa6SJustin Iurman 	.n_ops		= ARRAY_SIZE(ioam6_genl_ops),
6809c5d03d3SJakub Kicinski 	.resv_start_op	= IOAM6_CMD_NS_SET_SCHEMA + 1,
68167c8e4bbSJustin Iurman 	.mcgrps		= ioam6_mcgrps,
68267c8e4bbSJustin Iurman 	.n_mcgrps	= ARRAY_SIZE(ioam6_mcgrps),
6838c6f6fa6SJustin Iurman 	.module		= THIS_MODULE,
6848c6f6fa6SJustin Iurman };
6858c6f6fa6SJustin Iurman 
ioam6_namespace(struct net * net,__be16 id)6869ee11f0fSJustin Iurman struct ioam6_namespace *ioam6_namespace(struct net *net, __be16 id)
6879ee11f0fSJustin Iurman {
6889ee11f0fSJustin Iurman 	struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
6899ee11f0fSJustin Iurman 
6909ee11f0fSJustin Iurman 	return rhashtable_lookup_fast(&nsdata->namespaces, &id, rht_ns_params);
6919ee11f0fSJustin Iurman }
6929ee11f0fSJustin 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)6939ee11f0fSJustin Iurman static void __ioam6_fill_trace_data(struct sk_buff *skb,
6949ee11f0fSJustin Iurman 				    struct ioam6_namespace *ns,
6959ee11f0fSJustin Iurman 				    struct ioam6_trace_hdr *trace,
6969ee11f0fSJustin Iurman 				    struct ioam6_schema *sc,
69752d03786SJustin Iurman 				    u8 sclen, bool is_input)
6989ee11f0fSJustin Iurman {
699*93d1cff3SEric Dumazet 	struct net_device *dev = skb_dst_dev(skb);
700b6561f84SMartin KaFai Lau 	struct timespec64 ts;
701b6561f84SMartin KaFai Lau 	ktime_t tstamp;
7029ee11f0fSJustin Iurman 	u64 raw64;
7039ee11f0fSJustin Iurman 	u32 raw32;
7049ee11f0fSJustin Iurman 	u16 raw16;
7059ee11f0fSJustin Iurman 	u8 *data;
7069ee11f0fSJustin Iurman 	u8 byte;
7079ee11f0fSJustin Iurman 
7089ee11f0fSJustin Iurman 	data = trace->data + trace->remlen * 4 - trace->nodelen * 4 - sclen * 4;
7099ee11f0fSJustin Iurman 
7109ee11f0fSJustin Iurman 	/* hop_lim and node_id */
7119ee11f0fSJustin Iurman 	if (trace->type.bit0) {
7129ee11f0fSJustin Iurman 		byte = ipv6_hdr(skb)->hop_limit;
71352d03786SJustin Iurman 		if (is_input)
7149ee11f0fSJustin Iurman 			byte--;
7159ee11f0fSJustin Iurman 
716*93d1cff3SEric Dumazet 		raw32 = dev_net(dev)->ipv6.sysctl.ioam6_id;
7179ee11f0fSJustin Iurman 
7189ee11f0fSJustin Iurman 		*(__be32 *)data = cpu_to_be32((byte << 24) | raw32);
7199ee11f0fSJustin Iurman 		data += sizeof(__be32);
7209ee11f0fSJustin Iurman 	}
7219ee11f0fSJustin Iurman 
7229ee11f0fSJustin Iurman 	/* ingress_if_id and egress_if_id */
7239ee11f0fSJustin Iurman 	if (trace->type.bit1) {
7249ee11f0fSJustin Iurman 		if (!skb->dev)
7259ee11f0fSJustin Iurman 			raw16 = IOAM6_U16_UNAVAILABLE;
7269ee11f0fSJustin Iurman 		else
7272f0ff05aSEric Dumazet 			raw16 = (__force u16)READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id);
7289ee11f0fSJustin Iurman 
7299ee11f0fSJustin Iurman 		*(__be16 *)data = cpu_to_be16(raw16);
7309ee11f0fSJustin Iurman 		data += sizeof(__be16);
7319ee11f0fSJustin Iurman 
732*93d1cff3SEric Dumazet 		if (dev->flags & IFF_LOOPBACK)
7339ee11f0fSJustin Iurman 			raw16 = IOAM6_U16_UNAVAILABLE;
7349ee11f0fSJustin Iurman 		else
735*93d1cff3SEric Dumazet 			raw16 = (__force u16)READ_ONCE(__in6_dev_get(dev)->cnf.ioam6_id);
7369ee11f0fSJustin Iurman 
7379ee11f0fSJustin Iurman 		*(__be16 *)data = cpu_to_be16(raw16);
7389ee11f0fSJustin Iurman 		data += sizeof(__be16);
7399ee11f0fSJustin Iurman 	}
7409ee11f0fSJustin Iurman 
7419ee11f0fSJustin Iurman 	/* timestamp seconds */
7429ee11f0fSJustin Iurman 	if (trace->type.bit2) {
7433edede08SJustin Iurman 		if (!skb->dev) {
7443edede08SJustin Iurman 			*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7453edede08SJustin Iurman 		} else {
746b6561f84SMartin KaFai Lau 			tstamp = skb_tstamp_cond(skb, true);
747b6561f84SMartin KaFai Lau 			ts = ktime_to_timespec64(tstamp);
7489ee11f0fSJustin Iurman 
7499ee11f0fSJustin Iurman 			*(__be32 *)data = cpu_to_be32((u32)ts.tv_sec);
7503edede08SJustin Iurman 		}
7519ee11f0fSJustin Iurman 		data += sizeof(__be32);
7529ee11f0fSJustin Iurman 	}
7539ee11f0fSJustin Iurman 
7549ee11f0fSJustin Iurman 	/* timestamp subseconds */
7559ee11f0fSJustin Iurman 	if (trace->type.bit3) {
7563edede08SJustin Iurman 		if (!skb->dev) {
7573edede08SJustin Iurman 			*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7583edede08SJustin Iurman 		} else {
759b6561f84SMartin KaFai Lau 			if (!trace->type.bit2) {
760b6561f84SMartin KaFai Lau 				tstamp = skb_tstamp_cond(skb, true);
761b6561f84SMartin KaFai Lau 				ts = ktime_to_timespec64(tstamp);
762b6561f84SMartin KaFai Lau 			}
7639ee11f0fSJustin Iurman 
764b6561f84SMartin KaFai Lau 			*(__be32 *)data = cpu_to_be32((u32)(ts.tv_nsec / NSEC_PER_USEC));
7653edede08SJustin Iurman 		}
7669ee11f0fSJustin Iurman 		data += sizeof(__be32);
7679ee11f0fSJustin Iurman 	}
7689ee11f0fSJustin Iurman 
7699ee11f0fSJustin Iurman 	/* transit delay */
7709ee11f0fSJustin Iurman 	if (trace->type.bit4) {
7719ee11f0fSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
7729ee11f0fSJustin Iurman 		data += sizeof(__be32);
7739ee11f0fSJustin Iurman 	}
7749ee11f0fSJustin Iurman 
7759ee11f0fSJustin Iurman 	/* namespace data */
7769ee11f0fSJustin Iurman 	if (trace->type.bit5) {
7779ee11f0fSJustin Iurman 		*(__be32 *)data = ns->data;
7789ee11f0fSJustin Iurman 		data += sizeof(__be32);
7799ee11f0fSJustin Iurman 	}
7809ee11f0fSJustin Iurman 
7819ee11f0fSJustin Iurman 	/* queue depth */
7829ee11f0fSJustin Iurman 	if (trace->type.bit6) {
783b63c5478SJustin Iurman 		struct netdev_queue *queue;
784b63c5478SJustin Iurman 		struct Qdisc *qdisc;
785b63c5478SJustin Iurman 		__u32 qlen, backlog;
786b63c5478SJustin Iurman 
787*93d1cff3SEric Dumazet 		if (dev->flags & IFF_LOOPBACK) {
7889ee11f0fSJustin Iurman 			*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
789b63c5478SJustin Iurman 		} else {
790*93d1cff3SEric Dumazet 			queue = skb_get_tx_queue(dev, skb);
791b63c5478SJustin Iurman 			qdisc = rcu_dereference(queue->qdisc);
792b63c5478SJustin Iurman 			qdisc_qstats_qlen_backlog(qdisc, &qlen, &backlog);
793b63c5478SJustin Iurman 
794b63c5478SJustin Iurman 			*(__be32 *)data = cpu_to_be32(backlog);
795b63c5478SJustin Iurman 		}
7969ee11f0fSJustin Iurman 		data += sizeof(__be32);
7979ee11f0fSJustin Iurman 	}
7989ee11f0fSJustin Iurman 
7999ee11f0fSJustin Iurman 	/* checksum complement */
8009ee11f0fSJustin Iurman 	if (trace->type.bit7) {
8019ee11f0fSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8029ee11f0fSJustin Iurman 		data += sizeof(__be32);
8039ee11f0fSJustin Iurman 	}
8049ee11f0fSJustin Iurman 
8059ee11f0fSJustin Iurman 	/* hop_lim and node_id (wide) */
8069ee11f0fSJustin Iurman 	if (trace->type.bit8) {
8079ee11f0fSJustin Iurman 		byte = ipv6_hdr(skb)->hop_limit;
80852d03786SJustin Iurman 		if (is_input)
8099ee11f0fSJustin Iurman 			byte--;
8109ee11f0fSJustin Iurman 
811*93d1cff3SEric Dumazet 		raw64 = dev_net(dev)->ipv6.sysctl.ioam6_id_wide;
8129ee11f0fSJustin Iurman 
8139ee11f0fSJustin Iurman 		*(__be64 *)data = cpu_to_be64(((u64)byte << 56) | raw64);
8149ee11f0fSJustin Iurman 		data += sizeof(__be64);
8159ee11f0fSJustin Iurman 	}
8169ee11f0fSJustin Iurman 
8179ee11f0fSJustin Iurman 	/* ingress_if_id and egress_if_id (wide) */
8189ee11f0fSJustin Iurman 	if (trace->type.bit9) {
8199ee11f0fSJustin Iurman 		if (!skb->dev)
8209ee11f0fSJustin Iurman 			raw32 = IOAM6_U32_UNAVAILABLE;
8219ee11f0fSJustin Iurman 		else
8222f0ff05aSEric Dumazet 			raw32 = READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_id_wide);
8239ee11f0fSJustin Iurman 
8249ee11f0fSJustin Iurman 		*(__be32 *)data = cpu_to_be32(raw32);
8259ee11f0fSJustin Iurman 		data += sizeof(__be32);
8269ee11f0fSJustin Iurman 
827*93d1cff3SEric Dumazet 		if (dev->flags & IFF_LOOPBACK)
8289ee11f0fSJustin Iurman 			raw32 = IOAM6_U32_UNAVAILABLE;
8299ee11f0fSJustin Iurman 		else
830*93d1cff3SEric Dumazet 			raw32 = READ_ONCE(__in6_dev_get(dev)->cnf.ioam6_id_wide);
8319ee11f0fSJustin Iurman 
8329ee11f0fSJustin Iurman 		*(__be32 *)data = cpu_to_be32(raw32);
8339ee11f0fSJustin Iurman 		data += sizeof(__be32);
8349ee11f0fSJustin Iurman 	}
8359ee11f0fSJustin Iurman 
8369ee11f0fSJustin Iurman 	/* namespace data (wide) */
8379ee11f0fSJustin Iurman 	if (trace->type.bit10) {
8389ee11f0fSJustin Iurman 		*(__be64 *)data = ns->data_wide;
8399ee11f0fSJustin Iurman 		data += sizeof(__be64);
8409ee11f0fSJustin Iurman 	}
8419ee11f0fSJustin Iurman 
8429ee11f0fSJustin Iurman 	/* buffer occupancy */
8439ee11f0fSJustin Iurman 	if (trace->type.bit11) {
8449ee11f0fSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8459ee11f0fSJustin Iurman 		data += sizeof(__be32);
8469ee11f0fSJustin Iurman 	}
8479ee11f0fSJustin Iurman 
8482bbc977cSJustin Iurman 	/* bit12 undefined: filled with empty value */
8492bbc977cSJustin Iurman 	if (trace->type.bit12) {
8502bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8512bbc977cSJustin Iurman 		data += sizeof(__be32);
8522bbc977cSJustin Iurman 	}
8532bbc977cSJustin Iurman 
8542bbc977cSJustin Iurman 	/* bit13 undefined: filled with empty value */
8552bbc977cSJustin Iurman 	if (trace->type.bit13) {
8562bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8572bbc977cSJustin Iurman 		data += sizeof(__be32);
8582bbc977cSJustin Iurman 	}
8592bbc977cSJustin Iurman 
8602bbc977cSJustin Iurman 	/* bit14 undefined: filled with empty value */
8612bbc977cSJustin Iurman 	if (trace->type.bit14) {
8622bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8632bbc977cSJustin Iurman 		data += sizeof(__be32);
8642bbc977cSJustin Iurman 	}
8652bbc977cSJustin Iurman 
8662bbc977cSJustin Iurman 	/* bit15 undefined: filled with empty value */
8672bbc977cSJustin Iurman 	if (trace->type.bit15) {
8682bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8692bbc977cSJustin Iurman 		data += sizeof(__be32);
8702bbc977cSJustin Iurman 	}
8712bbc977cSJustin Iurman 
8722bbc977cSJustin Iurman 	/* bit16 undefined: filled with empty value */
8732bbc977cSJustin Iurman 	if (trace->type.bit16) {
8742bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8752bbc977cSJustin Iurman 		data += sizeof(__be32);
8762bbc977cSJustin Iurman 	}
8772bbc977cSJustin Iurman 
8782bbc977cSJustin Iurman 	/* bit17 undefined: filled with empty value */
8792bbc977cSJustin Iurman 	if (trace->type.bit17) {
8802bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8812bbc977cSJustin Iurman 		data += sizeof(__be32);
8822bbc977cSJustin Iurman 	}
8832bbc977cSJustin Iurman 
8842bbc977cSJustin Iurman 	/* bit18 undefined: filled with empty value */
8852bbc977cSJustin Iurman 	if (trace->type.bit18) {
8862bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8872bbc977cSJustin Iurman 		data += sizeof(__be32);
8882bbc977cSJustin Iurman 	}
8892bbc977cSJustin Iurman 
8902bbc977cSJustin Iurman 	/* bit19 undefined: filled with empty value */
8912bbc977cSJustin Iurman 	if (trace->type.bit19) {
8922bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8932bbc977cSJustin Iurman 		data += sizeof(__be32);
8942bbc977cSJustin Iurman 	}
8952bbc977cSJustin Iurman 
8962bbc977cSJustin Iurman 	/* bit20 undefined: filled with empty value */
8972bbc977cSJustin Iurman 	if (trace->type.bit20) {
8982bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
8992bbc977cSJustin Iurman 		data += sizeof(__be32);
9002bbc977cSJustin Iurman 	}
9012bbc977cSJustin Iurman 
9022bbc977cSJustin Iurman 	/* bit21 undefined: filled with empty value */
9032bbc977cSJustin Iurman 	if (trace->type.bit21) {
9042bbc977cSJustin Iurman 		*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE);
9052bbc977cSJustin Iurman 		data += sizeof(__be32);
9062bbc977cSJustin Iurman 	}
9072bbc977cSJustin Iurman 
9089ee11f0fSJustin Iurman 	/* opaque state snapshot */
9099ee11f0fSJustin Iurman 	if (trace->type.bit22) {
9109ee11f0fSJustin Iurman 		if (!sc) {
9119ee11f0fSJustin Iurman 			*(__be32 *)data = cpu_to_be32(IOAM6_U32_UNAVAILABLE >> 8);
9129ee11f0fSJustin Iurman 		} else {
9139ee11f0fSJustin Iurman 			*(__be32 *)data = sc->hdr;
9149ee11f0fSJustin Iurman 			data += sizeof(__be32);
9159ee11f0fSJustin Iurman 
9169ee11f0fSJustin Iurman 			memcpy(data, sc->data, sc->len);
9179ee11f0fSJustin Iurman 		}
9189ee11f0fSJustin Iurman 	}
9199ee11f0fSJustin Iurman }
9209ee11f0fSJustin Iurman 
9219ee11f0fSJustin 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)9229ee11f0fSJustin Iurman void ioam6_fill_trace_data(struct sk_buff *skb,
9239ee11f0fSJustin Iurman 			   struct ioam6_namespace *ns,
92452d03786SJustin Iurman 			   struct ioam6_trace_hdr *trace,
92552d03786SJustin Iurman 			   bool is_input)
9269ee11f0fSJustin Iurman {
9279ee11f0fSJustin Iurman 	struct ioam6_schema *sc;
9289ee11f0fSJustin Iurman 	u8 sclen = 0;
9299ee11f0fSJustin Iurman 
9302bbc977cSJustin Iurman 	/* Skip if Overflow flag is set
9319ee11f0fSJustin Iurman 	 */
9322bbc977cSJustin Iurman 	if (trace->overflow)
9339ee11f0fSJustin Iurman 		return;
9349ee11f0fSJustin Iurman 
9359ee11f0fSJustin Iurman 	/* NodeLen does not include Opaque State Snapshot length. We need to
9369ee11f0fSJustin Iurman 	 * take it into account if the corresponding bit is set (bit 22) and
9379ee11f0fSJustin Iurman 	 * if the current IOAM namespace has an active schema attached to it
9389ee11f0fSJustin Iurman 	 */
9399ee11f0fSJustin Iurman 	sc = rcu_dereference(ns->schema);
9409ee11f0fSJustin Iurman 	if (trace->type.bit22) {
9419ee11f0fSJustin Iurman 		sclen = sizeof_field(struct ioam6_schema, hdr) / 4;
9429ee11f0fSJustin Iurman 
9439ee11f0fSJustin Iurman 		if (sc)
9449ee11f0fSJustin Iurman 			sclen += sc->len / 4;
9459ee11f0fSJustin Iurman 	}
9469ee11f0fSJustin Iurman 
9479ee11f0fSJustin Iurman 	/* If there is no space remaining, we set the Overflow flag and we
9489ee11f0fSJustin Iurman 	 * skip without filling the trace
9499ee11f0fSJustin Iurman 	 */
9509ee11f0fSJustin Iurman 	if (!trace->remlen || trace->remlen < trace->nodelen + sclen) {
9519ee11f0fSJustin Iurman 		trace->overflow = 1;
9529ee11f0fSJustin Iurman 		return;
9539ee11f0fSJustin Iurman 	}
9549ee11f0fSJustin Iurman 
95552d03786SJustin Iurman 	__ioam6_fill_trace_data(skb, ns, trace, sc, sclen, is_input);
9569ee11f0fSJustin Iurman 	trace->remlen -= trace->nodelen + sclen;
9579ee11f0fSJustin Iurman }
9589ee11f0fSJustin Iurman 
ioam6_net_init(struct net * net)9599ee11f0fSJustin Iurman static int __net_init ioam6_net_init(struct net *net)
9609ee11f0fSJustin Iurman {
9619ee11f0fSJustin Iurman 	struct ioam6_pernet_data *nsdata;
9629ee11f0fSJustin Iurman 	int err = -ENOMEM;
9639ee11f0fSJustin Iurman 
9649ee11f0fSJustin Iurman 	nsdata = kzalloc(sizeof(*nsdata), GFP_KERNEL);
9659ee11f0fSJustin Iurman 	if (!nsdata)
9669ee11f0fSJustin Iurman 		goto out;
9679ee11f0fSJustin Iurman 
9689ee11f0fSJustin Iurman 	mutex_init(&nsdata->lock);
9699ee11f0fSJustin Iurman 	net->ipv6.ioam6_data = nsdata;
9709ee11f0fSJustin Iurman 
9719ee11f0fSJustin Iurman 	err = rhashtable_init(&nsdata->namespaces, &rht_ns_params);
9729ee11f0fSJustin Iurman 	if (err)
9739ee11f0fSJustin Iurman 		goto free_nsdata;
9749ee11f0fSJustin Iurman 
9759ee11f0fSJustin Iurman 	err = rhashtable_init(&nsdata->schemas, &rht_sc_params);
9769ee11f0fSJustin Iurman 	if (err)
9779ee11f0fSJustin Iurman 		goto free_rht_ns;
9789ee11f0fSJustin Iurman 
9799ee11f0fSJustin Iurman out:
9809ee11f0fSJustin Iurman 	return err;
9819ee11f0fSJustin Iurman free_rht_ns:
9829ee11f0fSJustin Iurman 	rhashtable_destroy(&nsdata->namespaces);
9839ee11f0fSJustin Iurman free_nsdata:
9849ee11f0fSJustin Iurman 	kfree(nsdata);
9859ee11f0fSJustin Iurman 	net->ipv6.ioam6_data = NULL;
9869ee11f0fSJustin Iurman 	goto out;
9879ee11f0fSJustin Iurman }
9889ee11f0fSJustin Iurman 
ioam6_net_exit(struct net * net)9899ee11f0fSJustin Iurman static void __net_exit ioam6_net_exit(struct net *net)
9909ee11f0fSJustin Iurman {
9919ee11f0fSJustin Iurman 	struct ioam6_pernet_data *nsdata = ioam6_pernet(net);
9929ee11f0fSJustin Iurman 
9939ee11f0fSJustin Iurman 	rhashtable_free_and_destroy(&nsdata->namespaces, ioam6_free_ns, NULL);
9949ee11f0fSJustin Iurman 	rhashtable_free_and_destroy(&nsdata->schemas, ioam6_free_sc, NULL);
9959ee11f0fSJustin Iurman 
9969ee11f0fSJustin Iurman 	kfree(nsdata);
9979ee11f0fSJustin Iurman }
9989ee11f0fSJustin Iurman 
9999ee11f0fSJustin Iurman static struct pernet_operations ioam6_net_ops = {
10009ee11f0fSJustin Iurman 	.init = ioam6_net_init,
10019ee11f0fSJustin Iurman 	.exit = ioam6_net_exit,
10029ee11f0fSJustin Iurman };
10039ee11f0fSJustin Iurman 
ioam6_init(void)10049ee11f0fSJustin Iurman int __init ioam6_init(void)
10059ee11f0fSJustin Iurman {
10069ee11f0fSJustin Iurman 	int err = register_pernet_subsys(&ioam6_net_ops);
10079ee11f0fSJustin Iurman 	if (err)
10088c6f6fa6SJustin Iurman 		goto out;
10098c6f6fa6SJustin Iurman 
10108c6f6fa6SJustin Iurman 	err = genl_register_family(&ioam6_genl_family);
10118c6f6fa6SJustin Iurman 	if (err)
10128c6f6fa6SJustin Iurman 		goto out_unregister_pernet_subsys;
10139ee11f0fSJustin Iurman 
10143edede08SJustin Iurman #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
10153edede08SJustin Iurman 	err = ioam6_iptunnel_init();
10163edede08SJustin Iurman 	if (err)
10173edede08SJustin Iurman 		goto out_unregister_genl;
10183edede08SJustin Iurman #endif
10193edede08SJustin Iurman 
10209ee11f0fSJustin Iurman 	pr_info("In-situ OAM (IOAM) with IPv6\n");
10218c6f6fa6SJustin Iurman 
10228c6f6fa6SJustin Iurman out:
10238c6f6fa6SJustin Iurman 	return err;
10243edede08SJustin Iurman #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
10253edede08SJustin Iurman out_unregister_genl:
10263edede08SJustin Iurman 	genl_unregister_family(&ioam6_genl_family);
10273edede08SJustin Iurman #endif
10288c6f6fa6SJustin Iurman out_unregister_pernet_subsys:
10298c6f6fa6SJustin Iurman 	unregister_pernet_subsys(&ioam6_net_ops);
10308c6f6fa6SJustin Iurman 	goto out;
10319ee11f0fSJustin Iurman }
10329ee11f0fSJustin Iurman 
ioam6_exit(void)10339ee11f0fSJustin Iurman void ioam6_exit(void)
10349ee11f0fSJustin Iurman {
10353edede08SJustin Iurman #ifdef CONFIG_IPV6_IOAM6_LWTUNNEL
10363edede08SJustin Iurman 	ioam6_iptunnel_exit();
10373edede08SJustin Iurman #endif
10388c6f6fa6SJustin Iurman 	genl_unregister_family(&ioam6_genl_family);
10399ee11f0fSJustin Iurman 	unregister_pernet_subsys(&ioam6_net_ops);
10409ee11f0fSJustin Iurman }
1041