xref: /openbmc/linux/net/ipv6/ioam6.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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