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
;
46 assert(lease
->ia_na
|| lease
->ia_pd
);
49 t1
= MIN(t1
, be32toh(lease
->ia_na
->header
.lifetime_t1
));
50 t2
= MIN(t2
, be32toh(lease
->ia_na
->header
.lifetime_t2
));
52 LIST_FOREACH(addresses
, a
, lease
->ia_na
->addresses
)
53 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iaaddr
.lifetime_valid
));
57 t1
= MIN(t1
, be32toh(lease
->ia_pd
->header
.lifetime_t1
));
58 t2
= MIN(t2
, be32toh(lease
->ia_pd
->header
.lifetime_t2
));
60 LIST_FOREACH(addresses
, a
, lease
->ia_pd
->addresses
)
61 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iapdprefix
.lifetime_valid
));
64 if (t2
== 0 || t2
> min_valid_lt
) {
65 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
66 * then adjust lifetime with it. */
67 t1
= min_valid_lt
/ 2;
68 t2
= min_valid_lt
/ 10 * 8;
71 lease
->lifetime_valid
= sec2usec(min_valid_lt
);
72 lease
->lifetime_t1
= sec2usec(t1
);
73 lease
->lifetime_t2
= sec2usec(t2
);
76 int dhcp6_lease_get_lifetime(sd_dhcp6_lease
*lease
, usec_t
*ret_t1
, usec_t
*ret_t2
, usec_t
*ret_valid
) {
79 if (!lease
->ia_na
&& !lease
->ia_pd
)
83 *ret_t1
= lease
->lifetime_t1
;
85 *ret_t2
= lease
->lifetime_t2
;
87 *ret_valid
= lease
->lifetime_valid
;
91 static void dhcp6_lease_set_server_address(sd_dhcp6_lease
*lease
, const struct in6_addr
*server_address
) {
95 lease
->server_address
= *server_address
;
97 lease
->server_address
= (struct in6_addr
) {};
100 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease
*lease
, struct in6_addr
*ret
) {
101 assert_return(lease
, -EINVAL
);
102 assert_return(ret
, -EINVAL
);
104 *ret
= lease
->server_address
;
108 void dhcp6_ia_clear_addresses(DHCP6IA
*ia
) {
111 LIST_FOREACH(addresses
, a
, ia
->addresses
)
114 ia
->addresses
= NULL
;
117 DHCP6IA
*dhcp6_ia_free(DHCP6IA
*ia
) {
121 dhcp6_ia_clear_addresses(ia
);
126 int dhcp6_lease_set_clientid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
127 uint8_t *clientid
= NULL
;
130 assert(id
|| len
== 0);
133 clientid
= memdup(id
, len
);
138 free_and_replace(lease
->clientid
, clientid
);
139 lease
->clientid_len
= len
;
144 int dhcp6_lease_get_clientid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
147 if (!lease
->clientid
)
151 *ret_id
= lease
->clientid
;
153 *ret_len
= lease
->clientid_len
;
158 int dhcp6_lease_set_serverid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
159 uint8_t *serverid
= NULL
;
162 assert(id
|| len
== 0);
165 serverid
= memdup(id
, len
);
170 free_and_replace(lease
->serverid
, serverid
);
171 lease
->serverid_len
= len
;
176 int dhcp6_lease_get_serverid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
179 if (!lease
->serverid
)
183 *ret_id
= lease
->serverid
;
185 *ret_len
= lease
->serverid_len
;
189 int dhcp6_lease_set_preference(sd_dhcp6_lease
*lease
, uint8_t preference
) {
192 lease
->preference
= preference
;
196 int dhcp6_lease_get_preference(sd_dhcp6_lease
*lease
, uint8_t *ret
) {
200 *ret
= lease
->preference
;
204 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease
*lease
) {
207 lease
->rapid_commit
= true;
211 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease
*lease
, bool *ret
) {
215 *ret
= lease
->rapid_commit
;
219 int sd_dhcp6_lease_get_address(
220 sd_dhcp6_lease
*lease
,
221 struct in6_addr
*ret_addr
,
222 uint32_t *ret_lifetime_preferred
,
223 uint32_t *ret_lifetime_valid
) {
225 assert_return(lease
, -EINVAL
);
227 if (!lease
->addr_iter
)
231 *ret_addr
= lease
->addr_iter
->iaaddr
.address
;
232 if (ret_lifetime_preferred
)
233 *ret_lifetime_preferred
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_preferred
);
234 if (ret_lifetime_valid
)
235 *ret_lifetime_valid
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_valid
);
237 lease
->addr_iter
= lease
->addr_iter
->addresses_next
;
241 void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease
*lease
) {
243 lease
->addr_iter
= lease
->ia_na
? lease
->ia_na
->addresses
: NULL
;
246 int sd_dhcp6_lease_get_pd(
247 sd_dhcp6_lease
*lease
,
248 struct in6_addr
*ret_prefix
,
249 uint8_t *ret_prefix_len
,
250 uint32_t *ret_lifetime_preferred
,
251 uint32_t *ret_lifetime_valid
) {
253 assert_return(lease
, -EINVAL
);
255 if (!lease
->prefix_iter
)
259 *ret_prefix
= lease
->prefix_iter
->iapdprefix
.address
;
261 *ret_prefix_len
= lease
->prefix_iter
->iapdprefix
.prefixlen
;
262 if (ret_lifetime_preferred
)
263 *ret_lifetime_preferred
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_preferred
);
264 if (ret_lifetime_valid
)
265 *ret_lifetime_valid
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_valid
);
267 lease
->prefix_iter
= lease
->prefix_iter
->addresses_next
;
271 void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease
*lease
) {
273 lease
->prefix_iter
= lease
->ia_pd
? lease
->ia_pd
->addresses
: NULL
;
276 int dhcp6_lease_add_dns(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
278 assert(optval
|| optlen
== 0);
283 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->dns
, &lease
->dns_count
);
286 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
287 assert_return(lease
, -EINVAL
);
295 return lease
->dns_count
;
298 int dhcp6_lease_add_domains(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
299 _cleanup_strv_free_
char **domains
= NULL
;
303 assert(optval
|| optlen
== 0);
308 r
= dhcp6_option_parse_domainname_list(optval
, optlen
, &domains
);
312 return strv_extend_strv(&lease
->domains
, domains
, true);
315 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease
*lease
, char ***ret
) {
316 assert_return(lease
, -EINVAL
);
317 assert_return(ret
, -EINVAL
);
322 *ret
= lease
->domains
;
323 return strv_length(lease
->domains
);
326 int dhcp6_lease_add_ntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
330 assert(optval
|| optlen
== 0);
332 for (size_t offset
= 0; offset
< optlen
;) {
333 const uint8_t *subval
;
337 r
= dhcp6_option_parse(optval
, optlen
, &offset
, &subopt
, &sublen
, &subval
);
342 case DHCP6_NTP_SUBOPTION_SRV_ADDR
:
343 case DHCP6_NTP_SUBOPTION_MC_ADDR
:
347 r
= dhcp6_option_parse_addresses(subval
, sublen
, &lease
->ntp
, &lease
->ntp_count
);
353 case DHCP6_NTP_SUBOPTION_SRV_FQDN
: {
354 _cleanup_free_
char *server
= NULL
;
356 r
= dhcp6_option_parse_domainname(subval
, sublen
, &server
);
360 if (strv_contains(lease
->ntp_fqdn
, server
))
363 r
= strv_consume(&lease
->ntp_fqdn
, TAKE_PTR(server
));
374 int dhcp6_lease_add_sntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
376 assert(optval
|| optlen
== 0);
381 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
382 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->sntp
, &lease
->sntp_count
);
385 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
386 assert_return(lease
, -EINVAL
);
391 return lease
->ntp_count
;
394 if (lease
->sntp
&& !lease
->ntp_fqdn
) {
395 /* Fallback to the deprecated SNTP option. */
398 return lease
->sntp_count
;
404 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease
*lease
, char ***ret
) {
405 assert_return(lease
, -EINVAL
);
407 if (!lease
->ntp_fqdn
)
411 *ret
= lease
->ntp_fqdn
;
412 return strv_length(lease
->ntp_fqdn
);
415 int dhcp6_lease_set_fqdn(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
420 assert(optval
|| optlen
== 0);
428 /* Ignore the flags field, it doesn't carry any useful
429 information for clients. */
430 r
= dhcp6_option_parse_domainname(optval
+ 1, optlen
- 1, &fqdn
);
434 return free_and_replace(lease
->fqdn
, fqdn
);
437 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease
*lease
, const char **ret
) {
438 assert_return(lease
, -EINVAL
);
439 assert_return(ret
, -EINVAL
);
448 static int dhcp6_lease_parse_message(
449 sd_dhcp6_client
*client
,
450 sd_dhcp6_lease
*lease
,
451 const DHCP6Message
*message
,
454 usec_t irt
= IRT_DEFAULT
;
460 assert(len
>= sizeof(DHCP6Message
));
462 len
-= sizeof(DHCP6Message
);
463 for (size_t offset
= 0; offset
< len
;) {
466 const uint8_t *optval
;
468 r
= dhcp6_option_parse(message
->options
, len
, &offset
, &optcode
, &optlen
, &optval
);
470 return log_dhcp6_client_errno(client
, r
,
471 "Failed to parse option header at offset %zu of total length %zu: %m",
475 case SD_DHCP6_OPTION_CLIENTID
:
476 if (dhcp6_lease_get_clientid(lease
, NULL
, NULL
) >= 0)
477 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple client IDs",
478 dhcp6_message_type_to_string(message
->type
));
480 r
= dhcp6_lease_set_clientid(lease
, optval
, optlen
);
482 return log_dhcp6_client_errno(client
, r
, "Failed to set client ID: %m");
486 case SD_DHCP6_OPTION_SERVERID
:
487 if (dhcp6_lease_get_serverid(lease
, NULL
, NULL
) >= 0)
488 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple server IDs",
489 dhcp6_message_type_to_string(message
->type
));
491 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
493 return log_dhcp6_client_errno(client
, r
, "Failed to set server ID: %m");
497 case SD_DHCP6_OPTION_PREFERENCE
:
499 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "Received invalid length for preference.");
501 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
503 return log_dhcp6_client_errno(client
, r
, "Failed to set preference: %m");
507 case SD_DHCP6_OPTION_STATUS_CODE
: {
508 _cleanup_free_
char *msg
= NULL
;
510 r
= dhcp6_option_parse_status(optval
, optlen
, &msg
);
512 return log_dhcp6_client_errno(client
, r
, "Failed to parse status code: %m");
515 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
516 "Received %s message with non-zero status: %s%s%s",
517 dhcp6_message_type_to_string(message
->type
),
518 strempty(msg
), isempty(msg
) ? "" : ": ",
519 dhcp6_message_status_to_string(r
));
522 case SD_DHCP6_OPTION_IA_NA
: {
523 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
525 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
526 log_dhcp6_client(client
, "Ignoring IA NA option in information requesting mode.");
530 r
= dhcp6_option_parse_ia(client
, client
->ia_na
.header
.id
, optcode
, optlen
, optval
, &ia
);
532 return log_oom_debug();
534 log_dhcp6_client_errno(client
, r
, "Failed to parse IA_NA option, ignoring: %m");
539 log_dhcp6_client(client
, "Received duplicate matching IA_NA option, ignoring.");
543 dhcp6_ia_free(lease
->ia_na
);
544 lease
->ia_na
= TAKE_PTR(ia
);
547 case SD_DHCP6_OPTION_IA_PD
: {
548 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
550 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
551 log_dhcp6_client(client
, "Ignoring IA PD option in information requesting mode.");
555 r
= dhcp6_option_parse_ia(client
, client
->ia_pd
.header
.id
, optcode
, optlen
, optval
, &ia
);
557 return log_oom_debug();
559 log_dhcp6_client_errno(client
, r
, "Failed to parse IA_PD option, ignoring: %m");
564 log_dhcp6_client(client
, "Received duplicate matching IA_PD option, ignoring.");
568 dhcp6_ia_free(lease
->ia_pd
);
569 lease
->ia_pd
= TAKE_PTR(ia
);
572 case SD_DHCP6_OPTION_RAPID_COMMIT
:
574 log_dhcp6_client(client
, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen
);
576 r
= dhcp6_lease_set_rapid_commit(lease
);
578 return log_dhcp6_client_errno(client
, r
, "Failed to set rapid commit flag: %m");
582 case SD_DHCP6_OPTION_DNS_SERVER
:
583 r
= dhcp6_lease_add_dns(lease
, optval
, optlen
);
585 log_dhcp6_client_errno(client
, r
, "Failed to parse DNS server option, ignoring: %m");
589 case SD_DHCP6_OPTION_DOMAIN
:
590 r
= dhcp6_lease_add_domains(lease
, optval
, optlen
);
592 log_dhcp6_client_errno(client
, r
, "Failed to parse domain list option, ignoring: %m");
596 case SD_DHCP6_OPTION_NTP_SERVER
:
597 r
= dhcp6_lease_add_ntp(lease
, optval
, optlen
);
599 log_dhcp6_client_errno(client
, r
, "Failed to parse NTP server option, ignoring: %m");
603 case SD_DHCP6_OPTION_SNTP_SERVER
:
604 r
= dhcp6_lease_add_sntp(lease
, optval
, optlen
);
606 log_dhcp6_client_errno(client
, r
, "Failed to parse SNTP server option, ignoring: %m");
610 case SD_DHCP6_OPTION_CLIENT_FQDN
:
611 r
= dhcp6_lease_set_fqdn(lease
, optval
, optlen
);
613 log_dhcp6_client_errno(client
, r
, "Failed to parse FQDN option, ignoring: %m");
617 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
619 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
620 "Received information refresh time option with an invalid length (%zu).", optlen
);
622 irt
= unaligned_read_be32((be32_t
*) optval
) * USEC_PER_SEC
;
629 if (dhcp6_lease_get_clientid(lease
, &clientid
, &clientid_len
) < 0)
630 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
631 "%s message does not contain client ID. Ignoring.",
632 dhcp6_message_type_to_string(message
->type
));
634 if (memcmp_nn(clientid
, clientid_len
, &client
->duid
, client
->duid_len
) != 0)
635 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
636 "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 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
641 log_dhcp6_client(client
, "New information request will be refused in %s.",
642 FORMAT_TIMESPAN(client
->information_refresh_time_usec
, USEC_PER_SEC
));
645 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
647 return log_dhcp6_client_errno(client
, r
, "%s has no server id",
648 dhcp6_message_type_to_string(message
->type
));
650 if (!lease
->ia_na
&& !lease
->ia_pd
)
651 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
652 "No IA_PD prefix or IA_NA address received. Ignoring.");
654 dhcp6_lease_set_lifetime(lease
);
660 static sd_dhcp6_lease
*dhcp6_lease_free(sd_dhcp6_lease
*lease
) {
664 free(lease
->clientid
);
665 free(lease
->serverid
);
666 dhcp6_ia_free(lease
->ia_na
);
667 dhcp6_ia_free(lease
->ia_pd
);
670 strv_free(lease
->domains
);
672 strv_free(lease
->ntp_fqdn
);
678 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease
, sd_dhcp6_lease
, dhcp6_lease_free
);
680 int dhcp6_lease_new(sd_dhcp6_lease
**ret
) {
681 sd_dhcp6_lease
*lease
;
685 lease
= new(sd_dhcp6_lease
, 1);
689 *lease
= (sd_dhcp6_lease
) {
697 int dhcp6_lease_new_from_message(
698 sd_dhcp6_client
*client
,
699 const DHCP6Message
*message
,
701 const triple_timestamp
*timestamp
,
702 const struct in6_addr
*server_address
,
703 sd_dhcp6_lease
**ret
) {
705 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
710 assert(len
>= sizeof(DHCP6Message
));
713 r
= dhcp6_lease_new(&lease
);
717 dhcp6_lease_set_timestamp(lease
, timestamp
);
718 dhcp6_lease_set_server_address(lease
, server_address
);
720 r
= dhcp6_lease_parse_message(client
, lease
, message
, len
);
724 *ret
= TAKE_PTR(lease
);