1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
6 #include "sd-dhcp6-option.h"
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"
15 #include "sort-util.h"
16 #include "string-util.h"
18 #include "unaligned.h"
20 #define IRT_DEFAULT (1 * USEC_PER_DAY)
21 #define IRT_MINIMUM (600 * USEC_PER_SEC)
23 static void dhcp6_lease_set_timestamp(sd_dhcp6_lease
*lease
, const triple_timestamp
*timestamp
) {
26 if (timestamp
&& triple_timestamp_is_set(timestamp
))
27 lease
->timestamp
= *timestamp
;
29 triple_timestamp_now(&lease
->timestamp
);
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
);
38 if (!triple_timestamp_is_set(&lease
->timestamp
))
41 *ret
= triple_timestamp_by_clock(&lease
->timestamp
, clock
);
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
;
49 assert(lease
->ia_na
|| lease
->ia_pd
);
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));
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));
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));
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));
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;
74 lease
->lifetime_valid
= min_valid_lt
;
75 lease
->lifetime_t1
= t1
;
76 lease
->lifetime_t2
= t2
;
79 #define DEFINE_GET_TIME_FUNCTIONS(name, val) \
80 int sd_dhcp6_lease_get_##name( \
81 sd_dhcp6_lease *lease, \
84 assert_return(lease, -EINVAL); \
86 if (!lease->ia_na && !lease->ia_pd) \
94 int sd_dhcp6_lease_get_##name##_timestamp( \
95 sd_dhcp6_lease *lease, \
102 assert_return(lease, -EINVAL); \
104 r = sd_dhcp6_lease_get_##name(lease, &s); \
108 r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
113 *ret = time_span_to_stamp(s, t); \
117 DEFINE_GET_TIME_FUNCTIONS(t1
, lifetime_t1
);
118 DEFINE_GET_TIME_FUNCTIONS(t2
, lifetime_t1
);
119 DEFINE_GET_TIME_FUNCTIONS(valid_lifetime
, lifetime_valid
);
121 static void dhcp6_lease_set_server_address(sd_dhcp6_lease
*lease
, const struct in6_addr
*server_address
) {
125 lease
->server_address
= *server_address
;
127 lease
->server_address
= (struct in6_addr
) {};
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
);
134 *ret
= lease
->server_address
;
138 void dhcp6_ia_clear_addresses(DHCP6IA
*ia
) {
141 LIST_FOREACH(addresses
, a
, ia
->addresses
)
144 ia
->addresses
= NULL
;
147 DHCP6IA
*dhcp6_ia_free(DHCP6IA
*ia
) {
151 dhcp6_ia_clear_addresses(ia
);
156 int dhcp6_lease_set_clientid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
157 uint8_t *clientid
= NULL
;
160 assert(id
|| len
== 0);
163 clientid
= memdup(id
, len
);
168 free_and_replace(lease
->clientid
, clientid
);
169 lease
->clientid_len
= len
;
174 int dhcp6_lease_get_clientid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
177 if (!lease
->clientid
)
181 *ret_id
= lease
->clientid
;
183 *ret_len
= lease
->clientid_len
;
188 int dhcp6_lease_set_serverid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
189 uint8_t *serverid
= NULL
;
192 assert(id
|| len
== 0);
195 serverid
= memdup(id
, len
);
200 free_and_replace(lease
->serverid
, serverid
);
201 lease
->serverid_len
= len
;
206 int dhcp6_lease_get_serverid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
209 if (!lease
->serverid
)
213 *ret_id
= lease
->serverid
;
215 *ret_len
= lease
->serverid_len
;
219 int dhcp6_lease_set_preference(sd_dhcp6_lease
*lease
, uint8_t preference
) {
222 lease
->preference
= preference
;
226 int dhcp6_lease_get_preference(sd_dhcp6_lease
*lease
, uint8_t *ret
) {
230 *ret
= lease
->preference
;
234 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease
*lease
) {
237 lease
->rapid_commit
= true;
241 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease
*lease
, bool *ret
) {
245 *ret
= lease
->rapid_commit
;
249 int sd_dhcp6_lease_get_address(sd_dhcp6_lease
*lease
, struct in6_addr
*ret
) {
250 assert_return(lease
, -EINVAL
);
252 if (!lease
->addr_iter
)
256 *ret
= lease
->addr_iter
->iaaddr
.address
;
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
) {
265 const struct iaaddr
*a
;
267 assert_return(lease
, -EINVAL
);
269 if (!lease
->addr_iter
)
272 a
= &lease
->addr_iter
->iaaddr
;
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);
281 int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease
*lease
) {
285 lease
->addr_iter
= lease
->ia_na
? lease
->ia_na
->addresses
: NULL
;
286 return !!lease
->addr_iter
;
289 int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease
*lease
) {
290 if (!lease
|| !lease
->addr_iter
)
293 lease
->addr_iter
= lease
->addr_iter
->addresses_next
;
294 return !!lease
->addr_iter
;
297 int sd_dhcp6_lease_has_address(sd_dhcp6_lease
*lease
) {
298 return lease
&& lease
->ia_na
;
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
) {
306 const struct iapdprefix
*a
;
308 assert_return(lease
, -EINVAL
);
310 if (!lease
->prefix_iter
)
313 a
= &lease
->prefix_iter
->iapdprefix
;
316 *ret_prefix
= a
->address
;
318 *ret_prefix_len
= a
->prefixlen
;
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
) {
327 const struct iapdprefix
*a
;
329 assert_return(lease
, -EINVAL
);
331 if (!lease
->prefix_iter
)
334 a
= &lease
->prefix_iter
->iapdprefix
;
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);
343 int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease
*lease
) {
347 lease
->prefix_iter
= lease
->ia_pd
? lease
->ia_pd
->addresses
: NULL
;
348 return !!lease
->prefix_iter
;
351 int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease
*lease
) {
352 if (!lease
|| !lease
->prefix_iter
)
355 lease
->prefix_iter
= lease
->prefix_iter
->addresses_next
;
356 return !!lease
->prefix_iter
;
359 #define DEFINE_GET_TIMESTAMP2(name) \
360 int sd_dhcp6_lease_get_##name##_lifetime_timestamp( \
361 sd_dhcp6_lease *lease, \
363 uint64_t *ret_lifetime_preferred, \
364 uint64_t *ret_lifetime_valid) { \
369 assert_return(lease, -EINVAL); \
371 r = sd_dhcp6_lease_get_##name##_lifetime( \
373 ret_lifetime_preferred ? &p : NULL, \
374 ret_lifetime_valid ? &v : NULL); \
378 r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
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); \
389 DEFINE_GET_TIMESTAMP2(address
);
390 DEFINE_GET_TIMESTAMP2(pd
);
392 int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease
*lease
) {
393 return lease
&& lease
->ia_pd
;
396 int dhcp6_lease_add_dns(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
398 assert(optval
|| optlen
== 0);
403 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->dns
, &lease
->dns_count
);
406 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
407 assert_return(lease
, -EINVAL
);
415 return lease
->dns_count
;
418 int dhcp6_lease_add_domains(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
419 _cleanup_strv_free_
char **domains
= NULL
;
423 assert(optval
|| optlen
== 0);
428 r
= dhcp6_option_parse_domainname_list(optval
, optlen
, &domains
);
432 return strv_extend_strv_consume(&lease
->domains
, TAKE_PTR(domains
), /* filter_duplicates = */ true);
435 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease
*lease
, char ***ret
) {
436 assert_return(lease
, -EINVAL
);
437 assert_return(ret
, -EINVAL
);
442 *ret
= lease
->domains
;
443 return strv_length(lease
->domains
);
446 static int dhcp6_lease_add_dnr(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
451 _cleanup_(sd_dns_resolver_done
) sd_dns_resolver res
= {};
456 if (optlen
- offset
< sizeof(uint16_t))
458 res
.priority
= unaligned_read_be16(optval
+ offset
);
459 offset
+= sizeof(uint16_t);
462 if (optlen
- offset
< sizeof(uint16_t))
464 size_t ilen
= unaligned_read_be16(optval
+ offset
);
465 offset
+= sizeof(uint16_t);
466 if (offset
+ ilen
> optlen
)
469 r
= dhcp6_option_parse_domainname(optval
+ offset
, ilen
, &res
.auth_name
);
472 r
= dns_name_is_valid_ldh(res
.auth_name
);
479 /* RFC9463 § 3.1.6: adn only mode */
480 if (offset
== optlen
)
484 if (optlen
- offset
< sizeof(uint16_t))
486 ilen
= unaligned_read_be16(optval
+ offset
);
487 offset
+= sizeof(uint16_t);
488 if (offset
+ ilen
> optlen
)
491 _cleanup_free_
struct in6_addr
*addrs
= NULL
;
494 r
= dhcp6_option_parse_addresses(optval
+ offset
, ilen
, &addrs
, &n_addrs
);
501 res
.addrs
= new(union in_addr_union
, n_addrs
);
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
))
513 res
.n_addrs
= n_addrs
;
514 res
.family
= AF_INET6
;
517 r
= dnr_parse_svc_params(optval
+ offset
, optlen
-offset
, &res
);
521 /* Append this resolver */
522 if (!GREEDY_REALLOC(lease
->dnr
, lease
->n_dnr
+1))
525 lease
->dnr
[lease
->n_dnr
++] = TAKE_STRUCT(res
);
527 typesafe_qsort(lease
->dnr
, lease
->n_dnr
, dns_resolver_prio_compare
);
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
);
543 int dhcp6_lease_add_ntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
547 assert(optval
|| optlen
== 0);
549 for (size_t offset
= 0; offset
< optlen
;) {
550 const uint8_t *subval
;
554 r
= dhcp6_option_parse(optval
, optlen
, &offset
, &subopt
, &sublen
, &subval
);
559 case DHCP6_NTP_SUBOPTION_SRV_ADDR
:
560 case DHCP6_NTP_SUBOPTION_MC_ADDR
:
564 r
= dhcp6_option_parse_addresses(subval
, sublen
, &lease
->ntp
, &lease
->ntp_count
);
570 case DHCP6_NTP_SUBOPTION_SRV_FQDN
: {
571 _cleanup_free_
char *server
= NULL
;
573 r
= dhcp6_option_parse_domainname(subval
, sublen
, &server
);
577 if (strv_contains(lease
->ntp_fqdn
, server
))
580 r
= strv_consume(&lease
->ntp_fqdn
, TAKE_PTR(server
));
591 int dhcp6_lease_add_sntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
593 assert(optval
|| optlen
== 0);
598 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
599 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->sntp
, &lease
->sntp_count
);
602 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
603 assert_return(lease
, -EINVAL
);
608 return lease
->ntp_count
;
611 if (lease
->sntp
&& !lease
->ntp_fqdn
) {
612 /* Fallback to the deprecated SNTP option. */
615 return lease
->sntp_count
;
621 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease
*lease
, char ***ret
) {
622 assert_return(lease
, -EINVAL
);
624 if (!lease
->ntp_fqdn
)
628 *ret
= lease
->ntp_fqdn
;
629 return strv_length(lease
->ntp_fqdn
);
632 int dhcp6_lease_set_fqdn(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
637 assert(optval
|| optlen
== 0);
645 /* Ignore the flags field, it doesn't carry any useful
646 information for clients. */
647 r
= dhcp6_option_parse_domainname(optval
+ 1, optlen
- 1, &fqdn
);
651 return free_and_replace(lease
->fqdn
, fqdn
);
654 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease
*lease
, const char **ret
) {
655 assert_return(lease
, -EINVAL
);
656 assert_return(ret
, -EINVAL
);
665 int dhcp6_lease_set_captive_portal(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
666 _cleanup_free_
char *uri
= NULL
;
670 assert(optval
|| optlen
== 0);
672 r
= dhcp6_option_parse_string(optval
, optlen
, &uri
);
676 if (uri
&& !in_charset(uri
, URI_VALID
))
679 return free_and_replace(lease
->captive_portal
, uri
);
682 int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease
*lease
, const char **ret
) {
683 assert_return(lease
, -EINVAL
);
684 assert_return(ret
, -EINVAL
);
686 if (!lease
->captive_portal
)
689 *ret
= lease
->captive_portal
;
693 int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease
*lease
, sd_dhcp6_option
***ret
) {
696 assert_return(lease
, -EINVAL
);
698 if (set_isempty(lease
->vendor_options
))
702 if (!lease
->sorted_vendor_options
) {
703 r
= set_dump_sorted(lease
->vendor_options
, (void***) &lease
->sorted_vendor_options
, NULL
);
708 *ret
= lease
->sorted_vendor_options
;
711 return set_size(lease
->vendor_options
);
714 static int dhcp6_lease_insert_vendor_option(
715 sd_dhcp6_lease
*lease
,
716 uint16_t option_code
,
719 uint32_t enterprise_id
) {
721 _cleanup_(sd_dhcp6_option_unrefp
) sd_dhcp6_option
*option
= NULL
;
725 option
= new(sd_dhcp6_option
, 1);
729 *option
= (sd_dhcp6_option
) {
731 .enterprise_identifier
= enterprise_id
,
732 .option
= option_code
,
735 option
->data
= memdup_suffix0(data
, len
);
739 return set_ensure_consume(&lease
->vendor_options
, &dhcp6_option_hash_ops
, TAKE_PTR(option
));
742 static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
744 uint32_t enterprise_id
;
747 assert(optval
|| optlen
== 0);
749 if (optlen
< sizeof(be32_t
))
752 enterprise_id
= unaligned_read_be32(optval
);
754 for (size_t offset
= 4; offset
< optlen
;) {
755 const uint8_t *subval
;
759 r
= dhcp6_option_parse(optval
, optlen
, &offset
, &subopt
, &sublen
, &subval
);
763 r
= dhcp6_lease_insert_vendor_option(lease
, subopt
, subval
, sublen
, enterprise_id
);
770 static int dhcp6_lease_parse_message(
771 sd_dhcp6_client
*client
,
772 sd_dhcp6_lease
*lease
,
773 const DHCP6Message
*message
,
776 usec_t irt
= IRT_DEFAULT
;
782 assert(len
>= sizeof(DHCP6Message
));
784 len
-= sizeof(DHCP6Message
);
785 for (size_t offset
= 0; offset
< len
;) {
788 const uint8_t *optval
;
790 if (len
- offset
< offsetof(DHCP6Option
, data
)) {
791 log_dhcp6_client(client
, "Ignoring %zu invalid byte(s) at the end of the packet", len
- offset
);
795 r
= dhcp6_option_parse(message
->options
, len
, &offset
, &optcode
, &optlen
, &optval
);
797 return log_dhcp6_client_errno(client
, r
,
798 "Failed to parse option header at offset %zu of total length %zu: %m",
802 case SD_DHCP6_OPTION_CLIENTID
:
803 if (dhcp6_lease_get_clientid(lease
, NULL
, NULL
) >= 0)
804 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple client IDs",
805 dhcp6_message_type_to_string(message
->type
));
807 r
= dhcp6_lease_set_clientid(lease
, optval
, optlen
);
809 return log_dhcp6_client_errno(client
, r
, "Failed to set client ID: %m");
813 case SD_DHCP6_OPTION_SERVERID
:
814 if (dhcp6_lease_get_serverid(lease
, NULL
, NULL
) >= 0)
815 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple server IDs",
816 dhcp6_message_type_to_string(message
->type
));
818 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
820 return log_dhcp6_client_errno(client
, r
, "Failed to set server ID: %m");
824 case SD_DHCP6_OPTION_PREFERENCE
:
826 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "Received invalid length for preference.");
828 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
830 return log_dhcp6_client_errno(client
, r
, "Failed to set preference: %m");
834 case SD_DHCP6_OPTION_STATUS_CODE
: {
835 _cleanup_free_
char *msg
= NULL
;
837 r
= dhcp6_option_parse_status(optval
, optlen
, &msg
);
839 return log_dhcp6_client_errno(client
, r
, "Failed to parse status code: %m");
841 return log_dhcp6_client_errno(client
, dhcp6_message_status_to_errno(r
),
842 "Received %s message with non-zero status%s%s",
843 dhcp6_message_type_to_string(message
->type
),
844 isempty(msg
) ? "." : ": ", strempty(msg
));
847 case SD_DHCP6_OPTION_IA_NA
: {
848 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
850 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
851 log_dhcp6_client(client
, "Ignoring IA NA option in information requesting mode.");
855 r
= dhcp6_option_parse_ia(client
, client
->ia_na
.header
.id
, optcode
, optlen
, optval
, &ia
);
857 return log_oom_debug();
859 log_dhcp6_client_errno(client
, r
, "Failed to parse IA_NA option, ignoring: %m");
864 log_dhcp6_client(client
, "Received duplicate matching IA_NA option, ignoring.");
868 free_and_replace_full(lease
->ia_na
, ia
, dhcp6_ia_free
);
871 case SD_DHCP6_OPTION_IA_PD
: {
872 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
874 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
875 log_dhcp6_client(client
, "Ignoring IA PD option in information requesting mode.");
879 r
= dhcp6_option_parse_ia(client
, client
->ia_pd
.header
.id
, optcode
, optlen
, optval
, &ia
);
881 return log_oom_debug();
883 log_dhcp6_client_errno(client
, r
, "Failed to parse IA_PD option, ignoring: %m");
888 log_dhcp6_client(client
, "Received duplicate matching IA_PD option, ignoring.");
892 free_and_replace_full(lease
->ia_pd
, ia
, dhcp6_ia_free
);
895 case SD_DHCP6_OPTION_RAPID_COMMIT
:
897 log_dhcp6_client(client
, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen
);
899 r
= dhcp6_lease_set_rapid_commit(lease
);
901 return log_dhcp6_client_errno(client
, r
, "Failed to set rapid commit flag: %m");
905 case SD_DHCP6_OPTION_DNS_SERVER
:
906 r
= dhcp6_lease_add_dns(lease
, optval
, optlen
);
908 log_dhcp6_client_errno(client
, r
, "Failed to parse DNS server option, ignoring: %m");
912 case SD_DHCP6_OPTION_DOMAIN
:
913 r
= dhcp6_lease_add_domains(lease
, optval
, optlen
);
915 log_dhcp6_client_errno(client
, r
, "Failed to parse domain list option, ignoring: %m");
919 case SD_DHCP6_OPTION_NTP_SERVER
:
920 r
= dhcp6_lease_add_ntp(lease
, optval
, optlen
);
922 log_dhcp6_client_errno(client
, r
, "Failed to parse NTP server option, ignoring: %m");
926 case SD_DHCP6_OPTION_SNTP_SERVER
:
927 r
= dhcp6_lease_add_sntp(lease
, optval
, optlen
);
929 log_dhcp6_client_errno(client
, r
, "Failed to parse SNTP server option, ignoring: %m");
933 case SD_DHCP6_OPTION_CAPTIVE_PORTAL
:
934 r
= dhcp6_lease_set_captive_portal(lease
, optval
, optlen
);
936 log_dhcp6_client_errno(client
, r
, "Failed to parse captive portal option, ignoring: %m");
939 case SD_DHCP6_OPTION_CLIENT_FQDN
:
940 r
= dhcp6_lease_set_fqdn(lease
, optval
, optlen
);
942 log_dhcp6_client_errno(client
, r
, "Failed to parse FQDN option, ignoring: %m");
946 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
948 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
949 "Received information refresh time option with an invalid length (%zu).", optlen
);
951 irt
= unaligned_be32_sec_to_usec(optval
, /* max_as_infinity = */ false);
954 case SD_DHCP6_OPTION_V6_DNR
:
955 r
= dhcp6_lease_add_dnr(lease
, optval
, optlen
);
957 return log_dhcp6_client_errno(client
, r
, "Failed to parse DNR option, ignoring: %m");
959 log_dhcp6_client(client
, "Received ADN-only DNRv6 option, ignoring.");
963 case SD_DHCP6_OPTION_VENDOR_OPTS
:
964 r
= dhcp6_lease_add_vendor_option(lease
, optval
, optlen
);
966 log_dhcp6_client_errno(client
, r
, "Failed to parse vendor option, ignoring: %m");
974 if (dhcp6_lease_get_clientid(lease
, &clientid
, &clientid_len
) < 0)
975 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
976 "%s message does not contain client ID. Ignoring.",
977 dhcp6_message_type_to_string(message
->type
));
979 if (memcmp_nn(clientid
, clientid_len
, &client
->duid
.duid
, client
->duid
.size
) != 0)
980 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
981 "The client ID in %s message does not match. Ignoring.",
982 dhcp6_message_type_to_string(message
->type
));
984 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
985 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
986 log_dhcp6_client(client
, "New information request will be refused in %s.",
987 FORMAT_TIMESPAN(client
->information_refresh_time_usec
, USEC_PER_SEC
));
990 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
992 return log_dhcp6_client_errno(client
, r
, "%s has no server id",
993 dhcp6_message_type_to_string(message
->type
));
995 if (!lease
->ia_na
&& !lease
->ia_pd
)
996 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
997 "No IA_PD prefix or IA_NA address received. Ignoring.");
999 dhcp6_lease_set_lifetime(lease
);
1005 static sd_dhcp6_lease
*dhcp6_lease_free(sd_dhcp6_lease
*lease
) {
1009 set_free(lease
->vendor_options
);
1010 free(lease
->sorted_vendor_options
);
1011 free(lease
->clientid
);
1012 free(lease
->serverid
);
1013 dhcp6_ia_free(lease
->ia_na
);
1014 dhcp6_ia_free(lease
->ia_pd
);
1016 dns_resolver_done_many(lease
->dnr
, lease
->n_dnr
);
1018 free(lease
->captive_portal
);
1019 strv_free(lease
->domains
);
1021 strv_free(lease
->ntp_fqdn
);
1024 return mfree(lease
);
1027 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease
, sd_dhcp6_lease
, dhcp6_lease_free
);
1029 int dhcp6_lease_new(sd_dhcp6_lease
**ret
) {
1030 sd_dhcp6_lease
*lease
;
1034 lease
= new(sd_dhcp6_lease
, 1);
1038 *lease
= (sd_dhcp6_lease
) {
1046 int dhcp6_lease_new_from_message(
1047 sd_dhcp6_client
*client
,
1048 const DHCP6Message
*message
,
1050 const triple_timestamp
*timestamp
,
1051 const struct in6_addr
*server_address
,
1052 sd_dhcp6_lease
**ret
) {
1054 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1059 assert(len
>= sizeof(DHCP6Message
));
1062 r
= dhcp6_lease_new(&lease
);
1066 dhcp6_lease_set_timestamp(lease
, timestamp
);
1067 dhcp6_lease_set_server_address(lease
, server_address
);
1069 r
= dhcp6_lease_parse_message(client
, lease
, message
, len
);
1073 *ret
= TAKE_PTR(lease
);