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
);
473 case SD_DHCP6_OPTION_CLIENTID
:
474 if (dhcp6_lease_get_clientid(lease
, NULL
, NULL
) >= 0)
475 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple client IDs",
476 dhcp6_message_type_to_string(message
->type
));
478 r
= dhcp6_lease_set_clientid(lease
, optval
, optlen
);
484 case SD_DHCP6_OPTION_SERVERID
:
485 if (dhcp6_lease_get_serverid(lease
, NULL
, NULL
) >= 0)
486 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple server IDs",
487 dhcp6_message_type_to_string(message
->type
));
489 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
495 case SD_DHCP6_OPTION_PREFERENCE
:
499 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
505 case SD_DHCP6_OPTION_STATUS_CODE
: {
506 _cleanup_free_
char *msg
= NULL
;
508 r
= dhcp6_option_parse_status(optval
, optlen
, &msg
);
513 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
514 "Received %s message with non-zero status: %s%s%s",
515 dhcp6_message_type_to_string(message
->type
),
516 strempty(msg
), isempty(msg
) ? "" : ": ",
517 dhcp6_message_status_to_string(r
));
520 case SD_DHCP6_OPTION_IA_NA
: {
521 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
523 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
524 log_dhcp6_client(client
, "Ignoring IA NA option in information requesting mode.");
528 r
= dhcp6_option_parse_ia(client
, client
->ia_na
.header
.id
, optcode
, optlen
, optval
, &ia
);
535 log_dhcp6_client(client
, "Received duplicate matching IA_NA option, ignoring.");
539 dhcp6_ia_free(lease
->ia_na
);
540 lease
->ia_na
= TAKE_PTR(ia
);
543 case SD_DHCP6_OPTION_IA_PD
: {
544 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
546 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
547 log_dhcp6_client(client
, "Ignoring IA PD option in information requesting mode.");
551 r
= dhcp6_option_parse_ia(client
, client
->ia_pd
.header
.id
, optcode
, optlen
, optval
, &ia
);
558 log_dhcp6_client(client
, "Received duplicate matching IA_PD option, ignoring.");
562 dhcp6_ia_free(lease
->ia_pd
);
563 lease
->ia_pd
= TAKE_PTR(ia
);
566 case SD_DHCP6_OPTION_RAPID_COMMIT
:
567 r
= dhcp6_lease_set_rapid_commit(lease
);
573 case SD_DHCP6_OPTION_DNS_SERVER
:
574 r
= dhcp6_lease_add_dns(lease
, optval
, optlen
);
576 log_dhcp6_client_errno(client
, r
, "Failed to parse DNS server option, ignoring: %m");
580 case SD_DHCP6_OPTION_DOMAIN
:
581 r
= dhcp6_lease_add_domains(lease
, optval
, optlen
);
583 log_dhcp6_client_errno(client
, r
, "Failed to parse domain list option, ignoring: %m");
587 case SD_DHCP6_OPTION_NTP_SERVER
:
588 r
= dhcp6_lease_add_ntp(lease
, optval
, optlen
);
590 log_dhcp6_client_errno(client
, r
, "Failed to parse NTP server option, ignoring: %m");
594 case SD_DHCP6_OPTION_SNTP_SERVER
:
595 r
= dhcp6_lease_add_sntp(lease
, optval
, optlen
);
597 log_dhcp6_client_errno(client
, r
, "Failed to parse SNTP server option, ignoring: %m");
601 case SD_DHCP6_OPTION_CLIENT_FQDN
:
602 r
= dhcp6_lease_set_fqdn(lease
, optval
, optlen
);
604 log_dhcp6_client_errno(client
, r
, "Failed to parse FQDN option, ignoring: %m");
608 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
612 irt
= unaligned_read_be32((be32_t
*) optval
) * USEC_PER_SEC
;
619 if (dhcp6_lease_get_clientid(lease
, &clientid
, &clientid_len
) < 0)
620 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
621 "%s message does not contain client ID. Ignoring.",
622 dhcp6_message_type_to_string(message
->type
));
624 if (memcmp_nn(clientid
, clientid_len
, &client
->duid
, client
->duid_len
) != 0)
625 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
626 "The client ID in %s message does not match. Ignoring.",
627 dhcp6_message_type_to_string(message
->type
));
629 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
630 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
631 log_dhcp6_client(client
, "New information request will be refused in %s.",
632 FORMAT_TIMESPAN(client
->information_refresh_time_usec
, USEC_PER_SEC
));
635 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
637 return log_dhcp6_client_errno(client
, r
, "%s has no server id",
638 dhcp6_message_type_to_string(message
->type
));
640 if (!lease
->ia_na
&& !lease
->ia_pd
)
641 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
642 "No IA_PD prefix or IA_NA address received. Ignoring.");
644 dhcp6_lease_set_lifetime(lease
);
650 static sd_dhcp6_lease
*dhcp6_lease_free(sd_dhcp6_lease
*lease
) {
654 free(lease
->clientid
);
655 free(lease
->serverid
);
656 dhcp6_ia_free(lease
->ia_na
);
657 dhcp6_ia_free(lease
->ia_pd
);
660 strv_free(lease
->domains
);
662 strv_free(lease
->ntp_fqdn
);
668 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease
, sd_dhcp6_lease
, dhcp6_lease_free
);
670 int dhcp6_lease_new(sd_dhcp6_lease
**ret
) {
671 sd_dhcp6_lease
*lease
;
675 lease
= new(sd_dhcp6_lease
, 1);
679 *lease
= (sd_dhcp6_lease
) {
687 int dhcp6_lease_new_from_message(
688 sd_dhcp6_client
*client
,
689 const DHCP6Message
*message
,
691 const triple_timestamp
*timestamp
,
692 const struct in6_addr
*server_address
,
693 sd_dhcp6_lease
**ret
) {
695 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
700 assert(len
>= sizeof(DHCP6Message
));
703 r
= dhcp6_lease_new(&lease
);
707 dhcp6_lease_set_timestamp(lease
, timestamp
);
708 dhcp6_lease_set_server_address(lease
, server_address
);
710 r
= dhcp6_lease_parse_message(client
, lease
, message
, len
);
714 *ret
= TAKE_PTR(lease
);