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-internal.h"
10 #include "dhcp6-lease-internal.h"
13 #define IRT_DEFAULT (1 * USEC_PER_DAY)
14 #define IRT_MINIMUM (600 * USEC_PER_SEC)
16 static void dhcp6_lease_set_timestamp(sd_dhcp6_lease
*lease
, const triple_timestamp
*timestamp
) {
19 if (timestamp
&& triple_timestamp_is_set(timestamp
))
20 lease
->timestamp
= *timestamp
;
22 triple_timestamp_get(&lease
->timestamp
);
25 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease
*lease
, clockid_t clock
, uint64_t *ret
) {
26 assert_return(lease
, -EINVAL
);
27 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
28 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
29 assert_return(ret
, -EINVAL
);
31 if (!triple_timestamp_is_set(&lease
->timestamp
))
34 *ret
= triple_timestamp_by_clock(&lease
->timestamp
, clock
);
38 static usec_t
sec2usec(uint32_t sec
) {
39 return sec
== UINT32_MAX
? USEC_INFINITY
: sec
* USEC_PER_SEC
;
42 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease
*lease
) {
43 uint32_t t1
= UINT32_MAX
, t2
= UINT32_MAX
, min_valid_lt
= UINT32_MAX
;
47 assert(lease
->ia_na
|| lease
->ia_pd
);
50 t1
= MIN(t1
, be32toh(lease
->ia_na
->header
.lifetime_t1
));
51 t2
= MIN(t2
, be32toh(lease
->ia_na
->header
.lifetime_t2
));
53 LIST_FOREACH(addresses
, a
, lease
->ia_na
->addresses
)
54 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iaaddr
.lifetime_valid
));
58 t1
= MIN(t1
, be32toh(lease
->ia_pd
->header
.lifetime_t1
));
59 t2
= MIN(t2
, be32toh(lease
->ia_pd
->header
.lifetime_t2
));
61 LIST_FOREACH(addresses
, a
, lease
->ia_pd
->addresses
)
62 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iapdprefix
.lifetime_valid
));
65 if (t2
== 0 || t2
> min_valid_lt
) {
66 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
67 * then adjust lifetime with it. */
68 t1
= min_valid_lt
/ 2;
69 t2
= min_valid_lt
/ 10 * 8;
72 lease
->lifetime_valid
= sec2usec(min_valid_lt
);
73 lease
->lifetime_t1
= sec2usec(t1
);
74 lease
->lifetime_t2
= sec2usec(t2
);
77 int dhcp6_lease_get_lifetime(sd_dhcp6_lease
*lease
, usec_t
*ret_t1
, usec_t
*ret_t2
, usec_t
*ret_valid
) {
80 if (!lease
->ia_na
&& !lease
->ia_pd
)
84 *ret_t1
= lease
->lifetime_t1
;
86 *ret_t2
= lease
->lifetime_t2
;
88 *ret_valid
= lease
->lifetime_valid
;
92 static void dhcp6_lease_set_server_address(sd_dhcp6_lease
*lease
, const struct in6_addr
*server_address
) {
96 lease
->server_address
= *server_address
;
98 lease
->server_address
= (struct in6_addr
) {};
101 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease
*lease
, struct in6_addr
*ret
) {
102 assert_return(lease
, -EINVAL
);
103 assert_return(ret
, -EINVAL
);
105 *ret
= lease
->server_address
;
109 void dhcp6_ia_clear_addresses(DHCP6IA
*ia
) {
114 LIST_FOREACH_SAFE(addresses
, a
, n
, ia
->addresses
)
117 ia
->addresses
= NULL
;
120 DHCP6IA
*dhcp6_ia_free(DHCP6IA
*ia
) {
124 dhcp6_ia_clear_addresses(ia
);
129 int dhcp6_lease_set_clientid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
130 uint8_t *clientid
= NULL
;
133 assert(id
|| len
== 0);
136 clientid
= memdup(id
, len
);
141 free_and_replace(lease
->clientid
, clientid
);
142 lease
->clientid_len
= len
;
147 int dhcp6_lease_get_clientid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
150 if (!lease
->clientid
)
154 *ret_id
= lease
->clientid
;
156 *ret_len
= lease
->clientid_len
;
161 int dhcp6_lease_set_serverid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
162 uint8_t *serverid
= NULL
;
165 assert(id
|| len
== 0);
168 serverid
= memdup(id
, len
);
173 free_and_replace(lease
->serverid
, serverid
);
174 lease
->serverid_len
= len
;
179 int dhcp6_lease_get_serverid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
182 if (!lease
->serverid
)
186 *ret_id
= lease
->serverid
;
188 *ret_len
= lease
->serverid_len
;
192 int dhcp6_lease_set_preference(sd_dhcp6_lease
*lease
, uint8_t preference
) {
195 lease
->preference
= preference
;
199 int dhcp6_lease_get_preference(sd_dhcp6_lease
*lease
, uint8_t *ret
) {
203 *ret
= lease
->preference
;
207 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease
*lease
) {
210 lease
->rapid_commit
= true;
214 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease
*lease
, bool *ret
) {
218 *ret
= lease
->rapid_commit
;
222 int sd_dhcp6_lease_get_address(
223 sd_dhcp6_lease
*lease
,
224 struct in6_addr
*ret_addr
,
225 uint32_t *ret_lifetime_preferred
,
226 uint32_t *ret_lifetime_valid
) {
228 assert_return(lease
, -EINVAL
);
230 if (!lease
->addr_iter
)
234 *ret_addr
= lease
->addr_iter
->iaaddr
.address
;
235 if (ret_lifetime_preferred
)
236 *ret_lifetime_preferred
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_preferred
);
237 if (ret_lifetime_valid
)
238 *ret_lifetime_valid
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_valid
);
240 lease
->addr_iter
= lease
->addr_iter
->addresses_next
;
244 void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease
*lease
) {
246 lease
->addr_iter
= lease
->ia_na
? lease
->ia_na
->addresses
: NULL
;
249 int sd_dhcp6_lease_get_pd(
250 sd_dhcp6_lease
*lease
,
251 struct in6_addr
*ret_prefix
,
252 uint8_t *ret_prefix_len
,
253 uint32_t *ret_lifetime_preferred
,
254 uint32_t *ret_lifetime_valid
) {
256 assert_return(lease
, -EINVAL
);
258 if (!lease
->prefix_iter
)
262 *ret_prefix
= lease
->prefix_iter
->iapdprefix
.address
;
264 *ret_prefix_len
= lease
->prefix_iter
->iapdprefix
.prefixlen
;
265 if (ret_lifetime_preferred
)
266 *ret_lifetime_preferred
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_preferred
);
267 if (ret_lifetime_valid
)
268 *ret_lifetime_valid
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_valid
);
270 lease
->prefix_iter
= lease
->prefix_iter
->addresses_next
;
274 void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease
*lease
) {
276 lease
->prefix_iter
= lease
->ia_pd
? lease
->ia_pd
->addresses
: NULL
;
279 int dhcp6_lease_add_dns(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
281 assert(optval
|| optlen
== 0);
286 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->dns
, &lease
->dns_count
);
289 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
290 assert_return(lease
, -EINVAL
);
298 return lease
->dns_count
;
301 int dhcp6_lease_add_domains(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
302 _cleanup_strv_free_
char **domains
= NULL
;
306 assert(optval
|| optlen
== 0);
311 r
= dhcp6_option_parse_domainname_list(optval
, optlen
, &domains
);
315 return strv_extend_strv(&lease
->domains
, domains
, true);
318 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease
*lease
, char ***ret
) {
319 assert_return(lease
, -EINVAL
);
320 assert_return(ret
, -EINVAL
);
325 *ret
= lease
->domains
;
326 return strv_length(lease
->domains
);
329 int dhcp6_lease_add_ntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
333 assert(optval
|| optlen
== 0);
335 for (size_t offset
= 0; offset
< optlen
;) {
336 const uint8_t *subval
;
340 r
= dhcp6_option_parse(optval
, optlen
, &offset
, &subopt
, &sublen
, &subval
);
345 case DHCP6_NTP_SUBOPTION_SRV_ADDR
:
346 case DHCP6_NTP_SUBOPTION_MC_ADDR
:
350 r
= dhcp6_option_parse_addresses(subval
, sublen
, &lease
->ntp
, &lease
->ntp_count
);
356 case DHCP6_NTP_SUBOPTION_SRV_FQDN
: {
357 _cleanup_free_
char *server
= NULL
;
359 r
= dhcp6_option_parse_domainname(subval
, sublen
, &server
);
363 if (strv_contains(lease
->ntp_fqdn
, server
))
366 r
= strv_consume(&lease
->ntp_fqdn
, TAKE_PTR(server
));
377 int dhcp6_lease_add_sntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
379 assert(optval
|| optlen
== 0);
384 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
385 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->sntp
, &lease
->sntp_count
);
388 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
389 assert_return(lease
, -EINVAL
);
394 return lease
->ntp_count
;
397 if (lease
->sntp
&& !lease
->ntp_fqdn
) {
398 /* Fallback to the deprecated SNTP option. */
401 return lease
->sntp_count
;
407 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease
*lease
, char ***ret
) {
408 assert_return(lease
, -EINVAL
);
410 if (!lease
->ntp_fqdn
)
414 *ret
= lease
->ntp_fqdn
;
415 return strv_length(lease
->ntp_fqdn
);
418 int dhcp6_lease_set_fqdn(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
423 assert(optval
|| optlen
== 0);
431 /* Ignore the flags field, it doesn't carry any useful
432 information for clients. */
433 r
= dhcp6_option_parse_domainname(optval
+ 1, optlen
- 1, &fqdn
);
437 return free_and_replace(lease
->fqdn
, fqdn
);
440 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease
*lease
, const char **ret
) {
441 assert_return(lease
, -EINVAL
);
442 assert_return(ret
, -EINVAL
);
451 static int dhcp6_lease_parse_message(
452 sd_dhcp6_client
*client
,
453 sd_dhcp6_lease
*lease
,
454 const DHCP6Message
*message
,
457 usec_t irt
= IRT_DEFAULT
;
463 assert(len
>= sizeof(DHCP6Message
));
465 len
-= sizeof(DHCP6Message
);
466 for (size_t offset
= 0; offset
< len
;) {
469 const uint8_t *optval
;
471 r
= dhcp6_option_parse(message
->options
, len
, &offset
, &optcode
, &optlen
, &optval
);
476 case SD_DHCP6_OPTION_CLIENTID
:
477 if (dhcp6_lease_get_clientid(lease
, NULL
, NULL
) >= 0)
478 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple client IDs",
479 dhcp6_message_type_to_string(message
->type
));
481 r
= dhcp6_lease_set_clientid(lease
, optval
, optlen
);
487 case SD_DHCP6_OPTION_SERVERID
:
488 if (dhcp6_lease_get_serverid(lease
, NULL
, NULL
) >= 0)
489 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple server IDs",
490 dhcp6_message_type_to_string(message
->type
));
492 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
498 case SD_DHCP6_OPTION_PREFERENCE
:
502 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
508 case SD_DHCP6_OPTION_STATUS_CODE
: {
509 _cleanup_free_
char *msg
= NULL
;
511 r
= dhcp6_option_parse_status(optval
, optlen
, &msg
);
516 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
517 "Received %s message with non-zero status: %s%s%s",
518 dhcp6_message_type_to_string(message
->type
),
519 strempty(msg
), isempty(msg
) ? "" : ": ",
520 dhcp6_message_status_to_string(r
));
523 case SD_DHCP6_OPTION_IA_NA
: {
524 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
526 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
527 log_dhcp6_client(client
, "Ignoring IA NA option in information requesting mode.");
531 r
= dhcp6_option_parse_ia(client
, client
->ia_na
.header
.id
, optcode
, optlen
, optval
, &ia
);
538 log_dhcp6_client(client
, "Received duplicate matching IA_NA option, ignoring.");
542 dhcp6_ia_free(lease
->ia_na
);
543 lease
->ia_na
= TAKE_PTR(ia
);
546 case SD_DHCP6_OPTION_IA_PD
: {
547 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
549 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
550 log_dhcp6_client(client
, "Ignoring IA PD option in information requesting mode.");
554 r
= dhcp6_option_parse_ia(client
, client
->ia_pd
.header
.id
, optcode
, optlen
, optval
, &ia
);
561 log_dhcp6_client(client
, "Received duplicate matching IA_PD option, ignoring.");
565 dhcp6_ia_free(lease
->ia_pd
);
566 lease
->ia_pd
= TAKE_PTR(ia
);
569 case SD_DHCP6_OPTION_RAPID_COMMIT
:
570 r
= dhcp6_lease_set_rapid_commit(lease
);
576 case SD_DHCP6_OPTION_DNS_SERVERS
:
577 r
= dhcp6_lease_add_dns(lease
, optval
, optlen
);
579 log_dhcp6_client_errno(client
, r
, "Failed to parse DNS server option, ignoring: %m");
583 case SD_DHCP6_OPTION_DOMAIN_LIST
:
584 r
= dhcp6_lease_add_domains(lease
, optval
, optlen
);
586 log_dhcp6_client_errno(client
, r
, "Failed to parse domain list option, ignoring: %m");
590 case SD_DHCP6_OPTION_NTP_SERVER
:
591 r
= dhcp6_lease_add_ntp(lease
, optval
, optlen
);
593 log_dhcp6_client_errno(client
, r
, "Failed to parse NTP server option, ignoring: %m");
597 case SD_DHCP6_OPTION_SNTP_SERVERS
:
598 r
= dhcp6_lease_add_sntp(lease
, optval
, optlen
);
600 log_dhcp6_client_errno(client
, r
, "Failed to parse SNTP server option, ignoring: %m");
604 case SD_DHCP6_OPTION_CLIENT_FQDN
:
605 r
= dhcp6_lease_set_fqdn(lease
, optval
, optlen
);
607 log_dhcp6_client_errno(client
, r
, "Failed to parse FQDN option, ignoring: %m");
611 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
615 irt
= unaligned_read_be32((be32_t
*) optval
) * USEC_PER_SEC
;
622 if (dhcp6_lease_get_clientid(lease
, &clientid
, &clientid_len
) < 0)
623 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
624 "%s message does not contain client ID. Ignoring.",
625 dhcp6_message_type_to_string(message
->type
));
627 if (memcmp_nn(clientid
, clientid_len
, &client
->duid
, client
->duid_len
) != 0)
628 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
629 "The client ID in %s message does not match. Ignoring.",
630 dhcp6_message_type_to_string(message
->type
));
632 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
633 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
634 log_dhcp6_client(client
, "New information request will be refused in %s.",
635 FORMAT_TIMESPAN(client
->information_refresh_time_usec
, USEC_PER_SEC
));
638 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
640 return log_dhcp6_client_errno(client
, r
, "%s has no server id",
641 dhcp6_message_type_to_string(message
->type
));
643 if (!lease
->ia_na
&& !lease
->ia_pd
)
644 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
645 "No IA_PD prefix or IA_NA address received. Ignoring.");
647 dhcp6_lease_set_lifetime(lease
);
653 static sd_dhcp6_lease
*dhcp6_lease_free(sd_dhcp6_lease
*lease
) {
657 free(lease
->clientid
);
658 free(lease
->serverid
);
659 dhcp6_ia_free(lease
->ia_na
);
660 dhcp6_ia_free(lease
->ia_pd
);
663 strv_free(lease
->domains
);
665 strv_free(lease
->ntp_fqdn
);
671 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease
, sd_dhcp6_lease
, dhcp6_lease_free
);
673 int dhcp6_lease_new(sd_dhcp6_lease
**ret
) {
674 sd_dhcp6_lease
*lease
;
678 lease
= new(sd_dhcp6_lease
, 1);
682 *lease
= (sd_dhcp6_lease
) {
690 int dhcp6_lease_new_from_message(
691 sd_dhcp6_client
*client
,
692 const DHCP6Message
*message
,
694 const triple_timestamp
*timestamp
,
695 const struct in6_addr
*server_address
,
696 sd_dhcp6_lease
**ret
) {
698 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
703 assert(len
>= sizeof(DHCP6Message
));
706 r
= dhcp6_lease_new(&lease
);
710 dhcp6_lease_set_timestamp(lease
, timestamp
);
711 dhcp6_lease_set_server_address(lease
, server_address
);
713 r
= dhcp6_lease_parse_message(client
, lease
, message
, len
);
717 *ret
= TAKE_PTR(lease
);