]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-lease.c
278962808a190113347891da286de179200b458b
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-lease.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
4 ***/
5
6 #include "sd-dhcp6-option.h"
7
8 #include "alloc-util.h"
9 #include "dhcp6-internal.h"
10 #include "dhcp6-lease-internal.h"
11 #include "dns-domain.h"
12 #include "dns-resolver-internal.h"
13 #include "network-common.h"
14 #include "set.h"
15 #include "sort-util.h"
16 #include "string-util.h"
17 #include "strv.h"
18 #include "unaligned.h"
19
20 #define IRT_DEFAULT (1 * USEC_PER_DAY)
21 #define IRT_MINIMUM (600 * USEC_PER_SEC)
22
23 static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) {
24 assert(lease);
25
26 if (timestamp && triple_timestamp_is_set(timestamp))
27 lease->timestamp = *timestamp;
28 else
29 triple_timestamp_now(&lease->timestamp);
30 }
31
32 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
33 assert_return(lease, -EINVAL);
34 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
35 assert_return(clock_supported(clock), -EOPNOTSUPP);
36 assert_return(ret, -EINVAL);
37
38 if (!triple_timestamp_is_set(&lease->timestamp))
39 return -ENODATA;
40
41 *ret = triple_timestamp_by_clock(&lease->timestamp, clock);
42 return 0;
43 }
44
45 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
46 usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY;
47
48 assert(lease);
49 assert(lease->ia_na || lease->ia_pd);
50
51 if (lease->ia_na) {
52 t1 = MIN(t1, be32_sec_to_usec(lease->ia_na->header.lifetime_t1, /* max_as_infinity = */ true));
53 t2 = MIN(t2, be32_sec_to_usec(lease->ia_na->header.lifetime_t2, /* max_as_infinity = */ true));
54
55 LIST_FOREACH(addresses, a, lease->ia_na->addresses)
56 min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true));
57 }
58
59 if (lease->ia_pd) {
60 t1 = MIN(t1, be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true));
61 t2 = MIN(t2, be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true));
62
63 LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
64 min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true));
65 }
66
67 if (t2 == 0 || t2 > min_valid_lt) {
68 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
69 * then adjust lifetime with it. */
70 t1 = min_valid_lt / 2;
71 t2 = min_valid_lt / 10 * 8;
72 }
73
74 lease->lifetime_valid = min_valid_lt;
75 lease->lifetime_t1 = t1;
76 lease->lifetime_t2 = t2;
77 }
78
79 #define DEFINE_GET_TIME_FUNCTIONS(name, val) \
80 int sd_dhcp6_lease_get_##name( \
81 sd_dhcp6_lease *lease, \
82 uint64_t *ret) { \
83 \
84 assert_return(lease, -EINVAL); \
85 \
86 if (!lease->ia_na && !lease->ia_pd) \
87 return -ENODATA; \
88 \
89 if (ret) \
90 *ret = lease->val; \
91 return 0; \
92 } \
93 \
94 int sd_dhcp6_lease_get_##name##_timestamp( \
95 sd_dhcp6_lease *lease, \
96 clockid_t clock, \
97 uint64_t *ret) { \
98 \
99 usec_t s, t; \
100 int r; \
101 \
102 assert_return(lease, -EINVAL); \
103 \
104 r = sd_dhcp6_lease_get_##name(lease, &s); \
105 if (r < 0) \
106 return r; \
107 \
108 r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
109 if (r < 0) \
110 return r; \
111 \
112 if (ret) \
113 *ret = time_span_to_stamp(s, t); \
114 return 0; \
115 }
116
117 DEFINE_GET_TIME_FUNCTIONS(t1, lifetime_t1);
118 DEFINE_GET_TIME_FUNCTIONS(t2, lifetime_t2);
119 DEFINE_GET_TIME_FUNCTIONS(valid_lifetime, lifetime_valid);
120
121 static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
122 assert(lease);
123
124 if (server_address)
125 lease->server_address = *server_address;
126 else
127 lease->server_address = (struct in6_addr) {};
128 }
129
130 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
131 assert_return(lease, -EINVAL);
132 assert_return(ret, -EINVAL);
133
134 *ret = lease->server_address;
135 return 0;
136 }
137
138 void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
139 assert(ia);
140
141 LIST_FOREACH(addresses, a, ia->addresses)
142 free(a);
143
144 ia->addresses = NULL;
145 }
146
147 DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
148 if (!ia)
149 return NULL;
150
151 dhcp6_ia_clear_addresses(ia);
152
153 return mfree(ia);
154 }
155
156 int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
157 uint8_t *clientid = NULL;
158
159 assert(lease);
160 assert(id || len == 0);
161
162 if (len > 0) {
163 clientid = memdup(id, len);
164 if (!clientid)
165 return -ENOMEM;
166 }
167
168 free_and_replace(lease->clientid, clientid);
169 lease->clientid_len = len;
170
171 return 0;
172 }
173
174 int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
175 assert(lease);
176
177 if (!lease->clientid)
178 return -ENODATA;
179
180 if (ret_id)
181 *ret_id = lease->clientid;
182 if (ret_len)
183 *ret_len = lease->clientid_len;
184
185 return 0;
186 }
187
188 int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
189 uint8_t *serverid = NULL;
190
191 assert(lease);
192 assert(id || len == 0);
193
194 if (len > 0) {
195 serverid = memdup(id, len);
196 if (!serverid)
197 return -ENOMEM;
198 }
199
200 free_and_replace(lease->serverid, serverid);
201 lease->serverid_len = len;
202
203 return 0;
204 }
205
206 int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
207 assert(lease);
208
209 if (!lease->serverid)
210 return -ENODATA;
211
212 if (ret_id)
213 *ret_id = lease->serverid;
214 if (ret_len)
215 *ret_len = lease->serverid_len;
216 return 0;
217 }
218
219 int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
220 assert(lease);
221
222 lease->preference = preference;
223 return 0;
224 }
225
226 int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
227 assert(lease);
228 assert(ret);
229
230 *ret = lease->preference;
231 return 0;
232 }
233
234 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
235 assert(lease);
236
237 lease->rapid_commit = true;
238 return 0;
239 }
240
241 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
242 assert(lease);
243 assert(ret);
244
245 *ret = lease->rapid_commit;
246 return 0;
247 }
248
249 int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
250 assert_return(lease, -EINVAL);
251
252 if (!lease->addr_iter)
253 return -ENODATA;
254
255 if (ret)
256 *ret = lease->addr_iter->iaaddr.address;
257 return 0;
258 }
259
260 int sd_dhcp6_lease_get_address_lifetime(
261 sd_dhcp6_lease *lease,
262 usec_t *ret_lifetime_preferred,
263 usec_t *ret_lifetime_valid) {
264
265 const struct iaaddr *a;
266
267 assert_return(lease, -EINVAL);
268
269 if (!lease->addr_iter)
270 return -ENODATA;
271
272 a = &lease->addr_iter->iaaddr;
273
274 if (ret_lifetime_preferred)
275 *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
276 if (ret_lifetime_valid)
277 *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
278 return 0;
279 }
280
281 int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease) {
282 if (!lease)
283 return false;
284
285 lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
286 return !!lease->addr_iter;
287 }
288
289 int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease) {
290 if (!lease || !lease->addr_iter)
291 return false;
292
293 lease->addr_iter = lease->addr_iter->addresses_next;
294 return !!lease->addr_iter;
295 }
296
297 int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease) {
298 return lease && lease->ia_na;
299 }
300
301 int sd_dhcp6_lease_get_pd_prefix(
302 sd_dhcp6_lease *lease,
303 struct in6_addr *ret_prefix,
304 uint8_t *ret_prefix_len) {
305
306 const struct iapdprefix *a;
307
308 assert_return(lease, -EINVAL);
309
310 if (!lease->prefix_iter)
311 return -ENODATA;
312
313 a = &lease->prefix_iter->iapdprefix;
314
315 if (ret_prefix)
316 *ret_prefix = a->address;
317 if (ret_prefix_len)
318 *ret_prefix_len = a->prefixlen;
319 return 0;
320 }
321
322 int sd_dhcp6_lease_get_pd_lifetime(
323 sd_dhcp6_lease *lease,
324 uint64_t *ret_lifetime_preferred,
325 uint64_t *ret_lifetime_valid) {
326
327 const struct iapdprefix *a;
328
329 assert_return(lease, -EINVAL);
330
331 if (!lease->prefix_iter)
332 return -ENODATA;
333
334 a = &lease->prefix_iter->iapdprefix;
335
336 if (ret_lifetime_preferred)
337 *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
338 if (ret_lifetime_valid)
339 *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
340 return 0;
341 }
342
343 int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease) {
344 if (!lease)
345 return false;
346
347 lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
348 return !!lease->prefix_iter;
349 }
350
351 int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease) {
352 if (!lease || !lease->prefix_iter)
353 return false;
354
355 lease->prefix_iter = lease->prefix_iter->addresses_next;
356 return !!lease->prefix_iter;
357 }
358
359 #define DEFINE_GET_TIMESTAMP2(name) \
360 int sd_dhcp6_lease_get_##name##_lifetime_timestamp( \
361 sd_dhcp6_lease *lease, \
362 clockid_t clock, \
363 uint64_t *ret_lifetime_preferred, \
364 uint64_t *ret_lifetime_valid) { \
365 \
366 usec_t t, p, v; \
367 int r; \
368 \
369 assert_return(lease, -EINVAL); \
370 \
371 r = sd_dhcp6_lease_get_##name##_lifetime( \
372 lease, \
373 ret_lifetime_preferred ? &p : NULL, \
374 ret_lifetime_valid ? &v : NULL); \
375 if (r < 0) \
376 return r; \
377 \
378 r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
379 if (r < 0) \
380 return r; \
381 \
382 if (ret_lifetime_preferred) \
383 *ret_lifetime_preferred = time_span_to_stamp(p, t); \
384 if (ret_lifetime_valid) \
385 *ret_lifetime_valid = time_span_to_stamp(v, t); \
386 return 0; \
387 }
388
389 DEFINE_GET_TIMESTAMP2(address);
390 DEFINE_GET_TIMESTAMP2(pd);
391
392 int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
393 return lease && lease->ia_pd;
394 }
395
396 int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
397 assert(lease);
398 assert(optval || optlen == 0);
399
400 if (optlen == 0)
401 return 0;
402
403 return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
404 }
405
406 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
407 assert_return(lease, -EINVAL);
408
409 if (!lease->dns)
410 return -ENODATA;
411
412 if (ret)
413 *ret = lease->dns;
414
415 return lease->dns_count;
416 }
417
418 int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
419 _cleanup_strv_free_ char **domains = NULL;
420 int r;
421
422 assert(lease);
423 assert(optval || optlen == 0);
424
425 if (optlen == 0)
426 return 0;
427
428 r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
429 if (r < 0)
430 return r;
431
432 return strv_extend_strv_consume(&lease->domains, TAKE_PTR(domains), /* filter_duplicates = */ true);
433 }
434
435 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
436 assert_return(lease, -EINVAL);
437 assert_return(ret, -EINVAL);
438
439 if (!lease->domains)
440 return -ENODATA;
441
442 *ret = lease->domains;
443 return strv_length(lease->domains);
444 }
445
446 static int dhcp6_lease_add_dnr(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
447 int r;
448
449 assert(lease);
450
451 _cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
452
453 size_t offset = 0;
454
455 /* priority */
456 if (optlen - offset < sizeof(uint16_t))
457 return -EBADMSG;
458 res.priority = unaligned_read_be16(optval + offset);
459 offset += sizeof(uint16_t);
460
461 /* adn */
462 if (optlen - offset < sizeof(uint16_t))
463 return -EBADMSG;
464 size_t ilen = unaligned_read_be16(optval + offset);
465 offset += sizeof(uint16_t);
466 if (offset + ilen > optlen)
467 return -EBADMSG;
468
469 r = dhcp6_option_parse_domainname(optval + offset, ilen, &res.auth_name);
470 if (r < 0)
471 return r;
472 r = dns_name_is_valid_ldh(res.auth_name);
473 if (r < 0)
474 return r;
475 if (!r)
476 return -EBADMSG;
477 offset += ilen;
478
479 /* RFC9463 § 3.1.6: adn only mode */
480 if (offset == optlen)
481 return 0;
482
483 /* addrs */
484 if (optlen - offset < sizeof(uint16_t))
485 return -EBADMSG;
486 ilen = unaligned_read_be16(optval + offset);
487 offset += sizeof(uint16_t);
488 if (offset + ilen > optlen)
489 return -EBADMSG;
490
491 _cleanup_free_ struct in6_addr *addrs = NULL;
492 size_t n_addrs = 0;
493
494 r = dhcp6_option_parse_addresses(optval + offset, ilen, &addrs, &n_addrs);
495 if (r < 0)
496 return r;
497 if (n_addrs == 0)
498 return -EBADMSG;
499 offset += ilen;
500
501 res.addrs = new(union in_addr_union, n_addrs);
502 if (!res.addrs)
503 return -ENOMEM;
504
505 for (size_t i = 0; i < n_addrs; i++) {
506 union in_addr_union addr = {.in6 = addrs[i]};
507 /* RFC9463 § 6.2 client MUST discard multicast and host loopback addresses */
508 if (in_addr_is_multicast(AF_INET6, &addr) ||
509 in_addr_is_localhost(AF_INET6, &addr))
510 return -EBADMSG;
511 res.addrs[i] = addr;
512 }
513 res.n_addrs = n_addrs;
514 res.family = AF_INET6;
515
516 /* svc params */
517 r = dnr_parse_svc_params(optval + offset, optlen-offset, &res);
518 if (r < 0)
519 return r;
520
521 /* Append this resolver */
522 if (!GREEDY_REALLOC(lease->dnr, lease->n_dnr+1))
523 return -ENOMEM;
524
525 lease->dnr[lease->n_dnr++] = TAKE_STRUCT(res);
526
527 typesafe_qsort(lease->dnr, lease->n_dnr, dns_resolver_prio_compare);
528
529 return 1;
530 }
531
532 int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret) {
533 assert_return(lease, -EINVAL);
534 assert_return(ret, -EINVAL);
535
536 if (!lease->dnr)
537 return -ENODATA;
538
539 *ret = lease->dnr;
540 return lease->n_dnr;
541 }
542
543 int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
544 int r;
545
546 assert(lease);
547 assert(optval || optlen == 0);
548
549 for (size_t offset = 0; offset < optlen;) {
550 const uint8_t *subval;
551 size_t sublen;
552 uint16_t subopt;
553
554 r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
555 if (r < 0)
556 return r;
557
558 switch (subopt) {
559 case DHCP6_NTP_SUBOPTION_SRV_ADDR:
560 case DHCP6_NTP_SUBOPTION_MC_ADDR:
561 if (sublen != 16)
562 return -EINVAL;
563
564 r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
565 if (r < 0)
566 return r;
567
568 break;
569
570 case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
571 _cleanup_free_ char *server = NULL;
572
573 r = dhcp6_option_parse_domainname(subval, sublen, &server);
574 if (r < 0)
575 return r;
576
577 if (strv_contains(lease->ntp_fqdn, server))
578 continue;
579
580 r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
581 if (r < 0)
582 return r;
583
584 break;
585 }}
586 }
587
588 return 0;
589 }
590
591 int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
592 assert(lease);
593 assert(optval || optlen == 0);
594
595 if (optlen == 0)
596 return 0;
597
598 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
599 return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
600 }
601
602 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
603 assert_return(lease, -EINVAL);
604
605 if (lease->ntp) {
606 if (ret)
607 *ret = lease->ntp;
608 return lease->ntp_count;
609 }
610
611 if (lease->sntp && !lease->ntp_fqdn) {
612 /* Fallback to the deprecated SNTP option. */
613 if (ret)
614 *ret = lease->sntp;
615 return lease->sntp_count;
616 }
617
618 return -ENODATA;
619 }
620
621 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
622 assert_return(lease, -EINVAL);
623
624 if (!lease->ntp_fqdn)
625 return -ENODATA;
626
627 if (ret)
628 *ret = lease->ntp_fqdn;
629 return strv_length(lease->ntp_fqdn);
630 }
631
632 int dhcp6_lease_add_sip_addrs(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
633 assert(lease);
634 assert(optval || optlen == 0);
635
636 if (optlen == 0)
637 return 0;
638
639 return dhcp6_option_parse_addresses(optval, optlen, &lease->sip, &lease->sip_count);
640 }
641
642 int sd_dhcp6_lease_get_sip_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
643 assert_return(lease, -EINVAL);
644
645 if (lease->sip) {
646 if (ret)
647 *ret = lease->sip;
648 return lease->sip_count;
649 }
650
651 return -ENODATA;
652 }
653
654 int dhcp6_lease_add_sip_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
655 _cleanup_strv_free_ char **domains = NULL;
656 int r;
657
658 assert(lease);
659 assert(optval || optlen == 0);
660
661 if (optlen == 0)
662 return 0;
663
664 r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
665 if (r < 0)
666 return r;
667
668 return strv_extend_strv_consume(&lease->sip_domains, TAKE_PTR(domains), /* filter_duplicates = */ true);
669 }
670
671 int sd_dhcp6_lease_get_sip_domains(sd_dhcp6_lease *lease, char ***ret) {
672 assert_return(lease, -EINVAL);
673 assert_return(ret, -EINVAL);
674
675 if (!lease->sip_domains)
676 return -ENODATA;
677
678 *ret = lease->sip_domains;
679 return strv_length(lease->sip_domains);
680 }
681
682 int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
683 char *fqdn;
684 int r;
685
686 assert(lease);
687 assert(optval || optlen == 0);
688
689 if (optlen == 0)
690 return 0;
691
692 if (optlen < 2)
693 return -ENODATA;
694
695 /* Ignore the flags field, it doesn't carry any useful
696 information for clients. */
697 r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
698 if (r < 0)
699 return r;
700
701 return free_and_replace(lease->fqdn, fqdn);
702 }
703
704 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
705 assert_return(lease, -EINVAL);
706 assert_return(ret, -EINVAL);
707
708 if (!lease->fqdn)
709 return -ENODATA;
710
711 *ret = lease->fqdn;
712 return 0;
713 }
714
715 int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
716 _cleanup_free_ char *uri = NULL;
717 int r;
718
719 assert(lease);
720 assert(optval || optlen == 0);
721
722 r = dhcp6_option_parse_string(optval, optlen, &uri);
723 if (r < 0)
724 return r;
725
726 if (uri && !in_charset(uri, URI_VALID))
727 return -EINVAL;
728
729 return free_and_replace(lease->captive_portal, uri);
730 }
731
732 int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
733 assert_return(lease, -EINVAL);
734 assert_return(ret, -EINVAL);
735
736 if (!lease->captive_portal)
737 return -ENODATA;
738
739 *ret = lease->captive_portal;
740 return 0;
741 }
742
743 int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) {
744 int r;
745
746 assert_return(lease, -EINVAL);
747
748 if (set_isempty(lease->vendor_options))
749 return -ENODATA;
750
751 if (ret) {
752 if (!lease->sorted_vendor_options) {
753 r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL);
754 if (r < 0)
755 return r;
756 }
757
758 *ret = lease->sorted_vendor_options;
759 }
760
761 return set_size(lease->vendor_options);
762 }
763
764 static int dhcp6_lease_insert_vendor_option(
765 sd_dhcp6_lease *lease,
766 uint16_t option_code,
767 const void *data,
768 size_t len,
769 uint32_t enterprise_id) {
770
771 _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL;
772
773 assert(lease);
774
775 option = new(sd_dhcp6_option, 1);
776 if (!option)
777 return -ENOMEM;
778
779 *option = (sd_dhcp6_option) {
780 .n_ref = 1,
781 .enterprise_identifier = enterprise_id,
782 .option = option_code,
783 .length = len,
784 };
785 option->data = memdup_suffix0(data, len);
786 if (!option->data)
787 return -ENOMEM;
788
789 return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option));
790 }
791
792 static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
793 int r;
794 uint32_t enterprise_id;
795
796 assert(lease);
797 assert(optval || optlen == 0);
798
799 if (optlen < sizeof(be32_t))
800 return -EBADMSG;
801
802 enterprise_id = unaligned_read_be32(optval);
803
804 for (size_t offset = 4; offset < optlen;) {
805 const uint8_t *subval;
806 size_t sublen;
807 uint16_t subopt;
808
809 r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
810 if (r < 0)
811 return r;
812
813 r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id);
814 if (r < 0)
815 return r;
816 }
817 return 0;
818 }
819
820 static int dhcp6_lease_parse_message(
821 sd_dhcp6_client *client,
822 sd_dhcp6_lease *lease,
823 const DHCP6Message *message,
824 size_t len) {
825
826 usec_t irt = IRT_DEFAULT;
827 int r;
828
829 assert(client);
830 assert(lease);
831 assert(message);
832 assert(len >= sizeof(DHCP6Message));
833
834 len -= sizeof(DHCP6Message);
835 for (size_t offset = 0; offset < len;) {
836 uint16_t optcode;
837 size_t optlen;
838 const uint8_t *optval;
839
840 if (len - offset < offsetof(DHCP6Option, data)) {
841 log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
842 break;
843 }
844
845 r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
846 if (r < 0)
847 return log_dhcp6_client_errno(client, r,
848 "Failed to parse option header at offset %zu of total length %zu: %m",
849 offset, len);
850
851 switch (optcode) {
852 case SD_DHCP6_OPTION_CLIENTID:
853 if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
854 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
855 dhcp6_message_type_to_string(message->type));
856
857 r = dhcp6_lease_set_clientid(lease, optval, optlen);
858 if (r < 0)
859 return log_dhcp6_client_errno(client, r, "Failed to set client ID: %m");
860
861 break;
862
863 case SD_DHCP6_OPTION_SERVERID:
864 if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
865 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
866 dhcp6_message_type_to_string(message->type));
867
868 r = dhcp6_lease_set_serverid(lease, optval, optlen);
869 if (r < 0)
870 return log_dhcp6_client_errno(client, r, "Failed to set server ID: %m");
871
872 break;
873
874 case SD_DHCP6_OPTION_PREFERENCE:
875 if (optlen != 1)
876 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received invalid length for preference.");
877
878 r = dhcp6_lease_set_preference(lease, optval[0]);
879 if (r < 0)
880 return log_dhcp6_client_errno(client, r, "Failed to set preference: %m");
881
882 break;
883
884 case SD_DHCP6_OPTION_STATUS_CODE: {
885 _cleanup_free_ char *msg = NULL;
886
887 r = dhcp6_option_parse_status(optval, optlen, &msg);
888 if (r < 0)
889 return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
890 if (r > 0)
891 return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
892 "Received %s message with non-zero status%s%s",
893 dhcp6_message_type_to_string(message->type),
894 isempty(msg) ? "." : ": ", strempty(msg));
895 break;
896 }
897 case SD_DHCP6_OPTION_IA_NA: {
898 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
899
900 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
901 log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
902 break;
903 }
904
905 r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
906 if (r == -ENOMEM)
907 return log_oom_debug();
908 if (r < 0) {
909 log_dhcp6_client_errno(client, r, "Failed to parse IA_NA option, ignoring: %m");
910 continue;
911 }
912
913 if (lease->ia_na) {
914 log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
915 continue;
916 }
917
918 free_and_replace_full(lease->ia_na, ia, dhcp6_ia_free);
919 break;
920 }
921 case SD_DHCP6_OPTION_IA_PD: {
922 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
923
924 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
925 log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
926 break;
927 }
928
929 r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
930 if (r == -ENOMEM)
931 return log_oom_debug();
932 if (r < 0) {
933 log_dhcp6_client_errno(client, r, "Failed to parse IA_PD option, ignoring: %m");
934 continue;
935 }
936
937 if (lease->ia_pd) {
938 log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
939 continue;
940 }
941
942 free_and_replace_full(lease->ia_pd, ia, dhcp6_ia_free);
943 break;
944 }
945 case SD_DHCP6_OPTION_RAPID_COMMIT:
946 if (optlen != 0)
947 log_dhcp6_client(client, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen);
948
949 r = dhcp6_lease_set_rapid_commit(lease);
950 if (r < 0)
951 return log_dhcp6_client_errno(client, r, "Failed to set rapid commit flag: %m");
952
953 break;
954
955 case SD_DHCP6_OPTION_DNS_SERVER:
956 r = dhcp6_lease_add_dns(lease, optval, optlen);
957 if (r < 0)
958 log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
959
960 break;
961
962 case SD_DHCP6_OPTION_DOMAIN:
963 r = dhcp6_lease_add_domains(lease, optval, optlen);
964 if (r < 0)
965 log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
966
967 break;
968
969 case SD_DHCP6_OPTION_NTP_SERVER:
970 r = dhcp6_lease_add_ntp(lease, optval, optlen);
971 if (r < 0)
972 log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
973
974 break;
975
976 case SD_DHCP6_OPTION_SNTP_SERVER:
977 r = dhcp6_lease_add_sntp(lease, optval, optlen);
978 if (r < 0)
979 log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
980
981 break;
982
983 case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS:
984 r = dhcp6_lease_add_sip_addrs(lease, optval, optlen);
985 if (r < 0)
986 log_dhcp6_client_errno(client, r, "Failed to parse SIP server address option, ignoring: %m");
987
988 break;
989
990 case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME:
991 r = dhcp6_lease_add_sip_domains(lease, optval, optlen);
992 if (r < 0)
993 log_dhcp6_client_errno(client, r, "Failed to parse SIP server domain name option, ignoring: %m");
994 break;
995
996 case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
997 r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
998 if (r < 0)
999 log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
1000 break;
1001
1002 case SD_DHCP6_OPTION_CLIENT_FQDN:
1003 r = dhcp6_lease_set_fqdn(lease, optval, optlen);
1004 if (r < 0)
1005 log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
1006
1007 break;
1008
1009 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
1010 if (optlen != 4)
1011 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1012 "Received information refresh time option with an invalid length (%zu).", optlen);
1013
1014 irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
1015 break;
1016
1017 case SD_DHCP6_OPTION_V6_DNR:
1018 r = dhcp6_lease_add_dnr(lease, optval, optlen);
1019 if (r < 0)
1020 return log_dhcp6_client_errno(client, r, "Failed to parse DNR option, ignoring: %m");
1021 if (r == 0)
1022 log_dhcp6_client(client, "Received ADN-only DNRv6 option, ignoring.");
1023
1024 break;
1025
1026 case SD_DHCP6_OPTION_VENDOR_OPTS:
1027 r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
1028 if (r < 0)
1029 log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m");
1030
1031 break;
1032 }
1033 }
1034
1035 uint8_t *clientid;
1036 size_t clientid_len;
1037 if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
1038 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1039 "%s message does not contain client ID. Ignoring.",
1040 dhcp6_message_type_to_string(message->type));
1041
1042 if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
1043 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1044 "The client ID in %s message does not match. Ignoring.",
1045 dhcp6_message_type_to_string(message->type));
1046
1047 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1048 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
1049 log_dhcp6_client(client, "New information request will be refused in %s.",
1050 FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
1051
1052 } else {
1053 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1054 if (r < 0)
1055 return log_dhcp6_client_errno(client, r, "%s has no server id",
1056 dhcp6_message_type_to_string(message->type));
1057
1058 if (!lease->ia_na && !lease->ia_pd)
1059 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1060 "No IA_PD prefix or IA_NA address received. Ignoring.");
1061
1062 dhcp6_lease_set_lifetime(lease);
1063 }
1064
1065 return 0;
1066 }
1067
1068 static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
1069 if (!lease)
1070 return NULL;
1071
1072 set_free(lease->vendor_options);
1073 free(lease->sorted_vendor_options);
1074 free(lease->clientid);
1075 free(lease->serverid);
1076 dhcp6_ia_free(lease->ia_na);
1077 dhcp6_ia_free(lease->ia_pd);
1078 free(lease->dns);
1079 dns_resolver_done_many(lease->dnr, lease->n_dnr);
1080 free(lease->fqdn);
1081 free(lease->captive_portal);
1082 strv_free(lease->domains);
1083 free(lease->ntp);
1084 strv_free(lease->ntp_fqdn);
1085 free(lease->sntp);
1086 free(lease->sip);
1087 strv_free(lease->sip_domains);
1088
1089 return mfree(lease);
1090 }
1091
1092 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
1093
1094 int dhcp6_lease_new(sd_dhcp6_lease **ret) {
1095 sd_dhcp6_lease *lease;
1096
1097 assert(ret);
1098
1099 lease = new(sd_dhcp6_lease, 1);
1100 if (!lease)
1101 return -ENOMEM;
1102
1103 *lease = (sd_dhcp6_lease) {
1104 .n_ref = 1,
1105 };
1106
1107 *ret = lease;
1108 return 0;
1109 }
1110
1111 int dhcp6_lease_new_from_message(
1112 sd_dhcp6_client *client,
1113 const DHCP6Message *message,
1114 size_t len,
1115 const triple_timestamp *timestamp,
1116 const struct in6_addr *server_address,
1117 sd_dhcp6_lease **ret) {
1118
1119 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1120 int r;
1121
1122 assert(client);
1123 assert(message);
1124 assert(len >= sizeof(DHCP6Message));
1125 assert(ret);
1126
1127 r = dhcp6_lease_new(&lease);
1128 if (r < 0)
1129 return r;
1130
1131 dhcp6_lease_set_timestamp(lease, timestamp);
1132 dhcp6_lease_set_server_address(lease, server_address);
1133
1134 r = dhcp6_lease_parse_message(client, lease, message, len);
1135 if (r < 0)
1136 return r;
1137
1138 *ret = TAKE_PTR(lease);
1139 return 0;
1140 }