]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/ndisc-router.c
a6a9ac8a9f2d6b6bf2fc4f0fd4fa2304f9b42772
[thirdparty/systemd.git] / src / libsystemd-network / ndisc-router.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright (C) 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <netinet/icmp6.h>
7
8 #include "sd-ndisc.h"
9
10 #include "alloc-util.h"
11 #include "dns-domain.h"
12 #include "hostname-util.h"
13 #include "missing.h"
14 #include "ndisc-internal.h"
15 #include "ndisc-router.h"
16 #include "strv.h"
17
18 _public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) {
19 if (!rt)
20 return NULL;
21
22 assert(rt->n_ref > 0);
23 rt->n_ref++;
24
25 return rt;
26 }
27
28 _public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) {
29 if (!rt)
30 return NULL;
31
32 assert(rt->n_ref > 0);
33 rt->n_ref--;
34
35 if (rt->n_ref > 0)
36 return NULL;
37
38 return mfree(rt);
39 }
40
41 sd_ndisc_router *ndisc_router_new(size_t raw_size) {
42 sd_ndisc_router *rt;
43
44 rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
45 if (!rt)
46 return NULL;
47
48 rt->raw_size = raw_size;
49 rt->n_ref = 1;
50
51 return rt;
52 }
53
54 _public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) {
55 _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
56 int r;
57
58 assert_return(ret, -EINVAL);
59 assert_return(raw || raw_size <= 0, -EINVAL);
60
61 rt = ndisc_router_new(raw_size);
62 if (!rt)
63 return -ENOMEM;
64
65 memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size);
66 r = ndisc_router_parse(rt);
67 if (r < 0)
68 return r;
69
70 *ret = TAKE_PTR(rt);
71
72 return r;
73 }
74
75 _public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
76 assert_return(rt, -EINVAL);
77 assert_return(ret_addr, -EINVAL);
78
79 if (IN6_IS_ADDR_UNSPECIFIED(&rt->address))
80 return -ENODATA;
81
82 *ret_addr = rt->address;
83 return 0;
84 }
85
86 _public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
87 assert_return(rt, -EINVAL);
88 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
89 assert_return(clock_supported(clock), -EOPNOTSUPP);
90 assert_return(ret, -EINVAL);
91
92 if (!triple_timestamp_is_set(&rt->timestamp))
93 return -ENODATA;
94
95 *ret = triple_timestamp_by_clock(&rt->timestamp, clock);
96 return 0;
97 }
98
99 _public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
100 assert_return(rt, -EINVAL);
101 assert_return(ret, -EINVAL);
102 assert_return(size, -EINVAL);
103
104 *ret = NDISC_ROUTER_RAW(rt);
105 *size = rt->raw_size;
106
107 return 0;
108 }
109
110 int ndisc_router_parse(sd_ndisc_router *rt) {
111 struct nd_router_advert *a;
112 const uint8_t *p;
113 bool has_mtu = false, has_flag_extension = false;
114 size_t left;
115
116 assert(rt);
117
118 if (rt->raw_size < sizeof(struct nd_router_advert)) {
119 log_ndisc("Too small to be a router advertisement, ignoring.");
120 return -EBADMSG;
121 }
122
123 /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
124 a = NDISC_ROUTER_RAW(rt);
125
126 if (a->nd_ra_type != ND_ROUTER_ADVERT) {
127 log_ndisc("Received ND packet that is not a router advertisement, ignoring.");
128 return -EBADMSG;
129 }
130
131 if (a->nd_ra_code != 0) {
132 log_ndisc("Received ND packet with wrong RA code, ignoring.");
133 return -EBADMSG;
134 }
135
136 rt->hop_limit = a->nd_ra_curhoplimit;
137 rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */
138 rt->lifetime = be16toh(a->nd_ra_router_lifetime);
139
140 rt->preference = (rt->flags >> 3) & 3;
141 if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
142 rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
143
144 p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
145 left = rt->raw_size - sizeof(struct nd_router_advert);
146
147 for (;;) {
148 uint8_t type;
149 size_t length;
150
151 if (left == 0)
152 break;
153
154 if (left < 2) {
155 log_ndisc("Option lacks header, ignoring datagram.");
156 return -EBADMSG;
157 }
158
159 type = p[0];
160 length = p[1] * 8;
161
162 if (length == 0) {
163 log_ndisc("Zero-length option, ignoring datagram.");
164 return -EBADMSG;
165 }
166 if (left < length) {
167 log_ndisc("Option truncated, ignoring datagram.");
168 return -EBADMSG;
169 }
170
171 switch (type) {
172
173 case SD_NDISC_OPTION_PREFIX_INFORMATION:
174
175 if (length != 4*8) {
176 log_ndisc("Prefix option of invalid size, ignoring datagram.");
177 return -EBADMSG;
178 }
179
180 if (p[2] > 128) {
181 log_ndisc("Bad prefix length, ignoring datagram.");
182 return -EBADMSG;
183 }
184
185 break;
186
187 case SD_NDISC_OPTION_MTU: {
188 uint32_t m;
189
190 if (has_mtu) {
191 log_ndisc("MTU option specified twice, ignoring.");
192 continue;
193 }
194
195 if (length != 8) {
196 log_ndisc("MTU option of invalid size, ignoring datagram.");
197 return -EBADMSG;
198 }
199
200 m = be32toh(*(uint32_t*) (p + 4));
201 if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
202 rt->mtu = m;
203
204 has_mtu = true;
205 break;
206 }
207
208 case SD_NDISC_OPTION_ROUTE_INFORMATION:
209 if (length < 1*8 || length > 3*8) {
210 log_ndisc("Route information option of invalid size, ignoring datagram.");
211 return -EBADMSG;
212 }
213
214 if (p[2] > 128) {
215 log_ndisc("Bad route prefix length, ignoring datagram.");
216 return -EBADMSG;
217 }
218
219 break;
220
221 case SD_NDISC_OPTION_RDNSS:
222 if (length < 3*8 || (length % (2*8)) != 1*8) {
223 log_ndisc("RDNSS option has invalid size.");
224 return -EBADMSG;
225 }
226
227 break;
228
229 case SD_NDISC_OPTION_FLAGS_EXTENSION:
230
231 if (has_flag_extension) {
232 log_ndisc("Flags extension option specified twice, ignoring.");
233 continue;
234 }
235
236 if (length < 1*8) {
237 log_ndisc("Flags extension option has invalid size.");
238 return -EBADMSG;
239 }
240
241 /* Add in the additional flags bits */
242 rt->flags |=
243 ((uint64_t) p[2] << 8) |
244 ((uint64_t) p[3] << 16) |
245 ((uint64_t) p[4] << 24) |
246 ((uint64_t) p[5] << 32) |
247 ((uint64_t) p[6] << 40) |
248 ((uint64_t) p[7] << 48);
249
250 has_flag_extension = true;
251 break;
252
253 case SD_NDISC_OPTION_DNSSL:
254 if (length < 2*8) {
255 log_ndisc("DNSSL option has invalid size.");
256 return -EBADMSG;
257 }
258
259 break;
260 }
261
262 p += length, left -= length;
263 }
264
265 rt->rindex = sizeof(struct nd_router_advert);
266 return 0;
267 }
268
269 _public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
270 assert_return(rt, -EINVAL);
271 assert_return(ret, -EINVAL);
272
273 *ret = rt->hop_limit;
274 return 0;
275 }
276
277 _public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) {
278 assert_return(rt, -EINVAL);
279 assert_return(ret_flags, -EINVAL);
280
281 *ret_flags = rt->flags;
282 return 0;
283 }
284
285 _public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) {
286 assert_return(rt, -EINVAL);
287 assert_return(ret_lifetime, -EINVAL);
288
289 *ret_lifetime = rt->lifetime;
290 return 0;
291 }
292
293 _public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
294 assert_return(rt, -EINVAL);
295 assert_return(ret, -EINVAL);
296
297 *ret = rt->preference;
298 return 0;
299 }
300
301 _public_ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
302 assert_return(rt, -EINVAL);
303 assert_return(ret, -EINVAL);
304
305 if (rt->mtu <= 0)
306 return -ENODATA;
307
308 *ret = rt->mtu;
309 return 0;
310 }
311
312 _public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
313 assert_return(rt, -EINVAL);
314
315 assert(rt->raw_size >= sizeof(struct nd_router_advert));
316 rt->rindex = sizeof(struct nd_router_advert);
317
318 return rt->rindex < rt->raw_size;
319 }
320
321 _public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
322 size_t length;
323
324 assert_return(rt, -EINVAL);
325
326 if (rt->rindex == rt->raw_size) /* EOF */
327 return -ESPIPE;
328
329 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
330 return -EBADMSG;
331
332 length = NDISC_ROUTER_OPTION_LENGTH(rt);
333 if (rt->rindex + length > rt->raw_size)
334 return -EBADMSG;
335
336 rt->rindex += length;
337 return rt->rindex < rt->raw_size;
338 }
339
340 _public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
341 assert_return(rt, -EINVAL);
342 assert_return(ret, -EINVAL);
343
344 if (rt->rindex == rt->raw_size) /* EOF */
345 return -ESPIPE;
346
347 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
348 return -EBADMSG;
349
350 *ret = NDISC_ROUTER_OPTION_TYPE(rt);
351 return 0;
352 }
353
354 _public_ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
355 uint8_t k;
356 int r;
357
358 assert_return(rt, -EINVAL);
359
360 r = sd_ndisc_router_option_get_type(rt, &k);
361 if (r < 0)
362 return r;
363
364 return type == k;
365 }
366
367 _public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
368 size_t length;
369
370 assert_return(rt, -EINVAL);
371 assert_return(ret, -EINVAL);
372 assert_return(size, -EINVAL);
373
374 /* Note that this returns the full option, including the option header */
375
376 if (rt->rindex + 2 > rt->raw_size)
377 return -EBADMSG;
378
379 length = NDISC_ROUTER_OPTION_LENGTH(rt);
380 if (rt->rindex + length > rt->raw_size)
381 return -EBADMSG;
382
383 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
384 *size = length;
385
386 return 0;
387 }
388
389 static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
390 struct nd_opt_prefix_info *ri;
391 size_t length;
392 int r;
393
394 assert(rt);
395 assert(ret);
396
397 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
398 if (r < 0)
399 return r;
400 if (r == 0)
401 return -EMEDIUMTYPE;
402
403 length = NDISC_ROUTER_OPTION_LENGTH(rt);
404 if (length != sizeof(struct nd_opt_prefix_info))
405 return -EBADMSG;
406
407 ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
408 if (ri->nd_opt_pi_prefix_len > 128)
409 return -EBADMSG;
410
411 *ret = ri;
412 return 0;
413 }
414
415 _public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
416 struct nd_opt_prefix_info *ri;
417 int r;
418
419 assert_return(rt, -EINVAL);
420 assert_return(ret, -EINVAL);
421
422 r = get_prefix_info(rt, &ri);
423 if (r < 0)
424 return r;
425
426 *ret = be32toh(ri->nd_opt_pi_valid_time);
427 return 0;
428 }
429
430 _public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
431 struct nd_opt_prefix_info *pi;
432 int r;
433
434 assert_return(rt, -EINVAL);
435 assert_return(ret, -EINVAL);
436
437 r = get_prefix_info(rt, &pi);
438 if (r < 0)
439 return r;
440
441 *ret = be32toh(pi->nd_opt_pi_preferred_time);
442 return 0;
443 }
444
445 _public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
446 struct nd_opt_prefix_info *pi;
447 uint8_t flags;
448 int r;
449
450 assert_return(rt, -EINVAL);
451 assert_return(ret, -EINVAL);
452
453 r = get_prefix_info(rt, &pi);
454 if (r < 0)
455 return r;
456
457 flags = pi->nd_opt_pi_flags_reserved;
458
459 if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) {
460 log_ndisc("Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
461 flags &= ~ND_OPT_PI_FLAG_AUTO;
462 }
463
464 *ret = flags;
465 return 0;
466 }
467
468 _public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
469 struct nd_opt_prefix_info *pi;
470 int r;
471
472 assert_return(rt, -EINVAL);
473 assert_return(ret_addr, -EINVAL);
474
475 r = get_prefix_info(rt, &pi);
476 if (r < 0)
477 return r;
478
479 *ret_addr = pi->nd_opt_pi_prefix;
480 return 0;
481 }
482
483 _public_ int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
484 struct nd_opt_prefix_info *pi;
485 int r;
486
487 assert_return(rt, -EINVAL);
488 assert_return(ret, -EINVAL);
489
490 r = get_prefix_info(rt, &pi);
491 if (r < 0)
492 return r;
493
494 if (pi->nd_opt_pi_prefix_len > 128)
495 return -EBADMSG;
496
497 *ret = pi->nd_opt_pi_prefix_len;
498 return 0;
499 }
500
501 static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
502 uint8_t *ri;
503 size_t length;
504 int r;
505
506 assert(rt);
507 assert(ret);
508
509 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
510 if (r < 0)
511 return r;
512 if (r == 0)
513 return -EMEDIUMTYPE;
514
515 length = NDISC_ROUTER_OPTION_LENGTH(rt);
516 if (length < 1*8 || length > 3*8)
517 return -EBADMSG;
518
519 ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
520
521 if (ri[2] > 128)
522 return -EBADMSG;
523
524 *ret = ri;
525 return 0;
526 }
527
528 _public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
529 uint8_t *ri;
530 int r;
531
532 assert_return(rt, -EINVAL);
533 assert_return(ret, -EINVAL);
534
535 r = get_route_info(rt, &ri);
536 if (r < 0)
537 return r;
538
539 *ret = be32toh(*(uint32_t*) (ri + 4));
540 return 0;
541 }
542
543 _public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
544 uint8_t *ri;
545 int r;
546
547 assert_return(rt, -EINVAL);
548 assert_return(ret_addr, -EINVAL);
549
550 r = get_route_info(rt, &ri);
551 if (r < 0)
552 return r;
553
554 zero(*ret_addr);
555 memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
556
557 return 0;
558 }
559
560 _public_ int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
561 uint8_t *ri;
562 int r;
563
564 assert_return(rt, -EINVAL);
565 assert_return(ret, -EINVAL);
566
567 r = get_route_info(rt, &ri);
568 if (r < 0)
569 return r;
570
571 *ret = ri[2];
572 return 0;
573 }
574
575 _public_ int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
576 uint8_t *ri;
577 int r;
578
579 assert_return(rt, -EINVAL);
580 assert_return(ret, -EINVAL);
581
582 r = get_route_info(rt, &ri);
583 if (r < 0)
584 return r;
585
586 *ret = (ri[3] >> 3) & 3;
587 if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
588 *ret = SD_NDISC_PREFERENCE_MEDIUM;
589
590 return 0;
591 }
592
593 static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
594 size_t length;
595 int r;
596
597 assert(rt);
598 assert(ret);
599
600 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
601 if (r < 0)
602 return r;
603 if (r == 0)
604 return -EMEDIUMTYPE;
605
606 length = NDISC_ROUTER_OPTION_LENGTH(rt);
607 if (length < 3*8 || (length % (2*8)) != 1*8)
608 return -EBADMSG;
609
610 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
611 return 0;
612 }
613
614 _public_ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
615 uint8_t *ri;
616 int r;
617
618 assert_return(rt, -EINVAL);
619 assert_return(ret, -EINVAL);
620
621 r = get_rdnss_info(rt, &ri);
622 if (r < 0)
623 return r;
624
625 *ret = (const struct in6_addr*) (ri + 8);
626 return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
627 }
628
629 _public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
630 uint8_t *ri;
631 int r;
632
633 assert_return(rt, -EINVAL);
634 assert_return(ret, -EINVAL);
635
636 r = get_rdnss_info(rt, &ri);
637 if (r < 0)
638 return r;
639
640 *ret = be32toh(*(uint32_t*) (ri + 4));
641 return 0;
642 }
643
644 static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
645 size_t length;
646 int r;
647
648 assert(rt);
649 assert(ret);
650
651 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
652 if (r < 0)
653 return r;
654 if (r == 0)
655 return -EMEDIUMTYPE;
656
657 length = NDISC_ROUTER_OPTION_LENGTH(rt);
658 if (length < 2*8)
659 return -EBADMSG;
660
661 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
662 return 0;
663 }
664
665 _public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
666 _cleanup_strv_free_ char **l = NULL;
667 _cleanup_free_ char *e = NULL;
668 size_t allocated = 0, n = 0, left;
669 uint8_t *ri, *p;
670 bool first = true;
671 int r;
672 unsigned k = 0;
673
674 assert_return(rt, -EINVAL);
675 assert_return(ret, -EINVAL);
676
677 r = get_dnssl_info(rt, &ri);
678 if (r < 0)
679 return r;
680
681 p = ri + 8;
682 left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
683
684 for (;;) {
685 if (left == 0) {
686
687 if (n > 0) /* Not properly NUL terminated */
688 return -EBADMSG;
689
690 break;
691 }
692
693 if (*p == 0) {
694 /* Found NUL termination */
695
696 if (n > 0) {
697 _cleanup_free_ char *normalized = NULL;
698
699 e[n] = 0;
700 r = dns_name_normalize(e, &normalized);
701 if (r < 0)
702 return r;
703
704 /* Ignore the root domain name or "localhost" and friends */
705 if (!is_localhost(normalized) &&
706 !dns_name_is_root(normalized)) {
707
708 if (strv_push(&l, normalized) < 0)
709 return -ENOMEM;
710
711 normalized = NULL;
712 k++;
713 }
714 }
715
716 n = 0;
717 first = true;
718 p++, left--;
719 continue;
720 }
721
722 /* Check for compression (which is not allowed) */
723 if (*p > 63)
724 return -EBADMSG;
725
726 if (1U + *p + 1U > left)
727 return -EBADMSG;
728
729 if (!GREEDY_REALLOC(e, allocated, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
730 return -ENOMEM;
731
732 if (first)
733 first = false;
734 else
735 e[n++] = '.';
736
737 r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
738 if (r < 0)
739 return r;
740
741 n += r;
742
743 left -= 1 + *p;
744 p += 1 + *p;
745 }
746
747 if (strv_isempty(l)) {
748 *ret = NULL;
749 return 0;
750 }
751
752 *ret = TAKE_PTR(l);
753
754 return k;
755 }
756
757 _public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) {
758 uint8_t *ri;
759 int r;
760
761 assert_return(rt, -EINVAL);
762 assert_return(ret_sec, -EINVAL);
763
764 r = get_dnssl_info(rt, &ri);
765 if (r < 0)
766 return r;
767
768 *ret_sec = be32toh(*(uint32_t*) (ri + 4));
769 return 0;
770 }