xref: /kernel/linux/linux-5.10/net/ipv6/seg6.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  SR-IPv6 implementation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Author:
68c2ecf20Sopenharmony_ci *  David Lebrun <david.lebrun@uclouvain.be>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/socket.h>
128c2ecf20Sopenharmony_ci#include <linux/net.h>
138c2ecf20Sopenharmony_ci#include <linux/in6.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/rhashtable.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <net/ipv6.h>
188c2ecf20Sopenharmony_ci#include <net/protocol.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <net/seg6.h>
218c2ecf20Sopenharmony_ci#include <net/genetlink.h>
228c2ecf20Sopenharmony_ci#include <linux/seg6.h>
238c2ecf20Sopenharmony_ci#include <linux/seg6_genl.h>
248c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
258c2ecf20Sopenharmony_ci#include <net/seg6_hmac.h>
268c2ecf20Sopenharmony_ci#endif
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cibool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	unsigned int tlv_offset;
318c2ecf20Sopenharmony_ci	int max_last_entry;
328c2ecf20Sopenharmony_ci	int trailing;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (srh->type != IPV6_SRCRT_TYPE_4)
358c2ecf20Sopenharmony_ci		return false;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (((srh->hdrlen + 1) << 3) != len)
388c2ecf20Sopenharmony_ci		return false;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (!reduced && srh->segments_left > srh->first_segment) {
418c2ecf20Sopenharmony_ci		return false;
428c2ecf20Sopenharmony_ci	} else {
438c2ecf20Sopenharmony_ci		max_last_entry = (srh->hdrlen / 2) - 1;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		if (srh->first_segment > max_last_entry)
468c2ecf20Sopenharmony_ci			return false;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci		if (srh->segments_left > srh->first_segment + 1)
498c2ecf20Sopenharmony_ci			return false;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	trailing = len - tlv_offset;
558c2ecf20Sopenharmony_ci	if (trailing < 0)
568c2ecf20Sopenharmony_ci		return false;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	while (trailing) {
598c2ecf20Sopenharmony_ci		struct sr6_tlv *tlv;
608c2ecf20Sopenharmony_ci		unsigned int tlv_len;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		if (trailing < sizeof(*tlv))
638c2ecf20Sopenharmony_ci			return false;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset);
668c2ecf20Sopenharmony_ci		tlv_len = sizeof(*tlv) + tlv->len;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		trailing -= tlv_len;
698c2ecf20Sopenharmony_ci		if (trailing < 0)
708c2ecf20Sopenharmony_ci			return false;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		tlv_offset += tlv_len;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return true;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic struct genl_family seg6_genl_family;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
818c2ecf20Sopenharmony_ci	[SEG6_ATTR_DST]				= { .type = NLA_BINARY,
828c2ecf20Sopenharmony_ci		.len = sizeof(struct in6_addr) },
838c2ecf20Sopenharmony_ci	[SEG6_ATTR_DSTLEN]			= { .type = NLA_S32, },
848c2ecf20Sopenharmony_ci	[SEG6_ATTR_HMACKEYID]		= { .type = NLA_U32, },
858c2ecf20Sopenharmony_ci	[SEG6_ATTR_SECRET]			= { .type = NLA_BINARY, },
868c2ecf20Sopenharmony_ci	[SEG6_ATTR_SECRETLEN]		= { .type = NLA_U8, },
878c2ecf20Sopenharmony_ci	[SEG6_ATTR_ALGID]			= { .type = NLA_U8, },
888c2ecf20Sopenharmony_ci	[SEG6_ATTR_HMACINFO]		= { .type = NLA_NESTED, },
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct net *net = genl_info_net(info);
968c2ecf20Sopenharmony_ci	struct seg6_pernet_data *sdata;
978c2ecf20Sopenharmony_ci	struct seg6_hmac_info *hinfo;
988c2ecf20Sopenharmony_ci	u32 hmackeyid;
998c2ecf20Sopenharmony_ci	char *secret;
1008c2ecf20Sopenharmony_ci	int err = 0;
1018c2ecf20Sopenharmony_ci	u8 algid;
1028c2ecf20Sopenharmony_ci	u8 slen;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	sdata = seg6_pernet(net);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (!info->attrs[SEG6_ATTR_HMACKEYID] ||
1078c2ecf20Sopenharmony_ci	    !info->attrs[SEG6_ATTR_SECRETLEN] ||
1088c2ecf20Sopenharmony_ci	    !info->attrs[SEG6_ATTR_ALGID])
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]);
1128c2ecf20Sopenharmony_ci	slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]);
1138c2ecf20Sopenharmony_ci	algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (hmackeyid == 0)
1168c2ecf20Sopenharmony_ci		return -EINVAL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (slen > SEG6_HMAC_SECRET_LEN)
1198c2ecf20Sopenharmony_ci		return -EINVAL;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	mutex_lock(&sdata->lock);
1228c2ecf20Sopenharmony_ci	hinfo = seg6_hmac_info_lookup(net, hmackeyid);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (!slen) {
1258c2ecf20Sopenharmony_ci		if (!hinfo)
1268c2ecf20Sopenharmony_ci			err = -ENOENT;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		err = seg6_hmac_info_del(net, hmackeyid);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		goto out_unlock;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!info->attrs[SEG6_ATTR_SECRET]) {
1348c2ecf20Sopenharmony_ci		err = -EINVAL;
1358c2ecf20Sopenharmony_ci		goto out_unlock;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (slen > nla_len(info->attrs[SEG6_ATTR_SECRET])) {
1398c2ecf20Sopenharmony_ci		err = -EINVAL;
1408c2ecf20Sopenharmony_ci		goto out_unlock;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (hinfo) {
1448c2ecf20Sopenharmony_ci		err = seg6_hmac_info_del(net, hmackeyid);
1458c2ecf20Sopenharmony_ci		if (err)
1468c2ecf20Sopenharmony_ci			goto out_unlock;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL);
1528c2ecf20Sopenharmony_ci	if (!hinfo) {
1538c2ecf20Sopenharmony_ci		err = -ENOMEM;
1548c2ecf20Sopenharmony_ci		goto out_unlock;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	memcpy(hinfo->secret, secret, slen);
1588c2ecf20Sopenharmony_ci	hinfo->slen = slen;
1598c2ecf20Sopenharmony_ci	hinfo->alg_id = algid;
1608c2ecf20Sopenharmony_ci	hinfo->hmackeyid = hmackeyid;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	err = seg6_hmac_info_add(net, hmackeyid, hinfo);
1638c2ecf20Sopenharmony_ci	if (err)
1648c2ecf20Sopenharmony_ci		kfree(hinfo);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ciout_unlock:
1678c2ecf20Sopenharmony_ci	mutex_unlock(&sdata->lock);
1688c2ecf20Sopenharmony_ci	return err;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci#else
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	return -ENOTSUPP;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci#endif
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct net *net = genl_info_net(info);
1838c2ecf20Sopenharmony_ci	struct in6_addr *val, *t_old, *t_new;
1848c2ecf20Sopenharmony_ci	struct seg6_pernet_data *sdata;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	sdata = seg6_pernet(net);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (!info->attrs[SEG6_ATTR_DST])
1898c2ecf20Sopenharmony_ci		return -EINVAL;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	val = nla_data(info->attrs[SEG6_ATTR_DST]);
1928c2ecf20Sopenharmony_ci	t_new = kmemdup(val, sizeof(*val), GFP_KERNEL);
1938c2ecf20Sopenharmony_ci	if (!t_new)
1948c2ecf20Sopenharmony_ci		return -ENOMEM;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	mutex_lock(&sdata->lock);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	t_old = sdata->tun_src;
1998c2ecf20Sopenharmony_ci	rcu_assign_pointer(sdata->tun_src, t_new);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	mutex_unlock(&sdata->lock);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	synchronize_net();
2048c2ecf20Sopenharmony_ci	kfree(t_old);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct net *net = genl_info_net(info);
2128c2ecf20Sopenharmony_ci	struct in6_addr *tun_src;
2138c2ecf20Sopenharmony_ci	struct sk_buff *msg;
2148c2ecf20Sopenharmony_ci	void *hdr;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
2178c2ecf20Sopenharmony_ci	if (!msg)
2188c2ecf20Sopenharmony_ci		return -ENOMEM;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
2218c2ecf20Sopenharmony_ci			  &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC);
2228c2ecf20Sopenharmony_ci	if (!hdr)
2238c2ecf20Sopenharmony_ci		goto free_msg;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	rcu_read_lock();
2268c2ecf20Sopenharmony_ci	tun_src = rcu_dereference(seg6_pernet(net)->tun_src);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src))
2298c2ecf20Sopenharmony_ci		goto nla_put_failure;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	rcu_read_unlock();
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	genlmsg_end(msg, hdr);
2348c2ecf20Sopenharmony_ci	return genlmsg_reply(msg, info);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cinla_put_failure:
2378c2ecf20Sopenharmony_ci	rcu_read_unlock();
2388c2ecf20Sopenharmony_cifree_msg:
2398c2ecf20Sopenharmony_ci	nlmsg_free(msg);
2408c2ecf20Sopenharmony_ci	return -ENOMEM;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo,
2468c2ecf20Sopenharmony_ci				 struct sk_buff *msg)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) ||
2498c2ecf20Sopenharmony_ci	    nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) ||
2508c2ecf20Sopenharmony_ci	    nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) ||
2518c2ecf20Sopenharmony_ci	    nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id))
2528c2ecf20Sopenharmony_ci		return -1;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo,
2588c2ecf20Sopenharmony_ci					u32 portid, u32 seq, u32 flags,
2598c2ecf20Sopenharmony_ci					struct sk_buff *skb, u8 cmd)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	void *hdr;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd);
2648c2ecf20Sopenharmony_ci	if (!hdr)
2658c2ecf20Sopenharmony_ci		return -ENOMEM;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (__seg6_hmac_fill_info(hinfo, skb) < 0)
2688c2ecf20Sopenharmony_ci		goto nla_put_failure;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cinla_put_failure:
2748c2ecf20Sopenharmony_ci	genlmsg_cancel(skb, hdr);
2758c2ecf20Sopenharmony_ci	return -EMSGSIZE;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_start(struct netlink_callback *cb)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct net *net = sock_net(cb->skb->sk);
2818c2ecf20Sopenharmony_ci	struct seg6_pernet_data *sdata;
2828c2ecf20Sopenharmony_ci	struct rhashtable_iter *iter;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	sdata = seg6_pernet(net);
2858c2ecf20Sopenharmony_ci	iter = (struct rhashtable_iter *)cb->args[0];
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (!iter) {
2888c2ecf20Sopenharmony_ci		iter = kmalloc(sizeof(*iter), GFP_KERNEL);
2898c2ecf20Sopenharmony_ci		if (!iter)
2908c2ecf20Sopenharmony_ci			return -ENOMEM;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		cb->args[0] = (long)iter;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	rhashtable_walk_enter(&sdata->hmac_infos, iter);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_done(struct netlink_callback *cb)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	rhashtable_walk_exit(iter);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	kfree(iter);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
3148c2ecf20Sopenharmony_ci	struct seg6_hmac_info *hinfo;
3158c2ecf20Sopenharmony_ci	int ret;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	rhashtable_walk_start(iter);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	for (;;) {
3208c2ecf20Sopenharmony_ci		hinfo = rhashtable_walk_next(iter);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		if (IS_ERR(hinfo)) {
3238c2ecf20Sopenharmony_ci			if (PTR_ERR(hinfo) == -EAGAIN)
3248c2ecf20Sopenharmony_ci				continue;
3258c2ecf20Sopenharmony_ci			ret = PTR_ERR(hinfo);
3268c2ecf20Sopenharmony_ci			goto done;
3278c2ecf20Sopenharmony_ci		} else if (!hinfo) {
3288c2ecf20Sopenharmony_ci			break;
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		ret = __seg6_genl_dumphmac_element(hinfo,
3328c2ecf20Sopenharmony_ci						   NETLINK_CB(cb->skb).portid,
3338c2ecf20Sopenharmony_ci						   cb->nlh->nlmsg_seq,
3348c2ecf20Sopenharmony_ci						   NLM_F_MULTI,
3358c2ecf20Sopenharmony_ci						   skb, SEG6_CMD_DUMPHMAC);
3368c2ecf20Sopenharmony_ci		if (ret)
3378c2ecf20Sopenharmony_ci			goto done;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ret = skb->len;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cidone:
3438c2ecf20Sopenharmony_ci	rhashtable_walk_stop(iter);
3448c2ecf20Sopenharmony_ci	return ret;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci#else
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_start(struct netlink_callback *cb)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	return 0;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac_done(struct netlink_callback *cb)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	return -ENOTSUPP;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci#endif
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int __net_init seg6_net_init(struct net *net)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct seg6_pernet_data *sdata;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	sdata = kzalloc(sizeof(*sdata), GFP_KERNEL);
3718c2ecf20Sopenharmony_ci	if (!sdata)
3728c2ecf20Sopenharmony_ci		return -ENOMEM;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	mutex_init(&sdata->lock);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL);
3778c2ecf20Sopenharmony_ci	if (!sdata->tun_src) {
3788c2ecf20Sopenharmony_ci		kfree(sdata);
3798c2ecf20Sopenharmony_ci		return -ENOMEM;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	net->ipv6.seg6_data = sdata;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
3858c2ecf20Sopenharmony_ci	seg6_hmac_net_init(net);
3868c2ecf20Sopenharmony_ci#endif
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void __net_exit seg6_net_exit(struct net *net)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct seg6_pernet_data *sdata = seg6_pernet(net);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
3968c2ecf20Sopenharmony_ci	seg6_hmac_net_exit(net);
3978c2ecf20Sopenharmony_ci#endif
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	kfree(sdata->tun_src);
4008c2ecf20Sopenharmony_ci	kfree(sdata);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic struct pernet_operations ip6_segments_ops = {
4048c2ecf20Sopenharmony_ci	.init = seg6_net_init,
4058c2ecf20Sopenharmony_ci	.exit = seg6_net_exit,
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic const struct genl_ops seg6_genl_ops[] = {
4098c2ecf20Sopenharmony_ci	{
4108c2ecf20Sopenharmony_ci		.cmd	= SEG6_CMD_SETHMAC,
4118c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
4128c2ecf20Sopenharmony_ci		.doit	= seg6_genl_sethmac,
4138c2ecf20Sopenharmony_ci		.flags	= GENL_ADMIN_PERM,
4148c2ecf20Sopenharmony_ci	},
4158c2ecf20Sopenharmony_ci	{
4168c2ecf20Sopenharmony_ci		.cmd	= SEG6_CMD_DUMPHMAC,
4178c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
4188c2ecf20Sopenharmony_ci		.start	= seg6_genl_dumphmac_start,
4198c2ecf20Sopenharmony_ci		.dumpit	= seg6_genl_dumphmac,
4208c2ecf20Sopenharmony_ci		.done	= seg6_genl_dumphmac_done,
4218c2ecf20Sopenharmony_ci		.flags	= GENL_ADMIN_PERM,
4228c2ecf20Sopenharmony_ci	},
4238c2ecf20Sopenharmony_ci	{
4248c2ecf20Sopenharmony_ci		.cmd	= SEG6_CMD_SET_TUNSRC,
4258c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
4268c2ecf20Sopenharmony_ci		.doit	= seg6_genl_set_tunsrc,
4278c2ecf20Sopenharmony_ci		.flags	= GENL_ADMIN_PERM,
4288c2ecf20Sopenharmony_ci	},
4298c2ecf20Sopenharmony_ci	{
4308c2ecf20Sopenharmony_ci		.cmd	= SEG6_CMD_GET_TUNSRC,
4318c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
4328c2ecf20Sopenharmony_ci		.doit	= seg6_genl_get_tunsrc,
4338c2ecf20Sopenharmony_ci		.flags	= GENL_ADMIN_PERM,
4348c2ecf20Sopenharmony_ci	},
4358c2ecf20Sopenharmony_ci};
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic struct genl_family seg6_genl_family __ro_after_init = {
4388c2ecf20Sopenharmony_ci	.hdrsize	= 0,
4398c2ecf20Sopenharmony_ci	.name		= SEG6_GENL_NAME,
4408c2ecf20Sopenharmony_ci	.version	= SEG6_GENL_VERSION,
4418c2ecf20Sopenharmony_ci	.maxattr	= SEG6_ATTR_MAX,
4428c2ecf20Sopenharmony_ci	.policy = seg6_genl_policy,
4438c2ecf20Sopenharmony_ci	.netnsok	= true,
4448c2ecf20Sopenharmony_ci	.parallel_ops	= true,
4458c2ecf20Sopenharmony_ci	.ops		= seg6_genl_ops,
4468c2ecf20Sopenharmony_ci	.n_ops		= ARRAY_SIZE(seg6_genl_ops),
4478c2ecf20Sopenharmony_ci	.module		= THIS_MODULE,
4488c2ecf20Sopenharmony_ci};
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ciint __init seg6_init(void)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	int err;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	err = register_pernet_subsys(&ip6_segments_ops);
4558c2ecf20Sopenharmony_ci	if (err)
4568c2ecf20Sopenharmony_ci		goto out;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	err = genl_register_family(&seg6_genl_family);
4598c2ecf20Sopenharmony_ci	if (err)
4608c2ecf20Sopenharmony_ci		goto out_unregister_pernet;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
4638c2ecf20Sopenharmony_ci	err = seg6_iptunnel_init();
4648c2ecf20Sopenharmony_ci	if (err)
4658c2ecf20Sopenharmony_ci		goto out_unregister_genl;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	err = seg6_local_init();
4688c2ecf20Sopenharmony_ci	if (err) {
4698c2ecf20Sopenharmony_ci		seg6_iptunnel_exit();
4708c2ecf20Sopenharmony_ci		goto out_unregister_genl;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci#endif
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
4758c2ecf20Sopenharmony_ci	err = seg6_hmac_init();
4768c2ecf20Sopenharmony_ci	if (err)
4778c2ecf20Sopenharmony_ci		goto out_unregister_iptun;
4788c2ecf20Sopenharmony_ci#endif
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	pr_info("Segment Routing with IPv6\n");
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciout:
4838c2ecf20Sopenharmony_ci	return err;
4848c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
4858c2ecf20Sopenharmony_ciout_unregister_iptun:
4868c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
4878c2ecf20Sopenharmony_ci	seg6_local_exit();
4888c2ecf20Sopenharmony_ci	seg6_iptunnel_exit();
4898c2ecf20Sopenharmony_ci#endif
4908c2ecf20Sopenharmony_ci#endif
4918c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
4928c2ecf20Sopenharmony_ciout_unregister_genl:
4938c2ecf20Sopenharmony_ci	genl_unregister_family(&seg6_genl_family);
4948c2ecf20Sopenharmony_ci#endif
4958c2ecf20Sopenharmony_ciout_unregister_pernet:
4968c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6_segments_ops);
4978c2ecf20Sopenharmony_ci	goto out;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_civoid seg6_exit(void)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
5038c2ecf20Sopenharmony_ci	seg6_hmac_exit();
5048c2ecf20Sopenharmony_ci#endif
5058c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
5068c2ecf20Sopenharmony_ci	seg6_iptunnel_exit();
5078c2ecf20Sopenharmony_ci#endif
5088c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ip6_segments_ops);
5098c2ecf20Sopenharmony_ci	genl_unregister_family(&seg6_genl_family);
5108c2ecf20Sopenharmony_ci}
511