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