]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/ndisc-option.c
sd-ndisc: make ndisc_send() and icmp6_send() take struct in6_addr
[thirdparty/systemd.git] / src / libsystemd-network / ndisc-option.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/icmp6.h>
4
5 #include "dns-domain.h"
6 #include "ether-addr-util.h"
7 #include "hostname-util.h"
8 #include "icmp6-util.h"
9 #include "in-addr-util.h"
10 #include "iovec-util.h"
11 #include "missing_network.h"
12 #include "ndisc-option.h"
13 #include "network-common.h"
14 #include "strv.h"
15 #include "unaligned.h"
16
17 /* RFC does not say anything about the maximum number of options, but let's limit the number of options for
18 * safety. Typically, the number of options in an ICMPv6 message should be only a few. */
19 #define MAX_OPTIONS 128
20
21 int ndisc_option_parse(
22 ICMP6Packet *p,
23 size_t offset,
24 uint8_t *ret_type,
25 size_t *ret_len,
26 const uint8_t **ret_opt) {
27
28 assert(p);
29
30 if (offset == p->raw_size)
31 return -ESPIPE; /* end of the packet */
32
33 if (offset > p->raw_size)
34 return -EBADMSG;
35
36 if (p->raw_size - offset < sizeof(struct nd_opt_hdr))
37 return -EBADMSG;
38
39 assert_cc(alignof(struct nd_opt_hdr) == 1);
40 const struct nd_opt_hdr *hdr = (const struct nd_opt_hdr*) (p->raw_packet + offset);
41 if (hdr->nd_opt_len == 0)
42 return -EBADMSG;
43
44 size_t len = hdr->nd_opt_len * 8;
45 if (p->raw_size - offset < len)
46 return -EBADMSG;
47
48 if (ret_type)
49 *ret_type = hdr->nd_opt_type;
50 if (ret_len)
51 *ret_len = len;
52 if (ret_opt)
53 *ret_opt = p->raw_packet + offset;
54
55 return 0;
56 }
57
58 static sd_ndisc_option* ndisc_option_new(uint8_t type, size_t offset) {
59 sd_ndisc_option *p = new0(sd_ndisc_option, 1); /* use new0() here to make the fuzzers silent. */
60 if (!p)
61 return NULL;
62
63 /* As the same reason in the above, do not use the structured initializer here. */
64 p->type = type;
65 p->offset = offset;
66
67 return p;
68 }
69
70 static void ndisc_raw_done(sd_ndisc_raw *raw) {
71 if (!raw)
72 return;
73
74 free(raw->bytes);
75 }
76
77 static void ndisc_rdnss_done(sd_ndisc_rdnss *rdnss) {
78 if (!rdnss)
79 return;
80
81 free(rdnss->addresses);
82 }
83
84 static void ndisc_dnssl_done(sd_ndisc_dnssl *dnssl) {
85 if (!dnssl)
86 return;
87
88 strv_free(dnssl->domains);
89 }
90
91 sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) {
92 if (!option)
93 return NULL;
94
95 switch (option->type) {
96 case 0:
97 ndisc_raw_done(&option->raw);
98 break;
99
100 case SD_NDISC_OPTION_RDNSS:
101 ndisc_rdnss_done(&option->rdnss);
102 break;
103
104 case SD_NDISC_OPTION_DNSSL:
105 ndisc_dnssl_done(&option->dnssl);
106 break;
107
108 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
109 free(option->captive_portal);
110 break;
111 }
112
113 return mfree(option);
114 }
115
116 static int ndisc_option_compare_func(const sd_ndisc_option *x, const sd_ndisc_option *y) {
117 int r;
118
119 assert(x);
120 assert(y);
121
122 r = CMP(x->type, y->type);
123 if (r != 0)
124 return r;
125
126 switch (x->type) {
127 case 0:
128 return memcmp_nn(x->raw.bytes, x->raw.length, y->raw.bytes, y->raw.length);
129
130 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
131 case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
132 case SD_NDISC_OPTION_REDIRECTED_HEADER:
133 case SD_NDISC_OPTION_MTU:
134 case SD_NDISC_OPTION_HOME_AGENT:
135 case SD_NDISC_OPTION_FLAGS_EXTENSION:
136 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
137 /* These options cannot be specified multiple times. */
138 return 0;
139
140 case SD_NDISC_OPTION_PREFIX_INFORMATION:
141 /* Should not specify the same prefix multiple times. */
142 r = CMP(x->prefix.prefixlen, y->prefix.prefixlen);
143 if (r != 0)
144 return r;
145
146 return memcmp(&x->prefix.address, &y->prefix.address, sizeof(struct in6_addr));
147
148 case SD_NDISC_OPTION_ROUTE_INFORMATION:
149 r = CMP(x->route.prefixlen, y->route.prefixlen);
150 if (r != 0)
151 return r;
152
153 return memcmp(&x->route.address, &y->route.address, sizeof(struct in6_addr));
154
155 case SD_NDISC_OPTION_PREF64:
156 r = CMP(x->prefix64.prefixlen, y->prefix64.prefixlen);
157 if (r != 0)
158 return r;
159
160 return memcmp(&x->prefix64.prefix, &y->prefix64.prefix, sizeof(struct in6_addr));
161
162 default:
163 /* DNSSL, RDNSS, and other unsupported options can be specified multiple times. */
164 return trivial_compare_func(x, y);
165 }
166 }
167
168 static void ndisc_option_hash_func(const sd_ndisc_option *option, struct siphash *state) {
169 assert(option);
170 assert(state);
171
172 siphash24_compress_typesafe(option->type, state);
173
174 switch (option->type) {
175 case 0:
176 siphash24_compress(option->raw.bytes, option->raw.length, state);
177 break;
178
179 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
180 case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
181 case SD_NDISC_OPTION_REDIRECTED_HEADER:
182 case SD_NDISC_OPTION_MTU:
183 case SD_NDISC_OPTION_FLAGS_EXTENSION:
184 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
185 break;
186
187 case SD_NDISC_OPTION_PREFIX_INFORMATION:
188 siphash24_compress_typesafe(option->prefix.prefixlen, state);
189 siphash24_compress_typesafe(option->prefix.address, state);
190 break;
191
192 case SD_NDISC_OPTION_ROUTE_INFORMATION:
193 siphash24_compress_typesafe(option->route.prefixlen, state);
194 siphash24_compress_typesafe(option->route.address, state);
195 break;
196
197 case SD_NDISC_OPTION_PREF64:
198 siphash24_compress_typesafe(option->prefix64.prefixlen, state);
199 siphash24_compress_typesafe(option->prefix64.prefix, state);
200 break;
201
202 default:
203 trivial_hash_func(option, state);
204 }
205 }
206
207 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
208 ndisc_option_hash_ops,
209 sd_ndisc_option,
210 ndisc_option_hash_func,
211 ndisc_option_compare_func,
212 ndisc_option_free);
213
214 static int ndisc_option_consume(Set **options, sd_ndisc_option *p) {
215 assert(options);
216 assert(p);
217
218 if (set_size(*options) >= MAX_OPTIONS) {
219 ndisc_option_free(p);
220 return -ETOOMANYREFS; /* recognizable error code */
221 }
222
223 return set_ensure_consume(options, &ndisc_option_hash_ops, p);
224 }
225
226 int ndisc_option_set_raw(Set **options, size_t length, const uint8_t *bytes) {
227 _cleanup_free_ uint8_t *copy = NULL;
228
229 assert(options);
230 assert(bytes);
231
232 if (length == 0)
233 return -EINVAL;
234
235 copy = newdup(uint8_t, bytes, length);
236 if (!copy)
237 return -ENOMEM;
238
239 sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, /* offset = */ 0);
240 if (!p)
241 return -ENOMEM;
242
243 p->raw = (sd_ndisc_raw) {
244 .bytes = TAKE_PTR(copy),
245 .length = length,
246 };
247
248 return ndisc_option_consume(options, p);
249 }
250
251 static int ndisc_option_build_raw(const sd_ndisc_option *option, uint8_t **ret) {
252 assert(option);
253 assert(option->type == 0);
254 assert(ret);
255
256 _cleanup_free_ uint8_t *buf = newdup(uint8_t, option->raw.bytes, option->raw.length);
257 if (!buf)
258 return -ENOMEM;
259
260 *ret = TAKE_PTR(buf);
261 return 0;
262 }
263
264 int ndisc_option_add_link_layer_address(Set **options, uint8_t type, size_t offset, const struct ether_addr *mac) {
265 assert(options);
266 assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
267
268 if (!mac || ether_addr_is_null(mac)) {
269 ndisc_option_remove_by_type(*options, type);
270 return 0;
271 }
272
273 sd_ndisc_option *p = ndisc_option_get_by_type(*options, type);
274 if (p) {
275 /* offset == 0 means that we are now building a packet to be sent, and in that case we allow
276 * to override the option we previously set.
277 * offset != 0 means that we are now parsing a received packet, and we refuse to override
278 * conflicting options. */
279 if (offset != 0)
280 return -EEXIST;
281
282 p->mac = *mac;
283 return 0;
284 }
285
286 p = ndisc_option_new(type, offset);
287 if (!p)
288 return -ENOMEM;
289
290 p->mac = *mac;
291
292 return set_ensure_consume(options, &ndisc_option_hash_ops, p);
293 }
294
295 static int ndisc_option_parse_link_layer_address(Set **options, size_t offset, size_t len, const uint8_t *opt) {
296 assert(options);
297 assert(opt);
298
299 if (len != sizeof(struct ether_addr) + 2)
300 return -EBADMSG;
301
302 if (!IN_SET(opt[0], SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS))
303 return -EBADMSG;
304
305 struct ether_addr mac;
306 memcpy(&mac, opt + 2, sizeof(struct ether_addr));
307
308 if (ether_addr_is_null(&mac))
309 return -EBADMSG;
310
311 return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac);
312 }
313
314 static int ndisc_option_build_link_layer_address(const sd_ndisc_option *option, uint8_t **ret) {
315 assert(option);
316 assert(IN_SET(option->type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
317 assert(ret);
318
319 assert_cc(2 + sizeof(struct ether_addr) == 8);
320
321 _cleanup_free_ uint8_t *buf = new(uint8_t, 2 + sizeof(struct ether_addr));
322 if (!buf)
323 return -ENOMEM;
324
325 buf[0] = option->type;
326 buf[1] = 1;
327 memcpy(buf + 2, &option->mac, sizeof(struct ether_addr));
328
329 *ret = TAKE_PTR(buf);
330 return 0;
331 }
332
333 int ndisc_option_add_prefix_internal(
334 Set **options,
335 size_t offset,
336 uint8_t flags,
337 uint8_t prefixlen,
338 const struct in6_addr *address,
339 usec_t valid_lifetime,
340 usec_t preferred_lifetime,
341 usec_t valid_until,
342 usec_t preferred_until) {
343
344 assert(options);
345 assert(address);
346
347 if (prefixlen > 128)
348 return -EINVAL;
349
350 struct in6_addr addr = *address;
351 in6_addr_mask(&addr, prefixlen);
352
353 /* RFC 4861 and 4862 only state that link-local prefix should be ignored.
354 * But here we also ignore null and multicast addresses. */
355 if (in6_addr_is_link_local(&addr) || in6_addr_is_null(&addr) || in6_addr_is_multicast(&addr))
356 return -EINVAL;
357
358 if (preferred_lifetime > valid_lifetime)
359 return -EINVAL;
360
361 if (preferred_until > valid_until)
362 return -EINVAL;
363
364 sd_ndisc_option *p = ndisc_option_get(
365 *options,
366 &(const sd_ndisc_option) {
367 .type = SD_NDISC_OPTION_PREFIX_INFORMATION,
368 .prefix.prefixlen = prefixlen,
369 .prefix.address = addr,
370 });
371 if (p) {
372 if (offset != 0)
373 return -EEXIST;
374
375 p->prefix.flags = flags;
376 p->prefix.valid_lifetime = valid_lifetime;
377 p->prefix.preferred_lifetime = preferred_lifetime;
378 p->prefix.valid_until = valid_until;
379 p->prefix.preferred_until = preferred_until;
380 return 0;
381 }
382
383 p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset);
384 if (!p)
385 return -ENOMEM;
386
387 p->prefix = (sd_ndisc_prefix) {
388 .flags = flags,
389 .prefixlen = prefixlen,
390 .address = addr,
391 .valid_lifetime = valid_lifetime,
392 .preferred_lifetime = preferred_lifetime,
393 .valid_until = valid_until,
394 .preferred_until = preferred_until,
395 };
396
397 return ndisc_option_consume(options, p);
398 }
399
400 static int ndisc_option_parse_prefix(Set **options, size_t offset, size_t len, const uint8_t *opt) {
401 const struct nd_opt_prefix_info *pi = (const struct nd_opt_prefix_info*) ASSERT_PTR(opt);
402
403 assert(options);
404
405 if (len != sizeof(struct nd_opt_prefix_info))
406 return -EBADMSG;
407
408 if (pi->nd_opt_pi_type != SD_NDISC_OPTION_PREFIX_INFORMATION)
409 return -EBADMSG;
410
411 usec_t valid = be32_sec_to_usec(pi->nd_opt_pi_valid_time, /* max_as_infinity = */ true);
412 usec_t pref = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true);
413
414 /* We only support 64 bits interface identifier for addrconf. */
415 uint8_t flags = pi->nd_opt_pi_flags_reserved;
416 if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO) && pi->nd_opt_pi_prefix_len != 64)
417 flags &= ~ND_OPT_PI_FLAG_AUTO;
418
419 return ndisc_option_add_prefix(options, offset, flags,
420 pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix,
421 valid, pref);
422 }
423
424 static int ndisc_option_build_prefix(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
425 assert(option);
426 assert(option->type == SD_NDISC_OPTION_PREFIX_INFORMATION);
427 assert(ret);
428
429 assert_cc(sizeof(struct nd_opt_prefix_info) % 8 == 0);
430
431 _cleanup_free_ struct nd_opt_prefix_info *buf = new(struct nd_opt_prefix_info, 1);
432 if (!buf)
433 return -ENOMEM;
434
435 usec_t valid = MIN(option->prefix.valid_lifetime,
436 usec_sub_unsigned(option->prefix.valid_until, timestamp));
437 usec_t pref = MIN3(valid,
438 option->prefix.preferred_lifetime,
439 usec_sub_unsigned(option->prefix.preferred_until, timestamp));
440
441 *buf = (struct nd_opt_prefix_info) {
442 .nd_opt_pi_type = SD_NDISC_OPTION_PREFIX_INFORMATION,
443 .nd_opt_pi_len = sizeof(struct nd_opt_prefix_info) / 8,
444 .nd_opt_pi_prefix_len = option->prefix.prefixlen,
445 .nd_opt_pi_flags_reserved = option->prefix.flags,
446 .nd_opt_pi_valid_time = usec_to_be32_sec(valid),
447 .nd_opt_pi_preferred_time = usec_to_be32_sec(pref),
448 .nd_opt_pi_prefix = option->prefix.address,
449 };
450
451 *ret = (uint8_t*) TAKE_PTR(buf);
452 return 0;
453 }
454
455 int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) {
456 assert(options);
457
458 if (!hdr) {
459 ndisc_option_remove_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
460 return 0;
461 }
462
463 sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
464 if (p) {
465 if (offset != 0)
466 return -EEXIST;
467
468 memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
469 return 0;
470 }
471
472 p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset);
473 if (!p)
474 return -ENOMEM;
475
476 /* For safety, here we copy only IPv6 header. */
477 memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
478
479 return ndisc_option_consume(options, p);
480 }
481
482 static int ndisc_option_parse_redirected_header(Set **options, size_t offset, size_t len, const uint8_t *opt) {
483 assert(options);
484 assert(opt);
485
486 if (len < sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr))
487 return -EBADMSG;
488
489 if (opt[0] != SD_NDISC_OPTION_REDIRECTED_HEADER)
490 return -EBADMSG;
491
492 return ndisc_option_add_redirected_header(options, offset, (const struct ip6_hdr*) (opt + sizeof(struct nd_opt_rd_hdr)));
493 }
494
495 static int ndisc_option_build_redirected_header(const sd_ndisc_option *option, uint8_t **ret) {
496 assert(option);
497 assert(option->type == SD_NDISC_OPTION_REDIRECTED_HEADER);
498 assert(ret);
499
500 assert_cc((sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr)) % 8 == 0);
501
502 size_t len = DIV_ROUND_UP(sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr), 8);
503
504 _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
505 if (!buf)
506 return -ENOMEM;
507
508 uint8_t *p;
509 p = mempcpy(buf,
510 &(const struct nd_opt_rd_hdr) {
511 .nd_opt_rh_type = SD_NDISC_OPTION_REDIRECTED_HEADER,
512 .nd_opt_rh_len = len,
513 },
514 sizeof(struct nd_opt_rd_hdr));
515 memcpy(p, &option->hdr, sizeof(struct ip6_hdr));
516
517 *ret = TAKE_PTR(buf);
518 return 0;
519 }
520
521 int ndisc_option_add_mtu(Set **options, size_t offset, uint32_t mtu) {
522 assert(options);
523
524 if (mtu < IPV6_MIN_MTU)
525 return -EINVAL;
526
527 sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_MTU);
528 if (p) {
529 if (offset != 0)
530 return -EEXIST;
531
532 p->mtu = mtu;
533 return 0;
534 }
535
536 p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset);
537 if (!p)
538 return -ENOMEM;
539
540 p->mtu = mtu;
541
542 return ndisc_option_consume(options, p);
543 }
544
545 static int ndisc_option_parse_mtu(Set **options, size_t offset, size_t len, const uint8_t *opt) {
546 const struct nd_opt_mtu *pm = (const struct nd_opt_mtu*) ASSERT_PTR(opt);
547
548 assert(options);
549
550 if (len != sizeof(struct nd_opt_mtu))
551 return -EBADMSG;
552
553 if (pm->nd_opt_mtu_type != SD_NDISC_OPTION_MTU)
554 return -EBADMSG;
555
556 return ndisc_option_add_mtu(options, offset, be32toh(pm->nd_opt_mtu_mtu));
557 }
558
559 static int ndisc_option_build_mtu(const sd_ndisc_option *option, uint8_t **ret) {
560 assert(option);
561 assert(option->type == SD_NDISC_OPTION_MTU);
562 assert(ret);
563
564 assert_cc(sizeof(struct nd_opt_mtu) % 8 == 0);
565
566 _cleanup_free_ struct nd_opt_mtu *buf = new(struct nd_opt_mtu, 1);
567 if (!buf)
568 return -ENOMEM;
569
570 *buf = (struct nd_opt_mtu) {
571 .nd_opt_mtu_type = SD_NDISC_OPTION_MTU,
572 .nd_opt_mtu_len = sizeof(struct nd_opt_mtu) / 8,
573 .nd_opt_mtu_mtu = htobe32(option->mtu),
574 };
575
576 *ret = (uint8_t*) TAKE_PTR(buf);
577 return 0;
578 }
579
580 int ndisc_option_add_home_agent_internal(
581 Set **options,
582 size_t offset,
583 uint16_t preference,
584 usec_t lifetime,
585 usec_t valid_until) {
586
587 assert(options);
588
589 if (lifetime > UINT16_MAX * USEC_PER_SEC)
590 return -EINVAL;
591
592 sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_HOME_AGENT);
593 if (p) {
594 if (offset != 0)
595 return -EEXIST;
596
597 p->home_agent = (sd_ndisc_home_agent) {
598 .preference = preference,
599 .lifetime = lifetime,
600 .valid_until = valid_until,
601 };
602 return 0;
603 }
604
605 p = ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT, offset);
606 if (!p)
607 return -ENOMEM;
608
609 p->home_agent = (sd_ndisc_home_agent) {
610 .preference = preference,
611 .lifetime = lifetime,
612 .valid_until = valid_until,
613 };
614
615 return ndisc_option_consume(options, p);
616 }
617
618 static int ndisc_option_parse_home_agent(Set **options, size_t offset, size_t len, const uint8_t *opt) {
619 const struct nd_opt_home_agent_info *p = (const struct nd_opt_home_agent_info*) ASSERT_PTR(opt);
620
621 assert(options);
622
623 if (len != sizeof(struct nd_opt_home_agent_info))
624 return -EBADMSG;
625
626 if (p->nd_opt_home_agent_info_type != SD_NDISC_OPTION_HOME_AGENT)
627 return -EBADMSG;
628
629 return ndisc_option_add_home_agent(
630 options, offset,
631 be16toh(p->nd_opt_home_agent_info_preference),
632 be16_sec_to_usec(p->nd_opt_home_agent_info_lifetime, /* max_as_infinity = */ false));
633 }
634
635 static int ndisc_option_build_home_agent(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
636 assert(option);
637 assert(option->type == SD_NDISC_OPTION_HOME_AGENT);
638 assert(ret);
639
640 assert_cc(sizeof(struct nd_opt_home_agent_info) % 8 == 0);
641
642 usec_t lifetime = MIN(option->home_agent.lifetime,
643 usec_sub_unsigned(option->home_agent.valid_until, timestamp));
644
645 _cleanup_free_ struct nd_opt_home_agent_info *buf = new(struct nd_opt_home_agent_info, 1);
646 if (!buf)
647 return -ENOMEM;
648
649 *buf = (struct nd_opt_home_agent_info) {
650 .nd_opt_home_agent_info_type = SD_NDISC_OPTION_HOME_AGENT,
651 .nd_opt_home_agent_info_len = sizeof(struct nd_opt_home_agent_info) / 8,
652 .nd_opt_home_agent_info_preference = htobe16(option->home_agent.preference),
653 .nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime),
654 };
655
656 *ret = (uint8_t*) TAKE_PTR(buf);
657 return 0;
658 }
659
660 int ndisc_option_add_route_internal(
661 Set **options,
662 size_t offset,
663 uint8_t preference,
664 uint8_t prefixlen,
665 const struct in6_addr *prefix,
666 usec_t lifetime,
667 usec_t valid_until) {
668
669 assert(options);
670 assert(prefix);
671
672 if (prefixlen > 128)
673 return -EINVAL;
674
675 /* RFC 4191 section 2.3
676 * Prf (Route Preference)
677 * 2-bit signed integer. The Route Preference indicates whether to prefer the router associated with
678 * this prefix over others, when multiple identical prefixes (for different routers) have been
679 * received. If the Reserved (10) value is received, the Route Information Option MUST be ignored. */
680 if (!IN_SET(preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
681 return -EINVAL;
682
683 struct in6_addr addr = *prefix;
684 in6_addr_mask(&addr, prefixlen);
685
686 sd_ndisc_option *p = ndisc_option_get(
687 *options,
688 &(const sd_ndisc_option) {
689 .type = SD_NDISC_OPTION_ROUTE_INFORMATION,
690 .route.prefixlen = prefixlen,
691 .route.address = addr,
692 });
693 if (p) {
694 if (offset != 0)
695 return -EEXIST;
696
697 p->route.preference = preference;
698 p->route.lifetime = lifetime;
699 p->route.valid_until = valid_until;
700 return 0;
701 }
702
703 p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset);
704 if (!p)
705 return -ENOMEM;
706
707 p->route = (sd_ndisc_route) {
708 .preference = preference,
709 .prefixlen = prefixlen,
710 .address = addr,
711 .lifetime = lifetime,
712 .valid_until = valid_until,
713 };
714
715 return ndisc_option_consume(options, p);
716 }
717
718 static int ndisc_option_parse_route(Set **options, size_t offset, size_t len, const uint8_t *opt) {
719 assert(options);
720 assert(opt);
721
722 if (!IN_SET(len, 1*8, 2*8, 3*8))
723 return -EBADMSG;
724
725 if (opt[0] != SD_NDISC_OPTION_ROUTE_INFORMATION)
726 return -EBADMSG;
727
728 uint8_t prefixlen = opt[2];
729 if (prefixlen > 128)
730 return -EBADMSG;
731
732 if (len < (size_t) (DIV_ROUND_UP(prefixlen, 64) + 1) * 8)
733 return -EBADMSG;
734
735 uint8_t preference = (opt[3] >> 3) & 0x03;
736 usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true);
737
738 struct in6_addr prefix;
739 memcpy(&prefix, opt + 8, len - 8);
740 in6_addr_mask(&prefix, prefixlen);
741
742 return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime);
743 }
744
745 static int ndisc_option_build_route(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
746 assert(option);
747 assert(option->type == SD_NDISC_OPTION_ROUTE_INFORMATION);
748 assert(option->route.prefixlen <= 128);
749 assert(ret);
750
751 size_t len = 1 + DIV_ROUND_UP(option->route.prefixlen, 64);
752 be32_t lifetime = usec_to_be32_sec(MIN(option->route.lifetime,
753 usec_sub_unsigned(option->route.valid_until, timestamp)));
754
755 _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
756 if (!buf)
757 return -ENOMEM;
758
759 buf[0] = SD_NDISC_OPTION_ROUTE_INFORMATION;
760 buf[1] = len;
761 buf[2] = option->route.prefixlen;
762 buf[3] = option->route.preference << 3;
763 memcpy(buf + 4, &lifetime, sizeof(be32_t));
764 memcpy_safe(buf + 8, &option->route.address, (len - 1) * 8);
765
766 *ret = TAKE_PTR(buf);
767 return 0;
768 }
769
770 int ndisc_option_add_rdnss_internal(
771 Set **options,
772 size_t offset,
773 size_t n_addresses,
774 const struct in6_addr *addresses,
775 usec_t lifetime,
776 usec_t valid_until) {
777
778 assert(options);
779 assert(addresses);
780
781 if (n_addresses == 0)
782 return -EINVAL;
783
784 _cleanup_free_ struct in6_addr *addrs = newdup(struct in6_addr, addresses, n_addresses);
785 if (!addrs)
786 return -ENOMEM;
787
788 sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_RDNSS, offset);
789 if (!p)
790 return -ENOMEM;
791
792 p->rdnss = (sd_ndisc_rdnss) {
793 .n_addresses = n_addresses,
794 .addresses = TAKE_PTR(addrs),
795 .lifetime = lifetime,
796 .valid_until = valid_until,
797 };
798
799 return ndisc_option_consume(options, p);
800 }
801
802 static int ndisc_option_parse_rdnss(Set **options, size_t offset, size_t len, const uint8_t *opt) {
803 assert(options);
804 assert(opt);
805
806 if (len < 8 + sizeof(struct in6_addr) || (len % sizeof(struct in6_addr)) != 8)
807 return -EBADMSG;
808
809 if (opt[0] != SD_NDISC_OPTION_RDNSS)
810 return -EBADMSG;
811
812 usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true);
813 size_t n_addrs = len / sizeof(struct in6_addr);
814
815 return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime);
816 }
817
818 static int ndisc_option_build_rdnss(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
819 assert(option);
820 assert(option->type == SD_NDISC_OPTION_RDNSS);
821 assert(ret);
822
823 size_t len = option->rdnss.n_addresses * 2 + 1;
824 be32_t lifetime = usec_to_be32_sec(MIN(option->rdnss.lifetime,
825 usec_sub_unsigned(option->rdnss.valid_until, timestamp)));
826
827 _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
828 if (!buf)
829 return -ENOMEM;
830
831 buf[0] = SD_NDISC_OPTION_RDNSS;
832 buf[1] = len;
833 buf[2] = 0;
834 buf[3] = 0;
835 memcpy(buf + 4, &lifetime, sizeof(be32_t));
836 memcpy(buf + 8, option->rdnss.addresses, sizeof(struct in6_addr) * option->rdnss.n_addresses);
837
838 *ret = TAKE_PTR(buf);
839 return 0;
840 }
841
842 int ndisc_option_add_flags_extension(Set **options, size_t offset, uint64_t flags) {
843 assert(options);
844
845 if ((flags & UINT64_C(0x00ffffffffffff00)) != flags)
846 return -EINVAL;
847
848 sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_FLAGS_EXTENSION);
849 if (p) {
850 if (offset != 0)
851 return -EEXIST;
852
853 p->extended_flags = flags;
854 return 0;
855 }
856
857 p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset);
858 if (!p)
859 return -ENOMEM;
860
861 p->extended_flags = flags;
862
863 return ndisc_option_consume(options, p);
864 }
865
866 static int ndisc_option_parse_flags_extension(Set **options, size_t offset, size_t len, const uint8_t *opt) {
867 assert(options);
868 assert(opt);
869
870 if (len != 8)
871 return -EBADMSG;
872
873 if (opt[0] != SD_NDISC_OPTION_FLAGS_EXTENSION)
874 return -EBADMSG;
875
876 uint64_t flags = (unaligned_read_be64(opt) & UINT64_C(0xffffffffffff0000)) >> 8;
877 return ndisc_option_add_flags_extension(options, offset, flags);
878 }
879
880 static int ndisc_option_build_flags_extension(const sd_ndisc_option *option, uint8_t **ret) {
881 assert(option);
882 assert(option->type == SD_NDISC_OPTION_FLAGS_EXTENSION);
883 assert(ret);
884
885 _cleanup_free_ uint8_t *buf = new(uint8_t, 8);
886 if (!buf)
887 return 0;
888
889 unaligned_write_be64(buf, (option->extended_flags & UINT64_C(0x00ffffffffffff00)) << 8);
890 buf[0] = SD_NDISC_OPTION_FLAGS_EXTENSION;
891 buf[1] = 1;
892
893 *ret = TAKE_PTR(buf);
894 return 0;
895 }
896
897 int ndisc_option_add_dnssl_internal(
898 Set **options,
899 size_t offset, char *
900 const *domains,
901 usec_t lifetime,
902 usec_t valid_until) {
903
904 int r;
905
906 assert(options);
907
908 if (strv_isempty(domains))
909 return -EINVAL;
910
911 STRV_FOREACH(s, domains) {
912 r = dns_name_is_valid(*s);
913 if (r < 0)
914 return r;
915
916 if (is_localhost(*s) || dns_name_is_root(*s))
917 return -EINVAL;
918 }
919
920 _cleanup_strv_free_ char **copy = strv_copy(domains);
921 if (!copy)
922 return -ENOMEM;
923
924 sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_DNSSL, offset);
925 if (!p)
926 return -ENOMEM;
927
928 p->dnssl = (sd_ndisc_dnssl) {
929 .domains = TAKE_PTR(copy),
930 .lifetime = lifetime,
931 .valid_until = valid_until,
932 };
933
934 return ndisc_option_consume(options, p);
935 }
936
937 static int ndisc_option_parse_dnssl(Set **options, size_t offset, size_t len, const uint8_t *opt) {
938 int r;
939
940 assert(options);
941 assert(opt);
942
943 if (len < 2*8)
944 return -EBADMSG;
945
946 if (opt[0] != SD_NDISC_OPTION_DNSSL)
947 return -EBADMSG;
948
949 usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true);
950
951 _cleanup_strv_free_ char **l = NULL;
952 _cleanup_free_ char *e = NULL;
953 size_t n = 0;
954 for (size_t c, pos = 8; pos < len; pos += c) {
955
956 c = opt[pos];
957 pos++;
958
959 if (c == 0) {
960 /* Found NUL termination */
961
962 if (n > 0) {
963 _cleanup_free_ char *normalized = NULL;
964
965 e[n] = 0;
966 r = dns_name_normalize(e, 0, &normalized);
967 if (r < 0)
968 return r;
969
970 /* Ignore the root domain name or "localhost" and friends */
971 if (!is_localhost(normalized) && !dns_name_is_root(normalized)) {
972 r = strv_consume(&l, TAKE_PTR(normalized));
973 if (r < 0)
974 return r;
975 }
976 }
977
978 n = 0;
979 continue;
980 }
981
982 /* Check for compression (which is not allowed) */
983 if (c > 63)
984 return -EBADMSG;
985
986 if (pos + c >= len)
987 return -EBADMSG;
988
989 if (!GREEDY_REALLOC(e, n + (n != 0) + DNS_LABEL_ESCAPED_MAX + 1U))
990 return -ENOMEM;
991
992 if (n != 0)
993 e[n++] = '.';
994
995 r = dns_label_escape((const char*) (opt + pos), c, e + n, DNS_LABEL_ESCAPED_MAX);
996 if (r < 0)
997 return r;
998
999 n += r;
1000 }
1001
1002 if (n > 0) /* Not properly NUL terminated */
1003 return -EBADMSG;
1004
1005 return ndisc_option_add_dnssl(options, offset, l, lifetime);
1006 }
1007
1008 static int ndisc_option_build_dnssl(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
1009 int r;
1010
1011 assert(option);
1012 assert(option->type == SD_NDISC_OPTION_DNSSL);
1013 assert(ret);
1014
1015 size_t len = 8;
1016 STRV_FOREACH(s, option->dnssl.domains)
1017 len += strlen(*s) + 2;
1018 len = DIV_ROUND_UP(len, 8);
1019
1020 be32_t lifetime = usec_to_be32_sec(MIN(option->dnssl.lifetime,
1021 usec_sub_unsigned(option->dnssl.valid_until, timestamp)));
1022
1023 _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
1024 if (!buf)
1025 return -ENOMEM;
1026
1027 buf[0] = SD_NDISC_OPTION_DNSSL;
1028 buf[1] = len;
1029 buf[2] = 0;
1030 buf[3] = 0;
1031 memcpy(buf + 4, &lifetime, sizeof(be32_t));
1032
1033 size_t remaining = len * 8 - 8;
1034 uint8_t *p = buf + 8;
1035
1036 STRV_FOREACH(s, option->dnssl.domains) {
1037 r = dns_name_to_wire_format(*s, p, remaining, /* canonical = */ false);
1038 if (r < 0)
1039 return r;
1040
1041 assert(remaining >= (size_t) r);
1042 p += r;
1043 remaining -= r;
1044 }
1045
1046 memzero(p, remaining);
1047
1048 *ret = TAKE_PTR(buf);
1049 return 0;
1050 }
1051
1052 int ndisc_option_add_captive_portal(Set **options, size_t offset, const char *portal) {
1053 assert(options);
1054
1055 if (isempty(portal))
1056 return -EINVAL;
1057
1058 if (!in_charset(portal, URI_VALID))
1059 return -EINVAL;
1060
1061 sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
1062 if (p) {
1063 if (offset != 0)
1064 return -EEXIST;
1065
1066 return free_and_strdup(&p->captive_portal, portal);
1067 }
1068
1069 _cleanup_free_ char *copy = strdup(portal);
1070 if (!copy)
1071 return -ENOMEM;
1072
1073 p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset);
1074 if (!p)
1075 return -ENOMEM;
1076
1077 p->captive_portal = TAKE_PTR(copy);
1078
1079 return ndisc_option_consume(options, p);
1080 }
1081
1082 static int ndisc_option_parse_captive_portal(Set **options, size_t offset, size_t len, const uint8_t *opt) {
1083 assert(options);
1084 assert(opt);
1085
1086 if (len < 8)
1087 return -EBADMSG;
1088
1089 if (opt[0] != SD_NDISC_OPTION_CAPTIVE_PORTAL)
1090 return -EBADMSG;
1091
1092 _cleanup_free_ char *portal = memdup_suffix0(opt + 2, len - 2);
1093 if (!portal)
1094 return -ENOMEM;
1095
1096 size_t size = strlen(portal);
1097 if (size == 0)
1098 return -EBADMSG;
1099
1100 /* Check that the message is not truncated by an embedded NUL.
1101 * NUL padding to a multiple of 8 is expected. */
1102 if (DIV_ROUND_UP(size + 2, 8) * 8 != len && DIV_ROUND_UP(size + 3, 8) * 8 != len)
1103 return -EBADMSG;
1104
1105 return ndisc_option_add_captive_portal(options, offset, portal);
1106 }
1107
1108 static int ndisc_option_build_captive_portal(const sd_ndisc_option *option, uint8_t **ret) {
1109 assert(option);
1110 assert(option->type == SD_NDISC_OPTION_CAPTIVE_PORTAL);
1111 assert(ret);
1112
1113 size_t len_portal = strlen(option->captive_portal);
1114 size_t len = DIV_ROUND_UP(len_portal + 1 + 2, 8);
1115
1116 _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
1117 if (!buf)
1118 return -ENOMEM;
1119
1120 buf[0] = SD_NDISC_OPTION_CAPTIVE_PORTAL;
1121 buf[1] = len;
1122
1123 uint8_t *p = mempcpy(buf + 2, option->captive_portal, len_portal);
1124 size_t remaining = len * 8 - 2 - len_portal;
1125
1126 memzero(p, remaining);
1127
1128 *ret = TAKE_PTR(buf);
1129 return 0;
1130 }
1131
1132 static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
1133 [PREFIX_LENGTH_CODE_96] = 96,
1134 [PREFIX_LENGTH_CODE_64] = 64,
1135 [PREFIX_LENGTH_CODE_56] = 56,
1136 [PREFIX_LENGTH_CODE_48] = 48,
1137 [PREFIX_LENGTH_CODE_40] = 40,
1138 [PREFIX_LENGTH_CODE_32] = 32,
1139 };
1140
1141 int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret) {
1142 for (size_t i = 0; i < ELEMENTSOF(prefix_length_code_to_prefix_length); i++)
1143 if (prefix_length_code_to_prefix_length[i] == prefixlen) {
1144 if (ret)
1145 *ret = i;
1146 return 0;
1147 }
1148
1149 return -EINVAL;
1150 }
1151
1152 static int pref64_lifetime_and_plc_parse(uint16_t lifetime_and_plc, uint8_t *ret_prefixlen, usec_t *ret_lifetime) {
1153 uint16_t plc = lifetime_and_plc & PREF64_PLC_MASK;
1154 if (plc >= _PREFIX_LENGTH_CODE_MAX)
1155 return -EINVAL;
1156
1157 if (ret_prefixlen)
1158 *ret_prefixlen = prefix_length_code_to_prefix_length[plc];
1159 if (ret_lifetime)
1160 *ret_lifetime = (lifetime_and_plc & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC;
1161 return 0;
1162 }
1163
1164 int ndisc_option_add_prefix64_internal(
1165 Set **options,
1166 size_t offset,
1167 uint8_t prefixlen,
1168 const struct in6_addr *prefix,
1169 usec_t lifetime,
1170 usec_t valid_until) {
1171
1172 int r;
1173
1174 assert(options);
1175 assert(prefix);
1176
1177 r = pref64_prefix_length_to_plc(prefixlen, NULL);
1178 if (r < 0)
1179 return r;
1180
1181 if (lifetime > PREF64_MAX_LIFETIME_USEC)
1182 return -EINVAL;
1183
1184 struct in6_addr addr = *prefix;
1185 in6_addr_mask(&addr, prefixlen);
1186
1187 sd_ndisc_option *p = ndisc_option_get(
1188 *options,
1189 &(const sd_ndisc_option) {
1190 .type = SD_NDISC_OPTION_PREF64,
1191 .prefix64.prefixlen = prefixlen,
1192 .prefix64.prefix = addr,
1193 });
1194 if (p) {
1195 if (offset != 0)
1196 return -EEXIST;
1197
1198 p->prefix64.lifetime = lifetime;
1199 p->prefix64.valid_until = valid_until;
1200 return 0;
1201 }
1202
1203 p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset);
1204 if (!p)
1205 return -ENOMEM;
1206
1207 p->prefix64 = (sd_ndisc_prefix64) {
1208 .prefixlen = prefixlen,
1209 .prefix = addr,
1210 .lifetime = lifetime,
1211 .valid_until = valid_until,
1212 };
1213
1214 return ndisc_option_consume(options, p);
1215 }
1216
1217 static int ndisc_option_parse_prefix64(Set **options, size_t offset, size_t len, const uint8_t *opt) {
1218 int r;
1219
1220 assert(options);
1221 assert(opt);
1222
1223 if (len != 2*8)
1224 return -EBADMSG;
1225
1226 if (opt[0] != SD_NDISC_OPTION_PREF64)
1227 return -EBADMSG;
1228
1229 uint8_t prefixlen;
1230 usec_t lifetime;
1231 r = pref64_lifetime_and_plc_parse(unaligned_read_be16(opt + 2), &prefixlen, &lifetime);
1232 if (r < 0)
1233 return r;
1234
1235 struct in6_addr prefix;
1236 memcpy(&prefix, opt + 4, len - 4);
1237 in6_addr_mask(&prefix, prefixlen);
1238
1239 return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime);
1240 }
1241
1242 static int ndisc_option_build_prefix64(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
1243 int r;
1244
1245 assert(option);
1246 assert(option->type == SD_NDISC_OPTION_PREF64);
1247 assert(ret);
1248
1249 uint8_t code;
1250 r = pref64_prefix_length_to_plc(option->prefix64.prefixlen, &code);
1251 if (r < 0)
1252 return r;
1253
1254 uint16_t lifetime = (uint16_t) DIV_ROUND_UP(MIN(option->prefix64.lifetime,
1255 usec_sub_unsigned(option->prefix64.valid_until, timestamp)),
1256 USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
1257
1258 _cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
1259 if (!buf)
1260 return -ENOMEM;
1261
1262 buf[0] = SD_NDISC_OPTION_PREF64;
1263 buf[1] = 2;
1264 unaligned_write_be16(buf + 2, lifetime | code);
1265 memcpy(buf + 4, &option->prefix64.prefix, 12);
1266
1267 *ret = TAKE_PTR(buf);
1268 return 0;
1269 }
1270
1271 static int ndisc_option_parse_default(Set **options, size_t offset, size_t len, const uint8_t *opt) {
1272 assert(options);
1273 assert(opt);
1274 assert(len > 0);
1275
1276 sd_ndisc_option *p = ndisc_option_new(opt[0], offset);
1277 if (!p)
1278 return -ENOMEM;
1279
1280 return ndisc_option_consume(options, p);
1281 }
1282
1283 static int ndisc_header_size(uint8_t icmp6_type) {
1284 switch (icmp6_type) {
1285 case ND_ROUTER_SOLICIT:
1286 return sizeof(struct nd_router_solicit);
1287 case ND_ROUTER_ADVERT:
1288 return sizeof(struct nd_router_advert);
1289 case ND_NEIGHBOR_SOLICIT:
1290 return sizeof(struct nd_neighbor_solicit);
1291 case ND_NEIGHBOR_ADVERT:
1292 return sizeof(struct nd_neighbor_advert);
1293 case ND_REDIRECT:
1294 return sizeof(struct nd_redirect);
1295 default:
1296 return -EINVAL;
1297 }
1298 }
1299
1300 int ndisc_parse_options(ICMP6Packet *packet, Set **ret_options) {
1301 _cleanup_set_free_ Set *options = NULL;
1302 int r;
1303
1304 assert(packet);
1305 assert(ret_options);
1306
1307 r = icmp6_packet_get_type(packet);
1308 if (r < 0)
1309 return r;
1310
1311 r = ndisc_header_size(r);
1312 if (r < 0)
1313 return -EBADMSG;
1314 size_t header_size = r;
1315
1316 if (packet->raw_size < header_size)
1317 return -EBADMSG;
1318
1319 for (size_t length, offset = header_size; offset < packet->raw_size; offset += length) {
1320 uint8_t type;
1321 const uint8_t *opt;
1322
1323 r = ndisc_option_parse(packet, offset, &type, &length, &opt);
1324 if (r < 0)
1325 return log_debug_errno(r, "Failed to parse NDisc option header: %m");
1326
1327 switch (type) {
1328 case 0:
1329 r = -EBADMSG;
1330 break;
1331
1332 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
1333 case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
1334 r = ndisc_option_parse_link_layer_address(&options, offset, length, opt);
1335 break;
1336
1337 case SD_NDISC_OPTION_PREFIX_INFORMATION:
1338 r = ndisc_option_parse_prefix(&options, offset, length, opt);
1339 break;
1340
1341 case SD_NDISC_OPTION_REDIRECTED_HEADER:
1342 r = ndisc_option_parse_redirected_header(&options, offset, length, opt);
1343 break;
1344
1345 case SD_NDISC_OPTION_MTU:
1346 r = ndisc_option_parse_mtu(&options, offset, length, opt);
1347 break;
1348
1349 case SD_NDISC_OPTION_HOME_AGENT:
1350 r = ndisc_option_parse_home_agent(&options, offset, length, opt);
1351 break;
1352
1353 case SD_NDISC_OPTION_ROUTE_INFORMATION:
1354 r = ndisc_option_parse_route(&options, offset, length, opt);
1355 break;
1356
1357 case SD_NDISC_OPTION_RDNSS:
1358 r = ndisc_option_parse_rdnss(&options, offset, length, opt);
1359 break;
1360
1361 case SD_NDISC_OPTION_FLAGS_EXTENSION:
1362 r = ndisc_option_parse_flags_extension(&options, offset, length, opt);
1363 break;
1364
1365 case SD_NDISC_OPTION_DNSSL:
1366 r = ndisc_option_parse_dnssl(&options, offset, length, opt);
1367 break;
1368
1369 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
1370 r = ndisc_option_parse_captive_portal(&options, offset, length, opt);
1371 break;
1372
1373 case SD_NDISC_OPTION_PREF64:
1374 r = ndisc_option_parse_prefix64(&options, offset, length, opt);
1375 break;
1376
1377 default:
1378 r = ndisc_option_parse_default(&options, offset, length, opt);
1379 }
1380 if (r == -ENOMEM)
1381 return log_oom_debug();
1382 if (r < 0)
1383 log_debug_errno(r, "Failed to parse NDisc option %u, ignoring: %m", type);
1384 }
1385
1386 *ret_options = TAKE_PTR(options);
1387 return 0;
1388 }
1389
1390 int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret) {
1391 assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
1392
1393 sd_ndisc_option *p = ndisc_option_get_by_type(options, type);
1394 if (!p)
1395 return -ENODATA;
1396
1397 if (ret)
1398 *ret = p->mac;
1399 return 0;
1400 }
1401
1402 int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp) {
1403 int r;
1404
1405 assert(fd >= 0);
1406 assert(dst);
1407 assert(hdr);
1408
1409 size_t n;
1410 _cleanup_free_ sd_ndisc_option **list = NULL;
1411 r = set_dump_sorted(options, (void***) &list, &n);
1412 if (r < 0)
1413 return r;
1414
1415 struct iovec *iov = NULL;
1416 size_t n_iov = 0;
1417 CLEANUP_ARRAY(iov, n_iov, iovec_array_free);
1418
1419 iov = new(struct iovec, 1 + n);
1420 if (!iov)
1421 return -ENOMEM;
1422
1423 r = ndisc_header_size(hdr->icmp6_type);
1424 if (r < 0)
1425 return r;
1426 size_t hdr_size = r;
1427
1428 _cleanup_free_ uint8_t *copy = newdup(uint8_t, hdr, hdr_size);
1429 if (!copy)
1430 return -ENOMEM;
1431
1432 iov[n_iov++] = IOVEC_MAKE(TAKE_PTR(copy), hdr_size);
1433
1434 FOREACH_ARRAY(p, list, n) {
1435 _cleanup_free_ uint8_t *buf = NULL;
1436 sd_ndisc_option *option = *p;
1437
1438 switch (option->type) {
1439 case 0:
1440 r = ndisc_option_build_raw(option, &buf);
1441 break;
1442
1443 case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
1444 case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
1445 r = ndisc_option_build_link_layer_address(option, &buf);
1446 break;
1447
1448 case SD_NDISC_OPTION_PREFIX_INFORMATION:
1449 r = ndisc_option_build_prefix(option, timestamp, &buf);
1450 break;
1451
1452 case SD_NDISC_OPTION_REDIRECTED_HEADER:
1453 r = ndisc_option_build_redirected_header(option, &buf);
1454 break;
1455
1456 case SD_NDISC_OPTION_MTU:
1457 r = ndisc_option_build_mtu(option, &buf);
1458 break;
1459
1460 case SD_NDISC_OPTION_HOME_AGENT:
1461 r = ndisc_option_build_home_agent(option, timestamp, &buf);
1462 break;
1463
1464 case SD_NDISC_OPTION_ROUTE_INFORMATION:
1465 r = ndisc_option_build_route(option, timestamp, &buf);
1466 break;
1467
1468 case SD_NDISC_OPTION_RDNSS:
1469 r = ndisc_option_build_rdnss(option, timestamp, &buf);
1470 break;
1471
1472 case SD_NDISC_OPTION_FLAGS_EXTENSION:
1473 r = ndisc_option_build_flags_extension(option, &buf);
1474 break;
1475
1476 case SD_NDISC_OPTION_DNSSL:
1477 r = ndisc_option_build_dnssl(option, timestamp, &buf);
1478 break;
1479
1480 case SD_NDISC_OPTION_CAPTIVE_PORTAL:
1481 r = ndisc_option_build_captive_portal(option, &buf);
1482 break;
1483
1484 case SD_NDISC_OPTION_PREF64:
1485 r = ndisc_option_build_prefix64(option, timestamp, &buf);
1486 break;
1487
1488 default:
1489 continue;
1490 }
1491 if (r == -ENOMEM)
1492 return log_oom_debug();
1493 if (r < 0)
1494 log_debug_errno(r, "Failed to build NDisc option %u, ignoring: %m", option->type);
1495
1496 iov[n_iov++] = IOVEC_MAKE(buf, buf[1] * 8);
1497 TAKE_PTR(buf);
1498 }
1499
1500 return icmp6_send(fd, dst, iov, n_iov);
1501 }