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 usec_t
sec2usec(uint32_t sec
) {
40 return sec
== UINT32_MAX
? USEC_INFINITY
: sec
* USEC_PER_SEC
;
43 static void dhcp6_lease_set_lifetime(sd_dhcp6_lease
*lease
) {
44 uint32_t t1
= UINT32_MAX
, t2
= UINT32_MAX
, min_valid_lt
= UINT32_MAX
;
48 assert(lease
->ia_na
|| lease
->ia_pd
);
51 t1
= MIN(t1
, be32toh(lease
->ia_na
->header
.lifetime_t1
));
52 t2
= MIN(t2
, be32toh(lease
->ia_na
->header
.lifetime_t2
));
54 LIST_FOREACH(addresses
, a
, lease
->ia_na
->addresses
)
55 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iaaddr
.lifetime_valid
));
59 t1
= MIN(t1
, be32toh(lease
->ia_pd
->header
.lifetime_t1
));
60 t2
= MIN(t2
, be32toh(lease
->ia_pd
->header
.lifetime_t2
));
62 LIST_FOREACH(addresses
, a
, lease
->ia_pd
->addresses
)
63 min_valid_lt
= MIN(min_valid_lt
, be32toh(a
->iapdprefix
.lifetime_valid
));
66 if (t2
== 0 || t2
> min_valid_lt
) {
67 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
68 * then adjust lifetime with it. */
69 t1
= min_valid_lt
/ 2;
70 t2
= min_valid_lt
/ 10 * 8;
73 lease
->lifetime_valid
= sec2usec(min_valid_lt
);
74 lease
->lifetime_t1
= sec2usec(t1
);
75 lease
->lifetime_t2
= sec2usec(t2
);
78 int dhcp6_lease_get_lifetime(sd_dhcp6_lease
*lease
, usec_t
*ret_t1
, usec_t
*ret_t2
, usec_t
*ret_valid
) {
81 if (!lease
->ia_na
&& !lease
->ia_pd
)
85 *ret_t1
= lease
->lifetime_t1
;
87 *ret_t2
= lease
->lifetime_t2
;
89 *ret_valid
= lease
->lifetime_valid
;
93 static void dhcp6_lease_set_server_address(sd_dhcp6_lease
*lease
, const struct in6_addr
*server_address
) {
97 lease
->server_address
= *server_address
;
99 lease
->server_address
= (struct in6_addr
) {};
102 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease
*lease
, struct in6_addr
*ret
) {
103 assert_return(lease
, -EINVAL
);
104 assert_return(ret
, -EINVAL
);
106 *ret
= lease
->server_address
;
110 void dhcp6_ia_clear_addresses(DHCP6IA
*ia
) {
115 LIST_FOREACH_SAFE(addresses
, a
, n
, ia
->addresses
)
118 ia
->addresses
= NULL
;
121 DHCP6IA
*dhcp6_ia_free(DHCP6IA
*ia
) {
125 dhcp6_ia_clear_addresses(ia
);
130 int dhcp6_lease_set_clientid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
131 uint8_t *clientid
= NULL
;
134 assert(id
|| len
== 0);
137 clientid
= memdup(id
, len
);
142 free_and_replace(lease
->clientid
, clientid
);
143 lease
->clientid_len
= len
;
148 int dhcp6_lease_get_clientid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
151 if (!lease
->clientid
)
155 *ret_id
= lease
->clientid
;
157 *ret_len
= lease
->clientid_len
;
162 int dhcp6_lease_set_serverid(sd_dhcp6_lease
*lease
, const uint8_t *id
, size_t len
) {
163 uint8_t *serverid
= NULL
;
166 assert(id
|| len
== 0);
169 serverid
= memdup(id
, len
);
174 free_and_replace(lease
->serverid
, serverid
);
175 lease
->serverid_len
= len
;
180 int dhcp6_lease_get_serverid(sd_dhcp6_lease
*lease
, uint8_t **ret_id
, size_t *ret_len
) {
183 if (!lease
->serverid
)
187 *ret_id
= lease
->serverid
;
189 *ret_len
= lease
->serverid_len
;
193 int dhcp6_lease_set_preference(sd_dhcp6_lease
*lease
, uint8_t preference
) {
196 lease
->preference
= preference
;
200 int dhcp6_lease_get_preference(sd_dhcp6_lease
*lease
, uint8_t *ret
) {
204 *ret
= lease
->preference
;
208 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease
*lease
) {
211 lease
->rapid_commit
= true;
215 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease
*lease
, bool *ret
) {
219 *ret
= lease
->rapid_commit
;
223 int sd_dhcp6_lease_get_address(
224 sd_dhcp6_lease
*lease
,
225 struct in6_addr
*ret_addr
,
226 uint32_t *ret_lifetime_preferred
,
227 uint32_t *ret_lifetime_valid
) {
229 assert_return(lease
, -EINVAL
);
231 if (!lease
->addr_iter
)
235 *ret_addr
= lease
->addr_iter
->iaaddr
.address
;
236 if (ret_lifetime_preferred
)
237 *ret_lifetime_preferred
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_preferred
);
238 if (ret_lifetime_valid
)
239 *ret_lifetime_valid
= be32toh(lease
->addr_iter
->iaaddr
.lifetime_valid
);
241 lease
->addr_iter
= lease
->addr_iter
->addresses_next
;
245 void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease
*lease
) {
247 lease
->addr_iter
= lease
->ia_na
? lease
->ia_na
->addresses
: NULL
;
250 int sd_dhcp6_lease_get_pd(
251 sd_dhcp6_lease
*lease
,
252 struct in6_addr
*ret_prefix
,
253 uint8_t *ret_prefix_len
,
254 uint32_t *ret_lifetime_preferred
,
255 uint32_t *ret_lifetime_valid
) {
257 assert_return(lease
, -EINVAL
);
259 if (!lease
->prefix_iter
)
263 *ret_prefix
= lease
->prefix_iter
->iapdprefix
.address
;
265 *ret_prefix_len
= lease
->prefix_iter
->iapdprefix
.prefixlen
;
266 if (ret_lifetime_preferred
)
267 *ret_lifetime_preferred
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_preferred
);
268 if (ret_lifetime_valid
)
269 *ret_lifetime_valid
= be32toh(lease
->prefix_iter
->iapdprefix
.lifetime_valid
);
271 lease
->prefix_iter
= lease
->prefix_iter
->addresses_next
;
275 void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease
*lease
) {
277 lease
->prefix_iter
= lease
->ia_pd
? lease
->ia_pd
->addresses
: NULL
;
280 int dhcp6_lease_add_dns(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
282 assert(optval
|| optlen
== 0);
287 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->dns
, &lease
->dns_count
);
290 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
291 assert_return(lease
, -EINVAL
);
299 return lease
->dns_count
;
302 int dhcp6_lease_add_domains(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
303 _cleanup_strv_free_
char **domains
= NULL
;
307 assert(optval
|| optlen
== 0);
312 r
= dhcp6_option_parse_domainname_list(optval
, optlen
, &domains
);
316 return strv_extend_strv(&lease
->domains
, domains
, true);
319 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease
*lease
, char ***ret
) {
320 assert_return(lease
, -EINVAL
);
321 assert_return(ret
, -EINVAL
);
326 *ret
= lease
->domains
;
327 return strv_length(lease
->domains
);
330 int dhcp6_lease_add_ntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
334 assert(optval
|| optlen
== 0);
336 for (size_t offset
= 0; offset
< optlen
;) {
337 const uint8_t *subval
;
341 r
= dhcp6_option_parse(optval
, optlen
, &offset
, &subopt
, &sublen
, &subval
);
346 case DHCP6_NTP_SUBOPTION_SRV_ADDR
:
347 case DHCP6_NTP_SUBOPTION_MC_ADDR
:
351 r
= dhcp6_option_parse_addresses(subval
, sublen
, &lease
->ntp
, &lease
->ntp_count
);
357 case DHCP6_NTP_SUBOPTION_SRV_FQDN
: {
358 _cleanup_free_
char *server
= NULL
;
360 r
= dhcp6_option_parse_domainname(subval
, sublen
, &server
);
364 if (strv_contains(lease
->ntp_fqdn
, server
))
367 r
= strv_consume(&lease
->ntp_fqdn
, TAKE_PTR(server
));
378 int dhcp6_lease_add_sntp(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
380 assert(optval
|| optlen
== 0);
385 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
386 return dhcp6_option_parse_addresses(optval
, optlen
, &lease
->sntp
, &lease
->sntp_count
);
389 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease
*lease
, const struct in6_addr
**ret
) {
390 assert_return(lease
, -EINVAL
);
395 return lease
->ntp_count
;
398 if (lease
->sntp
&& !lease
->ntp_fqdn
) {
399 /* Fallback to the deprecated SNTP option. */
402 return lease
->sntp_count
;
408 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease
*lease
, char ***ret
) {
409 assert_return(lease
, -EINVAL
);
411 if (!lease
->ntp_fqdn
)
415 *ret
= lease
->ntp_fqdn
;
416 return strv_length(lease
->ntp_fqdn
);
419 int dhcp6_lease_set_fqdn(sd_dhcp6_lease
*lease
, const uint8_t *optval
, size_t optlen
) {
424 assert(optval
|| optlen
== 0);
432 /* Ignore the flags field, it doesn't carry any useful
433 information for clients. */
434 r
= dhcp6_option_parse_domainname(optval
+ 1, optlen
- 1, &fqdn
);
438 return free_and_replace(lease
->fqdn
, fqdn
);
441 int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease
*lease
, const char **ret
) {
442 assert_return(lease
, -EINVAL
);
443 assert_return(ret
, -EINVAL
);
452 static int dhcp6_lease_parse_message(
453 sd_dhcp6_client
*client
,
454 sd_dhcp6_lease
*lease
,
455 const DHCP6Message
*message
,
458 usec_t irt
= IRT_DEFAULT
;
464 assert(len
>= sizeof(DHCP6Message
));
466 len
-= sizeof(DHCP6Message
);
467 for (size_t offset
= 0; offset
< len
;) {
470 const uint8_t *optval
;
472 r
= dhcp6_option_parse(message
->options
, len
, &offset
, &optcode
, &optlen
, &optval
);
477 case SD_DHCP6_OPTION_CLIENTID
:
478 if (dhcp6_lease_get_clientid(lease
, NULL
, NULL
) >= 0)
479 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple client IDs",
480 dhcp6_message_type_to_string(message
->type
));
482 r
= dhcp6_lease_set_clientid(lease
, optval
, optlen
);
488 case SD_DHCP6_OPTION_SERVERID
:
489 if (dhcp6_lease_get_serverid(lease
, NULL
, NULL
) >= 0)
490 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "%s contains multiple server IDs",
491 dhcp6_message_type_to_string(message
->type
));
493 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
499 case SD_DHCP6_OPTION_PREFERENCE
:
503 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
509 case SD_DHCP6_OPTION_STATUS_CODE
: {
510 _cleanup_free_
char *msg
= NULL
;
512 r
= dhcp6_option_parse_status(optval
, optlen
, &msg
);
517 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
518 "Received %s message with non-zero status: %s%s%s",
519 dhcp6_message_type_to_string(message
->type
),
520 strempty(msg
), isempty(msg
) ? "" : ": ",
521 dhcp6_message_status_to_string(r
));
524 case SD_DHCP6_OPTION_IA_NA
: {
525 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
527 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
528 log_dhcp6_client(client
, "Ignoring IA NA option in information requesting mode.");
532 r
= dhcp6_option_parse_ia(client
, client
->ia_na
.header
.id
, optcode
, optlen
, optval
, &ia
);
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
);
562 log_dhcp6_client(client
, "Received duplicate matching IA_PD option, ignoring.");
566 dhcp6_ia_free(lease
->ia_pd
);
567 lease
->ia_pd
= TAKE_PTR(ia
);
570 case SD_DHCP6_OPTION_RAPID_COMMIT
:
571 r
= dhcp6_lease_set_rapid_commit(lease
);
577 case SD_DHCP6_OPTION_DNS_SERVERS
:
578 r
= dhcp6_lease_add_dns(lease
, optval
, optlen
);
580 log_dhcp6_client_errno(client
, r
, "Failed to parse DNS server option, ignoring: %m");
584 case SD_DHCP6_OPTION_DOMAIN_LIST
:
585 r
= dhcp6_lease_add_domains(lease
, optval
, optlen
);
587 log_dhcp6_client_errno(client
, r
, "Failed to parse domain list option, ignoring: %m");
591 case SD_DHCP6_OPTION_NTP_SERVER
:
592 r
= dhcp6_lease_add_ntp(lease
, optval
, optlen
);
594 log_dhcp6_client_errno(client
, r
, "Failed to parse NTP server option, ignoring: %m");
598 case SD_DHCP6_OPTION_SNTP_SERVERS
:
599 r
= dhcp6_lease_add_sntp(lease
, optval
, optlen
);
601 log_dhcp6_client_errno(client
, r
, "Failed to parse SNTP server option, ignoring: %m");
605 case SD_DHCP6_OPTION_CLIENT_FQDN
:
606 r
= dhcp6_lease_set_fqdn(lease
, optval
, optlen
);
608 log_dhcp6_client_errno(client
, r
, "Failed to parse FQDN option, ignoring: %m");
612 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
616 irt
= unaligned_read_be32((be32_t
*) optval
) * USEC_PER_SEC
;
623 if (dhcp6_lease_get_clientid(lease
, &clientid
, &clientid_len
) < 0)
624 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
625 "%s message does not contain client ID. Ignoring.",
626 dhcp6_message_type_to_string(message
->type
));
628 if (memcmp_nn(clientid
, clientid_len
, &client
->duid
, client
->duid_len
) != 0)
629 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
630 "The client ID in %s message does not match. Ignoring.",
631 dhcp6_message_type_to_string(message
->type
));
633 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
634 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
635 log_dhcp6_client(client
, "New information request will be refused in %s.",
636 FORMAT_TIMESPAN(client
->information_refresh_time_usec
, USEC_PER_SEC
));
639 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
641 return log_dhcp6_client_errno(client
, r
, "%s has no server id",
642 dhcp6_message_type_to_string(message
->type
));
644 if (!lease
->ia_na
&& !lease
->ia_pd
)
645 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
646 "No IA_PD prefix or IA_NA address received. Ignoring.");
648 dhcp6_lease_set_lifetime(lease
);
654 static sd_dhcp6_lease
*dhcp6_lease_free(sd_dhcp6_lease
*lease
) {
658 free(lease
->clientid
);
659 free(lease
->serverid
);
660 dhcp6_ia_free(lease
->ia_na
);
661 dhcp6_ia_free(lease
->ia_pd
);
664 strv_free(lease
->domains
);
666 strv_free(lease
->ntp_fqdn
);
672 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease
, sd_dhcp6_lease
, dhcp6_lease_free
);
674 int dhcp6_lease_new(sd_dhcp6_lease
**ret
) {
675 sd_dhcp6_lease
*lease
;
679 lease
= new(sd_dhcp6_lease
, 1);
683 *lease
= (sd_dhcp6_lease
) {
691 int dhcp6_lease_new_from_message(
692 sd_dhcp6_client
*client
,
693 const DHCP6Message
*message
,
695 const triple_timestamp
*timestamp
,
696 const struct in6_addr
*server_address
,
697 sd_dhcp6_lease
**ret
) {
699 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
704 assert(len
>= sizeof(DHCP6Message
));
707 r
= dhcp6_lease_new(&lease
);
711 dhcp6_lease_set_timestamp(lease
, timestamp
);
712 dhcp6_lease_set_server_address(lease
, server_address
);
714 r
= dhcp6_lease_parse_message(client
, lease
, message
, len
);
718 *ret
= TAKE_PTR(lease
);