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