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