cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

seg6.c (11761B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  SR-IPv6 implementation
      4 *
      5 *  Author:
      6 *  David Lebrun <david.lebrun@uclouvain.be>
      7 */
      8
      9#include <linux/errno.h>
     10#include <linux/types.h>
     11#include <linux/socket.h>
     12#include <linux/net.h>
     13#include <linux/in6.h>
     14#include <linux/slab.h>
     15#include <linux/rhashtable.h>
     16
     17#include <net/ipv6.h>
     18#include <net/protocol.h>
     19
     20#include <net/seg6.h>
     21#include <net/genetlink.h>
     22#include <linux/seg6.h>
     23#include <linux/seg6_genl.h>
     24#ifdef CONFIG_IPV6_SEG6_HMAC
     25#include <net/seg6_hmac.h>
     26#endif
     27
     28bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced)
     29{
     30	unsigned int tlv_offset;
     31	int max_last_entry;
     32	int trailing;
     33
     34	if (srh->type != IPV6_SRCRT_TYPE_4)
     35		return false;
     36
     37	if (((srh->hdrlen + 1) << 3) != len)
     38		return false;
     39
     40	if (!reduced && srh->segments_left > srh->first_segment) {
     41		return false;
     42	} else {
     43		max_last_entry = (srh->hdrlen / 2) - 1;
     44
     45		if (srh->first_segment > max_last_entry)
     46			return false;
     47
     48		if (srh->segments_left > srh->first_segment + 1)
     49			return false;
     50	}
     51
     52	tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
     53
     54	trailing = len - tlv_offset;
     55	if (trailing < 0)
     56		return false;
     57
     58	while (trailing) {
     59		struct sr6_tlv *tlv;
     60		unsigned int tlv_len;
     61
     62		if (trailing < sizeof(*tlv))
     63			return false;
     64
     65		tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset);
     66		tlv_len = sizeof(*tlv) + tlv->len;
     67
     68		trailing -= tlv_len;
     69		if (trailing < 0)
     70			return false;
     71
     72		tlv_offset += tlv_len;
     73	}
     74
     75	return true;
     76}
     77
     78struct ipv6_sr_hdr *seg6_get_srh(struct sk_buff *skb, int flags)
     79{
     80	struct ipv6_sr_hdr *srh;
     81	int len, srhoff = 0;
     82
     83	if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0)
     84		return NULL;
     85
     86	if (!pskb_may_pull(skb, srhoff + sizeof(*srh)))
     87		return NULL;
     88
     89	srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
     90
     91	len = (srh->hdrlen + 1) << 3;
     92
     93	if (!pskb_may_pull(skb, srhoff + len))
     94		return NULL;
     95
     96	/* note that pskb_may_pull may change pointers in header;
     97	 * for this reason it is necessary to reload them when needed.
     98	 */
     99	srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
    100
    101	if (!seg6_validate_srh(srh, len, true))
    102		return NULL;
    103
    104	return srh;
    105}
    106
    107/* Determine if an ICMP invoking packet contains a segment routing
    108 * header.  If it does, extract the offset to the true destination
    109 * address, which is in the first segment address.
    110 */
    111void seg6_icmp_srh(struct sk_buff *skb, struct inet6_skb_parm *opt)
    112{
    113	__u16 network_header = skb->network_header;
    114	struct ipv6_sr_hdr *srh;
    115
    116	/* Update network header to point to the invoking packet
    117	 * inside the ICMP packet, so we can use the seg6_get_srh()
    118	 * helper.
    119	 */
    120	skb_reset_network_header(skb);
    121
    122	srh = seg6_get_srh(skb, 0);
    123	if (!srh)
    124		goto out;
    125
    126	if (srh->type != IPV6_SRCRT_TYPE_4)
    127		goto out;
    128
    129	opt->flags |= IP6SKB_SEG6;
    130	opt->srhoff = (unsigned char *)srh - skb->data;
    131
    132out:
    133	/* Restore the network header back to the ICMP packet */
    134	skb->network_header = network_header;
    135}
    136
    137static struct genl_family seg6_genl_family;
    138
    139static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
    140	[SEG6_ATTR_DST]				= { .type = NLA_BINARY,
    141		.len = sizeof(struct in6_addr) },
    142	[SEG6_ATTR_DSTLEN]			= { .type = NLA_S32, },
    143	[SEG6_ATTR_HMACKEYID]		= { .type = NLA_U32, },
    144	[SEG6_ATTR_SECRET]			= { .type = NLA_BINARY, },
    145	[SEG6_ATTR_SECRETLEN]		= { .type = NLA_U8, },
    146	[SEG6_ATTR_ALGID]			= { .type = NLA_U8, },
    147	[SEG6_ATTR_HMACINFO]		= { .type = NLA_NESTED, },
    148};
    149
    150#ifdef CONFIG_IPV6_SEG6_HMAC
    151
    152static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
    153{
    154	struct net *net = genl_info_net(info);
    155	struct seg6_pernet_data *sdata;
    156	struct seg6_hmac_info *hinfo;
    157	u32 hmackeyid;
    158	char *secret;
    159	int err = 0;
    160	u8 algid;
    161	u8 slen;
    162
    163	sdata = seg6_pernet(net);
    164
    165	if (!info->attrs[SEG6_ATTR_HMACKEYID] ||
    166	    !info->attrs[SEG6_ATTR_SECRETLEN] ||
    167	    !info->attrs[SEG6_ATTR_ALGID])
    168		return -EINVAL;
    169
    170	hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]);
    171	slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]);
    172	algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]);
    173
    174	if (hmackeyid == 0)
    175		return -EINVAL;
    176
    177	if (slen > SEG6_HMAC_SECRET_LEN)
    178		return -EINVAL;
    179
    180	mutex_lock(&sdata->lock);
    181	hinfo = seg6_hmac_info_lookup(net, hmackeyid);
    182
    183	if (!slen) {
    184		err = seg6_hmac_info_del(net, hmackeyid);
    185
    186		goto out_unlock;
    187	}
    188
    189	if (!info->attrs[SEG6_ATTR_SECRET]) {
    190		err = -EINVAL;
    191		goto out_unlock;
    192	}
    193
    194	if (hinfo) {
    195		err = seg6_hmac_info_del(net, hmackeyid);
    196		if (err)
    197			goto out_unlock;
    198	}
    199
    200	secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]);
    201
    202	hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL);
    203	if (!hinfo) {
    204		err = -ENOMEM;
    205		goto out_unlock;
    206	}
    207
    208	memcpy(hinfo->secret, secret, slen);
    209	hinfo->slen = slen;
    210	hinfo->alg_id = algid;
    211	hinfo->hmackeyid = hmackeyid;
    212
    213	err = seg6_hmac_info_add(net, hmackeyid, hinfo);
    214	if (err)
    215		kfree(hinfo);
    216
    217out_unlock:
    218	mutex_unlock(&sdata->lock);
    219	return err;
    220}
    221
    222#else
    223
    224static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
    225{
    226	return -ENOTSUPP;
    227}
    228
    229#endif
    230
    231static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
    232{
    233	struct net *net = genl_info_net(info);
    234	struct in6_addr *val, *t_old, *t_new;
    235	struct seg6_pernet_data *sdata;
    236
    237	sdata = seg6_pernet(net);
    238
    239	if (!info->attrs[SEG6_ATTR_DST])
    240		return -EINVAL;
    241
    242	val = nla_data(info->attrs[SEG6_ATTR_DST]);
    243	t_new = kmemdup(val, sizeof(*val), GFP_KERNEL);
    244	if (!t_new)
    245		return -ENOMEM;
    246
    247	mutex_lock(&sdata->lock);
    248
    249	t_old = sdata->tun_src;
    250	rcu_assign_pointer(sdata->tun_src, t_new);
    251
    252	mutex_unlock(&sdata->lock);
    253
    254	synchronize_net();
    255	kfree(t_old);
    256
    257	return 0;
    258}
    259
    260static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
    261{
    262	struct net *net = genl_info_net(info);
    263	struct in6_addr *tun_src;
    264	struct sk_buff *msg;
    265	void *hdr;
    266
    267	msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
    268	if (!msg)
    269		return -ENOMEM;
    270
    271	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
    272			  &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC);
    273	if (!hdr)
    274		goto free_msg;
    275
    276	rcu_read_lock();
    277	tun_src = rcu_dereference(seg6_pernet(net)->tun_src);
    278
    279	if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src))
    280		goto nla_put_failure;
    281
    282	rcu_read_unlock();
    283
    284	genlmsg_end(msg, hdr);
    285	return genlmsg_reply(msg, info);
    286
    287nla_put_failure:
    288	rcu_read_unlock();
    289free_msg:
    290	nlmsg_free(msg);
    291	return -ENOMEM;
    292}
    293
    294#ifdef CONFIG_IPV6_SEG6_HMAC
    295
    296static int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo,
    297				 struct sk_buff *msg)
    298{
    299	if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) ||
    300	    nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) ||
    301	    nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) ||
    302	    nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id))
    303		return -1;
    304
    305	return 0;
    306}
    307
    308static int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo,
    309					u32 portid, u32 seq, u32 flags,
    310					struct sk_buff *skb, u8 cmd)
    311{
    312	void *hdr;
    313
    314	hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd);
    315	if (!hdr)
    316		return -ENOMEM;
    317
    318	if (__seg6_hmac_fill_info(hinfo, skb) < 0)
    319		goto nla_put_failure;
    320
    321	genlmsg_end(skb, hdr);
    322	return 0;
    323
    324nla_put_failure:
    325	genlmsg_cancel(skb, hdr);
    326	return -EMSGSIZE;
    327}
    328
    329static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
    330{
    331	struct net *net = sock_net(cb->skb->sk);
    332	struct seg6_pernet_data *sdata;
    333	struct rhashtable_iter *iter;
    334
    335	sdata = seg6_pernet(net);
    336	iter = (struct rhashtable_iter *)cb->args[0];
    337
    338	if (!iter) {
    339		iter = kmalloc(sizeof(*iter), GFP_KERNEL);
    340		if (!iter)
    341			return -ENOMEM;
    342
    343		cb->args[0] = (long)iter;
    344	}
    345
    346	rhashtable_walk_enter(&sdata->hmac_infos, iter);
    347
    348	return 0;
    349}
    350
    351static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
    352{
    353	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
    354
    355	rhashtable_walk_exit(iter);
    356
    357	kfree(iter);
    358
    359	return 0;
    360}
    361
    362static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
    363{
    364	struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
    365	struct seg6_hmac_info *hinfo;
    366	int ret;
    367
    368	rhashtable_walk_start(iter);
    369
    370	for (;;) {
    371		hinfo = rhashtable_walk_next(iter);
    372
    373		if (IS_ERR(hinfo)) {
    374			if (PTR_ERR(hinfo) == -EAGAIN)
    375				continue;
    376			ret = PTR_ERR(hinfo);
    377			goto done;
    378		} else if (!hinfo) {
    379			break;
    380		}
    381
    382		ret = __seg6_genl_dumphmac_element(hinfo,
    383						   NETLINK_CB(cb->skb).portid,
    384						   cb->nlh->nlmsg_seq,
    385						   NLM_F_MULTI,
    386						   skb, SEG6_CMD_DUMPHMAC);
    387		if (ret)
    388			goto done;
    389	}
    390
    391	ret = skb->len;
    392
    393done:
    394	rhashtable_walk_stop(iter);
    395	return ret;
    396}
    397
    398#else
    399
    400static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
    401{
    402	return 0;
    403}
    404
    405static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
    406{
    407	return 0;
    408}
    409
    410static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
    411{
    412	return -ENOTSUPP;
    413}
    414
    415#endif
    416
    417static int __net_init seg6_net_init(struct net *net)
    418{
    419	struct seg6_pernet_data *sdata;
    420
    421	sdata = kzalloc(sizeof(*sdata), GFP_KERNEL);
    422	if (!sdata)
    423		return -ENOMEM;
    424
    425	mutex_init(&sdata->lock);
    426
    427	sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL);
    428	if (!sdata->tun_src) {
    429		kfree(sdata);
    430		return -ENOMEM;
    431	}
    432
    433	net->ipv6.seg6_data = sdata;
    434
    435#ifdef CONFIG_IPV6_SEG6_HMAC
    436	if (seg6_hmac_net_init(net)) {
    437		kfree(rcu_dereference_raw(sdata->tun_src));
    438		kfree(sdata);
    439		return -ENOMEM;
    440	}
    441#endif
    442
    443	return 0;
    444}
    445
    446static void __net_exit seg6_net_exit(struct net *net)
    447{
    448	struct seg6_pernet_data *sdata = seg6_pernet(net);
    449
    450#ifdef CONFIG_IPV6_SEG6_HMAC
    451	seg6_hmac_net_exit(net);
    452#endif
    453
    454	kfree(rcu_dereference_raw(sdata->tun_src));
    455	kfree(sdata);
    456}
    457
    458static struct pernet_operations ip6_segments_ops = {
    459	.init = seg6_net_init,
    460	.exit = seg6_net_exit,
    461};
    462
    463static const struct genl_ops seg6_genl_ops[] = {
    464	{
    465		.cmd	= SEG6_CMD_SETHMAC,
    466		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    467		.doit	= seg6_genl_sethmac,
    468		.flags	= GENL_ADMIN_PERM,
    469	},
    470	{
    471		.cmd	= SEG6_CMD_DUMPHMAC,
    472		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    473		.start	= seg6_genl_dumphmac_start,
    474		.dumpit	= seg6_genl_dumphmac,
    475		.done	= seg6_genl_dumphmac_done,
    476		.flags	= GENL_ADMIN_PERM,
    477	},
    478	{
    479		.cmd	= SEG6_CMD_SET_TUNSRC,
    480		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    481		.doit	= seg6_genl_set_tunsrc,
    482		.flags	= GENL_ADMIN_PERM,
    483	},
    484	{
    485		.cmd	= SEG6_CMD_GET_TUNSRC,
    486		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
    487		.doit	= seg6_genl_get_tunsrc,
    488		.flags	= GENL_ADMIN_PERM,
    489	},
    490};
    491
    492static struct genl_family seg6_genl_family __ro_after_init = {
    493	.hdrsize	= 0,
    494	.name		= SEG6_GENL_NAME,
    495	.version	= SEG6_GENL_VERSION,
    496	.maxattr	= SEG6_ATTR_MAX,
    497	.policy = seg6_genl_policy,
    498	.netnsok	= true,
    499	.parallel_ops	= true,
    500	.ops		= seg6_genl_ops,
    501	.n_ops		= ARRAY_SIZE(seg6_genl_ops),
    502	.module		= THIS_MODULE,
    503};
    504
    505int __init seg6_init(void)
    506{
    507	int err;
    508
    509	err = genl_register_family(&seg6_genl_family);
    510	if (err)
    511		goto out;
    512
    513	err = register_pernet_subsys(&ip6_segments_ops);
    514	if (err)
    515		goto out_unregister_genl;
    516
    517#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
    518	err = seg6_iptunnel_init();
    519	if (err)
    520		goto out_unregister_pernet;
    521
    522	err = seg6_local_init();
    523	if (err)
    524		goto out_unregister_pernet;
    525#endif
    526
    527#ifdef CONFIG_IPV6_SEG6_HMAC
    528	err = seg6_hmac_init();
    529	if (err)
    530		goto out_unregister_iptun;
    531#endif
    532
    533	pr_info("Segment Routing with IPv6\n");
    534
    535out:
    536	return err;
    537#ifdef CONFIG_IPV6_SEG6_HMAC
    538out_unregister_iptun:
    539#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
    540	seg6_local_exit();
    541	seg6_iptunnel_exit();
    542#endif
    543#endif
    544#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
    545out_unregister_pernet:
    546	unregister_pernet_subsys(&ip6_segments_ops);
    547#endif
    548out_unregister_genl:
    549	genl_unregister_family(&seg6_genl_family);
    550	goto out;
    551}
    552
    553void seg6_exit(void)
    554{
    555#ifdef CONFIG_IPV6_SEG6_HMAC
    556	seg6_hmac_exit();
    557#endif
    558#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
    559	seg6_iptunnel_exit();
    560#endif
    561	unregister_pernet_subsys(&ip6_segments_ops);
    562	genl_unregister_family(&seg6_genl_family);
    563}