]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-ndisc-router.c
Merge pull request #31444 from bluca/semaphore
[thirdparty/systemd.git] / src / libsystemd-network / sd-ndisc-router.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
1e7a0e21 2/***
810adae9 3 Copyright © 2014 Intel Corporation. All rights reserved.
1e7a0e21
LP
4***/
5
6#include <netinet/icmp6.h>
7
8#include "sd-ndisc.h"
9
10#include "alloc-util.h"
11#include "dns-domain.h"
6244184e 12#include "escape.h"
1e7a0e21 13#include "hostname-util.h"
0a970718 14#include "memory-util.h"
f5947a5e 15#include "missing_network.h"
1e7a0e21 16#include "ndisc-internal.h"
6e8f5e4c 17#include "ndisc-protocol.h"
ca34b434 18#include "ndisc-router-internal.h"
1e7a0e21
LP
19#include "strv.h"
20
8301aa0b 21DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, mfree);
1e7a0e21
LP
22
23sd_ndisc_router *ndisc_router_new(size_t raw_size) {
24 sd_ndisc_router *rt;
25
4e88a46b
YW
26 if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_ndisc_router)))
27 return NULL;
28
1e7a0e21
LP
29 rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
30 if (!rt)
31 return NULL;
32
33 rt->raw_size = raw_size;
34 rt->n_ref = 1;
35
36 return rt;
37}
38
3231f624 39int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
1e7a0e21 40 assert_return(rt, -EINVAL);
3231f624 41 assert_return(ret, -EINVAL);
1e7a0e21 42
94876904 43 if (in6_addr_is_null(&rt->address))
1e7a0e21
LP
44 return -ENODATA;
45
3231f624 46 *ret = rt->address;
1e7a0e21
LP
47 return 0;
48}
49
17347053 50int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
1e7a0e21
LP
51 assert_return(rt, -EINVAL);
52 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
53 assert_return(clock_supported(clock), -EOPNOTSUPP);
54 assert_return(ret, -EINVAL);
55
56 if (!triple_timestamp_is_set(&rt->timestamp))
57 return -ENODATA;
58
59 *ret = triple_timestamp_by_clock(&rt->timestamp, clock);
60 return 0;
61}
62
6197db53
YW
63#define DEFINE_GET_TIMESTAMP(name) \
64 int sd_ndisc_router_##name##_timestamp( \
65 sd_ndisc_router *rt, \
66 clockid_t clock, \
67 uint64_t *ret) { \
68 \
69 usec_t s, t; \
70 int r; \
71 \
72 assert_return(rt, -EINVAL); \
73 assert_return(ret, -EINVAL); \
74 \
75 r = sd_ndisc_router_##name(rt, &s); \
76 if (r < 0) \
77 return r; \
78 \
79 r = sd_ndisc_router_get_timestamp(rt, clock, &t); \
80 if (r < 0) \
81 return r; \
82 \
83 *ret = time_span_to_stamp(s, t); \
84 return 0; \
85 }
86
87DEFINE_GET_TIMESTAMP(get_lifetime);
88DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime);
89DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime);
90DEFINE_GET_TIMESTAMP(route_get_lifetime);
91DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
92DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
93DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
94
3231f624 95int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) {
1e7a0e21
LP
96 assert_return(rt, -EINVAL);
97 assert_return(ret, -EINVAL);
3231f624 98 assert_return(ret_size, -EINVAL);
1e7a0e21
LP
99
100 *ret = NDISC_ROUTER_RAW(rt);
3231f624 101 *ret_size = rt->raw_size;
1e7a0e21
LP
102
103 return 0;
104}
105
6e8f5e4c
SS
106static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t length) {
107 uint16_t lifetime_and_plc;
108
109 assert(p);
110
111 if (length != sizeof(struct nd_opt_prefix64_info))
112 return false;
113
114 lifetime_and_plc = be16toh(p->lifetime_and_plc);
115 if (pref64_plc_to_prefix_length(lifetime_and_plc, NULL) < 0)
116 return false;
117
118 return true;
119}
120
35388783 121int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
1e7a0e21
LP
122 struct nd_router_advert *a;
123 const uint8_t *p;
124 bool has_mtu = false, has_flag_extension = false;
125 size_t left;
126
127 assert(rt);
128
35388783
YW
129 if (rt->raw_size < sizeof(struct nd_router_advert))
130 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
131 "Too small to be a router advertisement, ignoring.");
1e7a0e21 132
da890466 133 /* Router advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
1e7a0e21
LP
134 a = NDISC_ROUTER_RAW(rt);
135
35388783
YW
136 if (a->nd_ra_type != ND_ROUTER_ADVERT)
137 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
138 "Received ND packet that is not a router advertisement, ignoring.");
1e7a0e21 139
35388783
YW
140 if (a->nd_ra_code != 0)
141 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
142 "Received ND packet with wrong RA code, ignoring.");
1e7a0e21
LP
143
144 rt->hop_limit = a->nd_ra_curhoplimit;
da890466 145 rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */
6197db53 146 rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false);
a68f007a 147 rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* mas_as_infinity = */ false);
d4c8de21 148 rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
1e7a0e21
LP
149
150 rt->preference = (rt->flags >> 3) & 3;
151 if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
152 rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
153
154 p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
155 left = rt->raw_size - sizeof(struct nd_router_advert);
156
157 for (;;) {
158 uint8_t type;
159 size_t length;
160
161 if (left == 0)
162 break;
163
35388783
YW
164 if (left < 2)
165 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
166 "Option lacks header, ignoring datagram.");
1e7a0e21
LP
167
168 type = p[0];
169 length = p[1] * 8;
170
35388783
YW
171 if (length == 0)
172 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
173 "Zero-length option, ignoring datagram.");
174 if (left < length)
175 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
176 "Option truncated, ignoring datagram.");
1e7a0e21
LP
177
178 switch (type) {
179
180 case SD_NDISC_OPTION_PREFIX_INFORMATION:
181
35388783
YW
182 if (length != 4*8)
183 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
184 "Prefix option of invalid size, ignoring datagram.");
1e7a0e21 185
35388783
YW
186 if (p[2] > 128)
187 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
188 "Bad prefix length, ignoring datagram.");
1e7a0e21
LP
189
190 break;
191
192 case SD_NDISC_OPTION_MTU: {
193 uint32_t m;
194
195 if (has_mtu) {
35388783 196 log_ndisc(nd, "MTU option specified twice, ignoring.");
f3241c61 197 break;
1e7a0e21
LP
198 }
199
35388783
YW
200 if (length != 8)
201 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
202 "MTU option of invalid size, ignoring datagram.");
1e7a0e21
LP
203
204 m = be32toh(*(uint32_t*) (p + 4));
205 if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
206 rt->mtu = m;
207
208 has_mtu = true;
209 break;
210 }
211
212 case SD_NDISC_OPTION_ROUTE_INFORMATION:
35388783
YW
213 if (length < 1*8 || length > 3*8)
214 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
215 "Route information option of invalid size, ignoring datagram.");
1e7a0e21 216
35388783
YW
217 if (p[2] > 128)
218 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
219 "Bad route prefix length, ignoring datagram.");
1e7a0e21
LP
220
221 break;
222
223 case SD_NDISC_OPTION_RDNSS:
35388783
YW
224 if (length < 3*8 || (length % (2*8)) != 1*8)
225 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), "RDNSS option has invalid size.");
1e7a0e21
LP
226
227 break;
228
229 case SD_NDISC_OPTION_FLAGS_EXTENSION:
230
231 if (has_flag_extension) {
35388783 232 log_ndisc(nd, "Flags extension option specified twice, ignoring.");
f3241c61 233 break;
1e7a0e21
LP
234 }
235
35388783
YW
236 if (length < 1*8)
237 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
238 "Flags extension option has invalid size.");
1e7a0e21
LP
239
240 /* Add in the additional flags bits */
241 rt->flags |=
242 ((uint64_t) p[2] << 8) |
243 ((uint64_t) p[3] << 16) |
244 ((uint64_t) p[4] << 24) |
245 ((uint64_t) p[5] << 32) |
246 ((uint64_t) p[6] << 40) |
247 ((uint64_t) p[7] << 48);
248
249 has_flag_extension = true;
250 break;
251
252 case SD_NDISC_OPTION_DNSSL:
35388783
YW
253 if (length < 2*8)
254 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
255 "DNSSL option has invalid size.");
1e7a0e21
LP
256
257 break;
6e8f5e4c
SS
258 case SD_NDISC_OPTION_PREF64: {
259 if (!pref64_option_verify((struct nd_opt_prefix64_info *) p, length))
260 log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
261 "PREF64 prefix has invalid prefix length.");
262 break;
263 }}
1e7a0e21
LP
264
265 p += length, left -= length;
266 }
267
268 rt->rindex = sizeof(struct nd_router_advert);
269 return 0;
270}
271
17347053 272int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
1e7a0e21
LP
273 assert_return(rt, -EINVAL);
274 assert_return(ret, -EINVAL);
275
276 *ret = rt->hop_limit;
277 return 0;
278}
279
a68f007a
YW
280int sd_ndisc_router_get_reachable_time(sd_ndisc_router *rt, uint64_t *ret) {
281 assert_return(rt, -EINVAL);
282 assert_return(ret, -EINVAL);
283
284 *ret = rt->reachable_time_usec;
285 return 0;
286}
287
d4c8de21
MM
288int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret) {
289 assert_return(rt, -EINVAL);
290 assert_return(ret, -EINVAL);
291
292 *ret = rt->retransmission_time_usec;
293 return 0;
294}
295
3231f624 296int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) {
1e7a0e21 297 assert_return(rt, -EINVAL);
3231f624 298 assert_return(ret, -EINVAL);
1e7a0e21 299
3231f624 300 *ret = rt->flags;
1e7a0e21
LP
301 return 0;
302}
303
6197db53 304int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
1e7a0e21 305 assert_return(rt, -EINVAL);
1e7a0e21 306
828b5dbf
YW
307 if (ret)
308 *ret = rt->lifetime_usec;
309
310 return rt->lifetime_usec > 0; /* Indicate if the router is still valid or not. */
1e7a0e21
LP
311}
312
17347053 313int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
1e7a0e21
LP
314 assert_return(rt, -EINVAL);
315 assert_return(ret, -EINVAL);
316
317 *ret = rt->preference;
318 return 0;
319}
320
17347053 321int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
1e7a0e21
LP
322 assert_return(rt, -EINVAL);
323 assert_return(ret, -EINVAL);
324
325 if (rt->mtu <= 0)
326 return -ENODATA;
327
328 *ret = rt->mtu;
329 return 0;
330}
331
17347053 332int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
1e7a0e21
LP
333 assert_return(rt, -EINVAL);
334
335 assert(rt->raw_size >= sizeof(struct nd_router_advert));
336 rt->rindex = sizeof(struct nd_router_advert);
337
338 return rt->rindex < rt->raw_size;
339}
340
17347053 341int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
1e7a0e21
LP
342 size_t length;
343
344 assert_return(rt, -EINVAL);
345
346 if (rt->rindex == rt->raw_size) /* EOF */
347 return -ESPIPE;
348
349 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
350 return -EBADMSG;
351
352 length = NDISC_ROUTER_OPTION_LENGTH(rt);
353 if (rt->rindex + length > rt->raw_size)
354 return -EBADMSG;
355
356 rt->rindex += length;
357 return rt->rindex < rt->raw_size;
358}
359
17347053 360int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
1e7a0e21
LP
361 assert_return(rt, -EINVAL);
362 assert_return(ret, -EINVAL);
363
364 if (rt->rindex == rt->raw_size) /* EOF */
365 return -ESPIPE;
366
367 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
368 return -EBADMSG;
369
370 *ret = NDISC_ROUTER_OPTION_TYPE(rt);
371 return 0;
372}
373
17347053 374int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
1e7a0e21
LP
375 uint8_t k;
376 int r;
377
378 assert_return(rt, -EINVAL);
379
380 r = sd_ndisc_router_option_get_type(rt, &k);
381 if (r < 0)
382 return r;
383
384 return type == k;
385}
386
3231f624 387int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) {
1e7a0e21
LP
388 size_t length;
389
390 assert_return(rt, -EINVAL);
391 assert_return(ret, -EINVAL);
3231f624 392 assert_return(ret_size, -EINVAL);
1e7a0e21
LP
393
394 /* Note that this returns the full option, including the option header */
395
396 if (rt->rindex + 2 > rt->raw_size)
397 return -EBADMSG;
398
399 length = NDISC_ROUTER_OPTION_LENGTH(rt);
400 if (rt->rindex + length > rt->raw_size)
401 return -EBADMSG;
402
403 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
3231f624 404 *ret_size = length;
1e7a0e21
LP
405
406 return 0;
407}
408
409static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
410 struct nd_opt_prefix_info *ri;
411 size_t length;
412 int r;
413
414 assert(rt);
415 assert(ret);
416
417 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
418 if (r < 0)
419 return r;
420 if (r == 0)
421 return -EMEDIUMTYPE;
422
423 length = NDISC_ROUTER_OPTION_LENGTH(rt);
424 if (length != sizeof(struct nd_opt_prefix_info))
425 return -EBADMSG;
426
427 ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
428 if (ri->nd_opt_pi_prefix_len > 128)
429 return -EBADMSG;
430
431 *ret = ri;
432 return 0;
433}
434
6197db53 435int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
1e7a0e21
LP
436 struct nd_opt_prefix_info *ri;
437 int r;
438
439 assert_return(rt, -EINVAL);
440 assert_return(ret, -EINVAL);
441
442 r = get_prefix_info(rt, &ri);
443 if (r < 0)
444 return r;
445
6197db53 446 *ret = be32_sec_to_usec(ri->nd_opt_pi_valid_time, /* max_as_infinity = */ true);
1e7a0e21
LP
447 return 0;
448}
449
6197db53 450int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
1e7a0e21
LP
451 struct nd_opt_prefix_info *pi;
452 int r;
453
454 assert_return(rt, -EINVAL);
455 assert_return(ret, -EINVAL);
456
457 r = get_prefix_info(rt, &pi);
458 if (r < 0)
459 return r;
460
6197db53 461 *ret = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true);
1e7a0e21
LP
462 return 0;
463}
464
17347053 465int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
1e7a0e21 466 struct nd_opt_prefix_info *pi;
9f702d00 467 uint8_t flags;
1e7a0e21
LP
468 int r;
469
470 assert_return(rt, -EINVAL);
471 assert_return(ret, -EINVAL);
472
473 r = get_prefix_info(rt, &pi);
474 if (r < 0)
475 return r;
476
9f702d00
JT
477 flags = pi->nd_opt_pi_flags_reserved;
478
479 if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) {
35388783 480 log_ndisc(NULL, "Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
9f702d00
JT
481 flags &= ~ND_OPT_PI_FLAG_AUTO;
482 }
483
484 *ret = flags;
1e7a0e21
LP
485 return 0;
486}
487
3231f624 488int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
1e7a0e21
LP
489 struct nd_opt_prefix_info *pi;
490 int r;
491
492 assert_return(rt, -EINVAL);
3231f624 493 assert_return(ret, -EINVAL);
1e7a0e21
LP
494
495 r = get_prefix_info(rt, &pi);
496 if (r < 0)
497 return r;
498
3231f624 499 *ret = pi->nd_opt_pi_prefix;
1e7a0e21
LP
500 return 0;
501}
502
17347053 503int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
1e7a0e21
LP
504 struct nd_opt_prefix_info *pi;
505 int r;
506
507 assert_return(rt, -EINVAL);
508 assert_return(ret, -EINVAL);
509
510 r = get_prefix_info(rt, &pi);
511 if (r < 0)
512 return r;
513
514 if (pi->nd_opt_pi_prefix_len > 128)
515 return -EBADMSG;
516
517 *ret = pi->nd_opt_pi_prefix_len;
518 return 0;
519}
520
521static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
522 uint8_t *ri;
523 size_t length;
524 int r;
525
526 assert(rt);
527 assert(ret);
528
529 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
530 if (r < 0)
531 return r;
532 if (r == 0)
533 return -EMEDIUMTYPE;
534
535 length = NDISC_ROUTER_OPTION_LENGTH(rt);
536 if (length < 1*8 || length > 3*8)
537 return -EBADMSG;
538
539 ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
540
541 if (ri[2] > 128)
542 return -EBADMSG;
543
544 *ret = ri;
545 return 0;
546}
547
6197db53 548int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
1e7a0e21
LP
549 uint8_t *ri;
550 int r;
551
552 assert_return(rt, -EINVAL);
553 assert_return(ret, -EINVAL);
554
555 r = get_route_info(rt, &ri);
556 if (r < 0)
557 return r;
558
6197db53 559 *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
1e7a0e21
LP
560 return 0;
561}
562
3231f624 563int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
1e7a0e21
LP
564 uint8_t *ri;
565 int r;
566
567 assert_return(rt, -EINVAL);
3231f624 568 assert_return(ret, -EINVAL);
1e7a0e21
LP
569
570 r = get_route_info(rt, &ri);
571 if (r < 0)
572 return r;
573
3231f624
YW
574 zero(*ret);
575 memcpy(ret, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
1e7a0e21
LP
576
577 return 0;
578}
579
17347053 580int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
1e7a0e21
LP
581 uint8_t *ri;
582 int r;
583
584 assert_return(rt, -EINVAL);
585 assert_return(ret, -EINVAL);
586
587 r = get_route_info(rt, &ri);
588 if (r < 0)
589 return r;
590
591 *ret = ri[2];
592 return 0;
593}
594
17347053 595int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
1e7a0e21
LP
596 uint8_t *ri;
597 int r;
598
599 assert_return(rt, -EINVAL);
600 assert_return(ret, -EINVAL);
601
602 r = get_route_info(rt, &ri);
603 if (r < 0)
604 return r;
605
3912d49d 606 if (!IN_SET((ri[3] >> 3) & 3, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
25f7271c 607 return -EOPNOTSUPP;
1e7a0e21 608
3912d49d 609 *ret = (ri[3] >> 3) & 3;
1e7a0e21
LP
610 return 0;
611}
612
613static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
614 size_t length;
615 int r;
616
617 assert(rt);
618 assert(ret);
619
620 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
621 if (r < 0)
622 return r;
623 if (r == 0)
624 return -EMEDIUMTYPE;
625
626 length = NDISC_ROUTER_OPTION_LENGTH(rt);
627 if (length < 3*8 || (length % (2*8)) != 1*8)
628 return -EBADMSG;
629
630 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
631 return 0;
632}
633
17347053 634int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
1e7a0e21
LP
635 uint8_t *ri;
636 int r;
637
638 assert_return(rt, -EINVAL);
639 assert_return(ret, -EINVAL);
640
641 r = get_rdnss_info(rt, &ri);
642 if (r < 0)
643 return r;
644
645 *ret = (const struct in6_addr*) (ri + 8);
646 return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
647}
648
6197db53 649int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
1e7a0e21
LP
650 uint8_t *ri;
651 int r;
652
653 assert_return(rt, -EINVAL);
654 assert_return(ret, -EINVAL);
655
656 r = get_rdnss_info(rt, &ri);
657 if (r < 0)
658 return r;
659
6197db53 660 *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
1e7a0e21
LP
661 return 0;
662}
663
664static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
665 size_t length;
666 int r;
667
668 assert(rt);
669 assert(ret);
670
671 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
672 if (r < 0)
673 return r;
674 if (r == 0)
675 return -EMEDIUMTYPE;
676
677 length = NDISC_ROUTER_OPTION_LENGTH(rt);
678 if (length < 2*8)
679 return -EBADMSG;
680
681 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
682 return 0;
683}
684
17347053 685int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
1e7a0e21
LP
686 _cleanup_strv_free_ char **l = NULL;
687 _cleanup_free_ char *e = NULL;
319a4f4b 688 size_t n = 0, left;
1e7a0e21
LP
689 uint8_t *ri, *p;
690 bool first = true;
691 int r;
692 unsigned k = 0;
693
694 assert_return(rt, -EINVAL);
695 assert_return(ret, -EINVAL);
696
697 r = get_dnssl_info(rt, &ri);
698 if (r < 0)
699 return r;
700
701 p = ri + 8;
702 left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
703
704 for (;;) {
705 if (left == 0) {
706
707 if (n > 0) /* Not properly NUL terminated */
708 return -EBADMSG;
709
710 break;
711 }
712
713 if (*p == 0) {
714 /* Found NUL termination */
715
716 if (n > 0) {
717 _cleanup_free_ char *normalized = NULL;
718
719 e[n] = 0;
7470cc4c 720 r = dns_name_normalize(e, 0, &normalized);
6244184e
YW
721 if (r < 0) {
722 _cleanup_free_ char *escaped = cescape(e);
723 log_debug_errno(r, "Failed to normalize advertised domain name \"%s\": %m", strna(escaped));
724 /* Here, do not propagate error code from dns_name_normalize() except for ENOMEM. */
725 return r == -ENOMEM ? -ENOMEM : -EBADMSG;
726 }
1e7a0e21
LP
727
728 /* Ignore the root domain name or "localhost" and friends */
729 if (!is_localhost(normalized) &&
730 !dns_name_is_root(normalized)) {
731
732 if (strv_push(&l, normalized) < 0)
733 return -ENOMEM;
734
735 normalized = NULL;
736 k++;
737 }
738 }
739
740 n = 0;
741 first = true;
742 p++, left--;
743 continue;
744 }
745
746 /* Check for compression (which is not allowed) */
747 if (*p > 63)
748 return -EBADMSG;
749
750 if (1U + *p + 1U > left)
751 return -EBADMSG;
752
319a4f4b 753 if (!GREEDY_REALLOC(e, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
1e7a0e21
LP
754 return -ENOMEM;
755
756 if (first)
757 first = false;
758 else
759 e[n++] = '.';
760
761 r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
6244184e
YW
762 if (r < 0) {
763 _cleanup_free_ char *escaped = cescape_length((const char*) p+1, *p);
764 log_debug_errno(r, "Failed to escape advertised domain name \"%s\": %m", strna(escaped));
765 /* Here, do not propagate error code from dns_label_escape() except for ENOMEM. */
766 return r == -ENOMEM ? -ENOMEM : -EBADMSG;
767 }
1e7a0e21
LP
768
769 n += r;
770
771 left -= 1 + *p;
772 p += 1 + *p;
773 }
774
775 if (strv_isempty(l)) {
776 *ret = NULL;
777 return 0;
778 }
779
1cc6c93a 780 *ret = TAKE_PTR(l);
1e7a0e21
LP
781
782 return k;
783}
784
6197db53 785int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
1e7a0e21
LP
786 uint8_t *ri;
787 int r;
788
789 assert_return(rt, -EINVAL);
3231f624 790 assert_return(ret, -EINVAL);
1e7a0e21
LP
791
792 r = get_dnssl_info(rt, &ri);
793 if (r < 0)
794 return r;
795
6197db53 796 *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
1e7a0e21
LP
797 return 0;
798}
9747955d 799
3231f624 800int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) {
9747955d
RP
801 int r;
802 const char *nd_opt_captive_portal;
803 size_t length;
804
805 assert_return(rt, -EINVAL);
3231f624
YW
806 assert_return(ret, -EINVAL);
807 assert_return(ret_size, -EINVAL);
9747955d
RP
808
809 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_CAPTIVE_PORTAL);
810 if (r < 0)
811 return r;
812 if (r == 0)
813 return -EMEDIUMTYPE;
814
815 r = sd_ndisc_router_option_get_raw(rt, (void *)&nd_opt_captive_portal, &length);
816 if (r < 0)
817 return r;
818
819 /* The length field has units of 8 octets */
820 assert(length % 8 == 0);
821 if (length == 0)
822 return -EBADMSG;
823
824 /* Check that the message is not truncated by an embedded NUL.
825 * NUL padding to a multiple of 8 is expected. */
826 size_t size = strnlen(nd_opt_captive_portal + 2, length - 2);
827 if (DIV_ROUND_UP(size + 2, 8) != length / 8)
828 return -EBADMSG;
829
830 /* Let's not return an empty buffer */
831 if (size == 0) {
3231f624 832 *ret = NULL;
9747955d
RP
833 *ret_size = 0;
834 return 0;
835 }
836
3231f624 837 *ret = nd_opt_captive_portal + 2;
9747955d
RP
838 *ret_size = size;
839
840 return 0;
841}
6e8f5e4c
SS
842
843static int get_pref64_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix64_info **ret) {
844 struct nd_opt_prefix64_info *ri;
845 size_t length;
846 int r;
847
848 assert(rt);
849 assert(ret);
850
851 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREF64);
852 if (r < 0)
853 return r;
854 if (r == 0)
855 return -EMEDIUMTYPE;
856
857 length = NDISC_ROUTER_OPTION_LENGTH(rt);
858 if (length != sizeof(struct nd_opt_prefix64_info))
859 return -EBADMSG;
860
861 ri = (struct nd_opt_prefix64_info *) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
862 if (!pref64_option_verify(ri, length))
863 return -EBADMSG;
864
865 *ret = ri;
866 return 0;
867}
868
3231f624 869int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret) {
6e8f5e4c
SS
870 struct nd_opt_prefix64_info *pi;
871 struct in6_addr a = {};
872 unsigned prefixlen;
873 int r;
874
875 assert_return(rt, -EINVAL);
3231f624 876 assert_return(ret, -EINVAL);
6e8f5e4c
SS
877
878 r = get_pref64_prefix_info(rt, &pi);
879 if (r < 0)
880 return r;
881
882 r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefixlen);
883 if (r < 0)
884 return r;
885
886 memcpy(&a, pi->prefix, sizeof(pi->prefix));
887 in6_addr_mask(&a, prefixlen);
888 /* extra safety check for refusing malformed prefix. */
889 if (memcmp(&a, pi->prefix, sizeof(pi->prefix)) != 0)
890 return -EBADMSG;
891
3231f624 892 *ret = a;
6e8f5e4c
SS
893 return 0;
894}
895
896int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
897 struct nd_opt_prefix64_info *pi;
898 uint16_t lifetime_prefix_len;
899 uint8_t prefix_len;
900 int r;
901
902 assert_return(rt, -EINVAL);
903 assert_return(ret, -EINVAL);
904
905 r = get_pref64_prefix_info(rt, &pi);
906 if (r < 0)
907 return r;
908
909 lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
910 pref64_plc_to_prefix_length(lifetime_prefix_len, &prefix_len);
911
912 *ret = prefix_len;
913 return 0;
914}
915
6197db53 916int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
6e8f5e4c
SS
917 struct nd_opt_prefix64_info *pi;
918 uint16_t lifetime_prefix_len;
919 int r;
920
921 assert_return(rt, -EINVAL);
922 assert_return(ret, -EINVAL);
923
924 r = get_pref64_prefix_info(rt, &pi);
925 if (r < 0)
926 return r;
927
928 lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
929
6197db53 930 *ret = (lifetime_prefix_len & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC;
6e8f5e4c
SS
931 return 0;
932}