1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
8 #include "alloc-util.h"
9 #include "dhcp6-lease-internal.h"
10 #include "dhcp6-protocol.h"
14 #define IRT_DEFAULT (1 * USEC_PER_DAY)
15 #define IRT_MINIMUM (600 * USEC_PER_SEC)
17 static void dhcp6_lease_set_timestamp(sd_dhcp6_lease
*lease
, const triple_timestamp
*timestamp
) {
20 if (timestamp
&& triple_timestamp_is_set(timestamp
))
21 lease
->timestamp
= *timestamp
;
23 triple_timestamp_get(&lease
->timestamp
);
26 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease
*lease
, clockid_t clock
, uint64_t *ret
) {
27 assert_return(lease
, -EINVAL
);
28 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
29 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
30 assert_return(ret
, -EINVAL
);
32 if (!triple_timestamp_is_set(&lease
->timestamp
))
35 *ret
= triple_timestamp_by_clock(&lease
->timestamp
, clock
);
39 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease
*lease
) {
40 uint32_t t1
= UINT32_MAX
, t2
= UINT32_MAX
, min_valid_lt
= UINT32_MAX
;
44 assert(lease
->ia_na
|| lease
->ia_pd
);
47 t1
= MIN(t1
, be32toh(lease
->ia_na
->header
.lifetime_t1
));
48 t2
= MIN(t2
, be32toh(lease
->ia_na
->header
.lifetime_t2
));
50 LIST_FOREACH(addresses
, a
, lease
->ia_na
->addresses
)
51 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iaaddr
.lifetime_valid
));
55 t1
= MIN(t1
, be32toh(lease
->ia_pd
->header
.lifetime_t1
));
56 t2
= MIN(t2
, be32toh(lease
->ia_pd
->header
.lifetime_t2
));
58 LIST_FOREACH(addresses
, a
, lease
->ia_pd
->addresses
)
59 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iapdprefix
.lifetime_valid
));
62 if (t2
== 0 || t2
> min_valid_lt
) {
63 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
64 * then adjust lifetime with it. */
65 t1
= min_valid_lt
/ 2;
66 t2
= min_valid_lt
/ 10 * 8;
69 assert(t2
<= min_valid_lt
);
70 lease
->max_retransmit_duration
= (min_valid_lt
- t2
) * USEC_PER_SEC
;
72 lease
->lifetime_t1
= t1
== UINT32_MAX
? USEC_INFINITY
: t1
* USEC_PER_SEC
;
73 lease
->lifetime_t2
= t2
== UINT32_MAX
? USEC_INFINITY
: t2
* USEC_PER_SEC
;
76 int dhcp6_lease_get_lifetime(sd_dhcp6_lease
*lease
, usec_t
*ret_t1
, usec_t
*ret_t2
) {
79 if (!lease
->ia_na
&& !lease
->ia_pd
)
83 *ret_t1
= lease
->lifetime_t1
;
85 *ret_t2
= lease
->lifetime_t2
;
89 int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease
*lease
, usec_t
*ret
) {
92 if (!lease
->ia_na
&& !lease
->ia_pd
)
96 *ret
= lease
->max_retransmit_duration
;
100 static void dhcp6_lease_set_server_address(sd_dhcp6_lease
*lease
, const struct in6_addr
*server_address
) {
104 lease
->server_address
= *server_address
;
106 lease
->server_address
= (struct in6_addr
) {};
109 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease
*lease
, struct in6_addr
*ret
) {
110 assert_return(lease
, -EINVAL
);
111 assert_return(ret
, -EINVAL
);
113 *ret
= lease
->server_address
;
117 void dhcp6_ia_clear_addresses(DHCP6IA
*ia
) {
122 LIST_FOREACH_SAFE(addresses
, a
, n
, ia
->addresses
)
125 ia
->addresses
= NULL
;
128 DHCP6IA
*dhcp6_ia_free(DHCP6IA
*ia
) {
132 dhcp6_ia_clear_addresses(ia
);
137 int dhcp6_lease_set_clientid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
138 uint8_t *clientid
= NULL
;
141 assert(id
|| len
== 0);
144 clientid
= memdup(id
, len
);
149 free_and_replace(lease
->clientid
, clientid
);
150 lease
->clientid_len
= len
;
155 int dhcp6_lease_get_clientid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
158 if (!lease
->clientid
)
162 *ret_id
= lease
->clientid
;
164 *ret_len
= lease
->clientid_len
;
169 int dhcp6_lease_set_serverid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
170 uint8_t *serverid
= NULL
;
173 assert(id
|| len
== 0);
176 serverid
= memdup(id
, len
);
181 free_and_replace(lease
->serverid
, serverid
);
182 lease
->serverid_len
= len
;
187 int dhcp6_lease_get_serverid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
190 if (!lease
->serverid
)
194 *ret_id
= lease
->serverid
;
196 *ret_len
= lease
->serverid_len
;
200 int dhcp6_lease_set_preference(sd_dhcp6_lease
*lease
, uint8_t preference
) {
203 lease
->preference
= preference
;
207 int dhcp6_lease_get_preference(sd_dhcp6_lease
*lease
, uint8_t *ret
) {
211 *ret
= lease
->preference
;
215 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease
*lease
) {
218 lease
->rapid_commit
= true;
222 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease
*lease
, bool *ret
) {
226 *ret
= lease
->rapid_commit
;
230 int sd_dhcp6_lease_get_address(
231 sd_dhcp6_lease
*lease
,
232 struct in6_addr
*ret_addr
,
233 uint32_t *ret_lifetime_preferred
,
234 uint32_t *ret_lifetime_valid
) {
236 assert_return(lease
, -EINVAL
);
238 if (!lease
->addr_iter
)
242 *ret_addr
= lease
->addr_iter
->iaaddr
.address
;
243 if (ret_lifetime_preferred
)
244 *ret_lifetime_preferred
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_preferred
);
245 if (ret_lifetime_valid
)
246 *ret_lifetime_valid
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_valid
);
248 lease
->addr_iter
= lease
->addr_iter
->addresses_next
;
252 void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease
*lease
) {
254 lease
->addr_iter
= lease
->ia_na
? lease
->ia_na
->addresses
: NULL
;
257 int sd_dhcp6_lease_get_pd(
258 sd_dhcp6_lease
*lease
,
259 struct in6_addr
*ret_prefix
,
260 uint8_t *ret_prefix_len
,
261 uint32_t *ret_lifetime_preferred
,
262 uint32_t *ret_lifetime_valid
) {
264 assert_return(lease
, -EINVAL
);
266 if (!lease
->prefix_iter
)
270 *ret_prefix
= lease
->prefix_iter
->iapdprefix
.address
;
272 *ret_prefix_len
= lease
->prefix_iter
->iapdprefix
.prefixlen
;
273 if (ret_lifetime_preferred
)
274 *ret_lifetime_preferred
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_preferred
);
275 if (ret_lifetime_valid
)
276 *ret_lifetime_valid
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_valid
);
278 lease
->prefix_iter
= lease
->prefix_iter
->addresses_next
;
282 void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease
*lease
) {
284 lease
->prefix_iter
= lease
->ia_pd
? lease
->ia_pd
->addresses
: NULL
;
287 int dhcp6_lease_add_dns(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
289 assert(optval
|| optlen
== 0);
294 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->dns
, &lease
->dns_count
);
297 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
298 assert_return(lease
, -EINVAL
);
306 return lease
->dns_count
;
309 int dhcp6_lease_add_domains(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
310 _cleanup_strv_free_
char **domains
= NULL
;
314 assert(optval
|| optlen
== 0);
319 r
= dhcp6_option_parse_domainname_list(optval
, optlen
, &domains
);
323 return strv_extend_strv(&lease
->domains
, domains
, true);
326 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease
*lease
, char ***ret
) {
327 assert_return(lease
, -EINVAL
);
328 assert_return(ret
, -EINVAL
);
333 *ret
= lease
->domains
;
334 return strv_length(lease
->domains
);
337 int dhcp6_lease_add_ntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
341 assert(optval
|| optlen
== 0);
343 for (size_t offset
= 0; offset
< optlen
;) {
344 const uint8_t *subval
;
348 r
= dhcp6_option_parse(optval
, optlen
, &offset
, &subopt
, &sublen
, &subval
);
353 case DHCP6_NTP_SUBOPTION_SRV_ADDR
:
354 case DHCP6_NTP_SUBOPTION_MC_ADDR
:
358 r
= dhcp6_option_parse_addresses(subval
, sublen
, &lease
->ntp
, &lease
->ntp_count
);
364 case DHCP6_NTP_SUBOPTION_SRV_FQDN
: {
365 _cleanup_free_
char *server
= NULL
;
367 r
= dhcp6_option_parse_domainname(subval
, sublen
, &server
);
371 if (strv_contains(lease
->ntp_fqdn
, server
))
374 r
= strv_consume(&lease
->ntp_fqdn
, TAKE_PTR(server
));
385 int dhcp6_lease_add_sntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
387 assert(optval
|| optlen
== 0);
392 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
393 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->sntp
, &lease
->sntp_count
);
396 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
397 assert_return(lease
, -EINVAL
);
402 return lease
->ntp_count
;
405 if (lease
->sntp
&& !lease
->ntp_fqdn
) {
406 /* Fallback to the deprecated SNTP option. */
409 return lease
->sntp_count
;
415 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease
*lease
, char ***ret
) {
416 assert_return(lease
, -EINVAL
);
418 if (!lease
->ntp_fqdn
)
422 *ret
= lease
->ntp_fqdn
;
423 return strv_length(lease
->ntp_fqdn
);
426 int dhcp6_lease_set_fqdn(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
431 assert(optval
|| optlen
== 0);
439 /* Ignore the flags field, it doesn't carry any useful
440 information for clients. */
441 r
= dhcp6_option_parse_domainname(optval
+ 1, optlen
- 1, &fqdn
);
445 return free_and_replace(lease
->fqdn
, fqdn
);
448 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease
*lease
, const char **ret
) {
449 assert_return(lease
, -EINVAL
);
450 assert_return(ret
, -EINVAL
);
459 static int dhcp6_lease_parse_message(
460 sd_dhcp6_client
*client
,
461 sd_dhcp6_lease
*lease
,
462 const DHCP6Message
*message
,
465 usec_t irt
= IRT_DEFAULT
;
471 assert(len
>= sizeof(DHCP6Message
));
473 len
-= sizeof(DHCP6Message
);
474 for (size_t offset
= 0; offset
< len
;) {
477 const uint8_t *optval
;
479 r
= dhcp6_option_parse(message
->options
, len
, &offset
, &optcode
, &optlen
, &optval
);
484 case SD_DHCP6_OPTION_CLIENTID
:
485 if (dhcp6_lease_get_clientid(lease
, NULL
, NULL
) >= 0)
486 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple client IDs",
487 dhcp6_message_type_to_string(message
->type
));
489 r
= dhcp6_lease_set_clientid(lease
, optval
, optlen
);
495 case SD_DHCP6_OPTION_SERVERID
:
496 if (dhcp6_lease_get_serverid(lease
, NULL
, NULL
) >= 0)
497 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple server IDs",
498 dhcp6_message_type_to_string(message
->type
));
500 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
506 case SD_DHCP6_OPTION_PREFERENCE
:
510 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
516 case SD_DHCP6_OPTION_STATUS_CODE
: {
517 _cleanup_free_
char *msg
= NULL
;
519 r
= dhcp6_option_parse_status(optval
, optlen
, &msg
);
524 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
525 "Received %s message with non-zero status: %s%s%s",
526 dhcp6_message_type_to_string(message
->type
),
527 strempty(msg
), isempty(msg
) ? "" : ": ",
528 dhcp6_message_status_to_string(r
));
531 case SD_DHCP6_OPTION_IA_NA
: {
532 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
534 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
535 log_dhcp6_client(client
, "Ignoring IA NA option in information requesting mode.");
539 r
= dhcp6_option_parse_ia(client
, client
->ia_na
.header
.id
, optcode
, optlen
, optval
, &ia
);
546 log_dhcp6_client(client
, "Received duplicate matching IA_NA option, ignoring.");
550 dhcp6_ia_free(lease
->ia_na
);
551 lease
->ia_na
= TAKE_PTR(ia
);
554 case SD_DHCP6_OPTION_IA_PD
: {
555 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
557 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
558 log_dhcp6_client(client
, "Ignoring IA PD option in information requesting mode.");
562 r
= dhcp6_option_parse_ia(client
, client
->ia_pd
.header
.id
, optcode
, optlen
, optval
, &ia
);
569 log_dhcp6_client(client
, "Received duplicate matching IA_PD option, ignoring.");
573 dhcp6_ia_free(lease
->ia_pd
);
574 lease
->ia_pd
= TAKE_PTR(ia
);
577 case SD_DHCP6_OPTION_RAPID_COMMIT
:
578 r
= dhcp6_lease_set_rapid_commit(lease
);
584 case SD_DHCP6_OPTION_DNS_SERVERS
:
585 r
= dhcp6_lease_add_dns(lease
, optval
, optlen
);
587 log_dhcp6_client_errno(client
, r
, "Failed to parse DNS server option, ignoring: %m");
591 case SD_DHCP6_OPTION_DOMAIN_LIST
:
592 r
= dhcp6_lease_add_domains(lease
, optval
, optlen
);
594 log_dhcp6_client_errno(client
, r
, "Failed to parse domain list option, ignoring: %m");
598 case SD_DHCP6_OPTION_NTP_SERVER
:
599 r
= dhcp6_lease_add_ntp(lease
, optval
, optlen
);
601 log_dhcp6_client_errno(client
, r
, "Failed to parse NTP server option, ignoring: %m");
605 case SD_DHCP6_OPTION_SNTP_SERVERS
:
606 r
= dhcp6_lease_add_sntp(lease
, optval
, optlen
);
608 log_dhcp6_client_errno(client
, r
, "Failed to parse SNTP server option, ignoring: %m");
612 case SD_DHCP6_OPTION_CLIENT_FQDN
:
613 r
= dhcp6_lease_set_fqdn(lease
, optval
, optlen
);
615 log_dhcp6_client_errno(client
, r
, "Failed to parse FQDN option, ignoring: %m");
619 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
623 irt
= unaligned_read_be32((be32_t
*) optval
) * USEC_PER_SEC
;
630 if (dhcp6_lease_get_clientid(lease
, &clientid
, &clientid_len
) < 0)
631 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s message does not contain client ID. Ignoring.",
632 dhcp6_message_type_to_string(message
->type
));
634 if (clientid_len
!= client
->duid_len
||
635 memcmp(clientid
, &client
->duid
, clientid_len
) != 0)
636 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "The client ID in %s message does not match. Ignoring.",
637 dhcp6_message_type_to_string(message
->type
));
639 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
640 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
642 return log_dhcp6_client_errno(client
, r
, "%s has no server id",
643 dhcp6_message_type_to_string(message
->type
));
645 if (!lease
->ia_na
&& !lease
->ia_pd
)
646 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "No IA_PD prefix or IA_NA address received. Ignoring.");
648 dhcp6_lease_set_lifetime(lease
);
651 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
656 static sd_dhcp6_lease
*dhcp6_lease_free(sd_dhcp6_lease
*lease
) {
660 free(lease
->clientid
);
661 free(lease
->serverid
);
662 dhcp6_ia_free(lease
->ia_na
);
663 dhcp6_ia_free(lease
->ia_pd
);
666 strv_free(lease
->domains
);
668 strv_free(lease
->ntp_fqdn
);
674 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease
, sd_dhcp6_lease
, dhcp6_lease_free
);
676 int dhcp6_lease_new(sd_dhcp6_lease
**ret
) {
677 sd_dhcp6_lease
*lease
;
681 lease
= new(sd_dhcp6_lease
, 1);
685 *lease
= (sd_dhcp6_lease
) {
693 int dhcp6_lease_new_from_message(
694 sd_dhcp6_client
*client
,
695 const DHCP6Message
*message
,
697 const triple_timestamp
*timestamp
,
698 const struct in6_addr
*server_address
,
699 sd_dhcp6_lease
**ret
) {
701 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
706 assert(len
>= sizeof(DHCP6Message
));
709 r
= dhcp6_lease_new(&lease
);
713 dhcp6_lease_set_timestamp(lease
, timestamp
);
714 dhcp6_lease_set_server_address(lease
, server_address
);
716 r
= dhcp6_lease_parse_message(client
, lease
, message
, len
);
720 *ret
= TAKE_PTR(lease
);