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