1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
8 #include <linux/if_arp.h>
9 #include <linux/if_infiniband.h>
11 #include "sd-dhcp6-client.h"
13 #include "alloc-util.h"
14 #include "dhcp-identifier.h"
15 #include "dhcp6-internal.h"
16 #include "dhcp6-lease-internal.h"
17 #include "dhcp6-protocol.h"
18 #include "dns-domain.h"
19 #include "event-util.h"
21 #include "hexdecoct.h"
22 #include "hostname-util.h"
23 #include "in-addr-util.h"
24 #include "network-internal.h"
25 #include "random-util.h"
26 #include "socket-util.h"
27 #include "string-table.h"
31 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
33 #define IRT_DEFAULT (1 * USEC_PER_DAY)
34 #define IRT_MINIMUM (600 * USEC_PER_SEC)
36 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
38 DHCP6_REQUEST_IA_NA
= 1,
39 DHCP6_REQUEST_IA_TA
= 2, /* currently not used */
40 DHCP6_REQUEST_IA_PD
= 4,
43 struct sd_dhcp6_client
{
46 enum DHCP6State state
;
50 DHCP6Address hint_pd_prefix
;
51 struct in6_addr local_address
;
52 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
57 sd_event_source
*timeout_t1
;
58 sd_event_source
*timeout_t2
;
60 be32_t transaction_id
;
61 usec_t transaction_start
;
62 struct sd_dhcp6_lease
*lease
;
64 bool information_request
;
67 size_t req_opts_allocated
;
73 sd_event_source
*receive_message
;
74 usec_t retransmit_time
;
75 uint8_t retransmit_count
;
76 sd_event_source
*timeout_resend
;
77 sd_event_source
*timeout_resend_expire
;
78 sd_dhcp6_client_callback_t callback
;
82 usec_t information_request_time_usec
;
83 usec_t information_refresh_time_usec
;
84 OrderedHashmap
*extra_options
;
85 OrderedHashmap
*vendor_options
;
88 static const uint16_t default_req_opts
[] = {
89 SD_DHCP6_OPTION_DNS_SERVERS
,
90 SD_DHCP6_OPTION_DOMAIN_LIST
,
91 SD_DHCP6_OPTION_NTP_SERVER
,
92 SD_DHCP6_OPTION_SNTP_SERVERS
,
95 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
96 [DHCP6_SOLICIT
] = "SOLICIT",
97 [DHCP6_ADVERTISE
] = "ADVERTISE",
98 [DHCP6_REQUEST
] = "REQUEST",
99 [DHCP6_CONFIRM
] = "CONFIRM",
100 [DHCP6_RENEW
] = "RENEW",
101 [DHCP6_REBIND
] = "REBIND",
102 [DHCP6_REPLY
] = "REPLY",
103 [DHCP6_RELEASE
] = "RELEASE",
104 [DHCP6_DECLINE
] = "DECLINE",
105 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
106 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
107 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
108 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
111 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
113 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
114 [DHCP6_STATUS_SUCCESS
] = "Success",
115 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
116 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
117 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
118 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
119 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
120 [DHCP6_STATUS_NO_PREFIX_AVAIL
] = "No prefix available",
121 [DHCP6_STATUS_UNKNOWN_QUERY_TYPE
] = "Unknown query type",
122 [DHCP6_STATUS_MALFORMED_QUERY
] = "Malformed query",
123 [DHCP6_STATUS_NOT_CONFIGURED
] = "Not configured",
124 [DHCP6_STATUS_NOT_ALLOWED
] = "Not allowed",
125 [DHCP6_STATUS_QUERY_TERMINATED
] = "Query terminated",
126 [DHCP6_STATUS_DATA_MISSING
] = "Data missing",
127 [DHCP6_STATUS_CATCHUP_COMPLETE
] = "Catch up complete",
128 [DHCP6_STATUS_NOT_SUPPORTED
] = "Not supported",
129 [DHCP6_STATUS_TLS_CONNECTION_REFUSED
] = "TLS connection refused",
130 [DHCP6_STATUS_ADDRESS_IN_USE
] = "Address in use",
131 [DHCP6_STATUS_CONFIGURATION_CONFLICT
] = "Configuration conflict",
132 [DHCP6_STATUS_MISSING_BINDING_INFORMATION
] = "Missing binding information",
133 [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION
] = "Outdated binding information",
134 [DHCP6_STATUS_SERVER_SHUTTING_DOWN
] = "Server shutting down",
135 [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED
] = "DNS update not supported",
136 [DHCP6_STATUS_EXCESSIVE_TIME_SKEW
] = "Excessive time skew",
139 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
141 #define DHCP6_CLIENT_DONT_DESTROY(client) \
142 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
144 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
146 int sd_dhcp6_client_set_callback(
147 sd_dhcp6_client
*client
,
148 sd_dhcp6_client_callback_t cb
,
151 assert_return(client
, -EINVAL
);
153 client
->callback
= cb
;
154 client
->userdata
= userdata
;
159 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
161 assert_return(client
, -EINVAL
);
162 assert_return(ifindex
>= -1, -EINVAL
);
163 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
165 client
->ifindex
= ifindex
;
169 int sd_dhcp6_client_set_local_address(
170 sd_dhcp6_client
*client
,
171 const struct in6_addr
*local_address
) {
173 assert_return(client
, -EINVAL
);
174 assert_return(local_address
, -EINVAL
);
175 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
177 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
179 client
->local_address
= *local_address
;
184 int sd_dhcp6_client_set_mac(
185 sd_dhcp6_client
*client
,
186 const uint8_t *addr
, size_t addr_len
,
189 assert_return(client
, -EINVAL
);
190 assert_return(addr
, -EINVAL
);
191 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
192 assert_return(arp_type
> 0, -EINVAL
);
194 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
196 if (arp_type
== ARPHRD_ETHER
)
197 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
198 else if (arp_type
== ARPHRD_INFINIBAND
)
199 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
203 if (client
->mac_addr_len
== addr_len
&&
204 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
207 memcpy(&client
->mac_addr
, addr
, addr_len
);
208 client
->mac_addr_len
= addr_len
;
209 client
->arp_type
= arp_type
;
214 int sd_dhcp6_client_set_prefix_delegation_hint(
215 sd_dhcp6_client
*client
,
217 const struct in6_addr
*pd_address
) {
219 assert_return(client
, -EINVAL
);
220 assert_return(pd_address
, -EINVAL
);
222 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
224 client
->hint_pd_prefix
.iapdprefix
.address
= *pd_address
;
225 client
->hint_pd_prefix
.iapdprefix
.prefixlen
= prefixlen
;
230 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
233 assert_return(client
, -EINVAL
);
234 assert_return(v
, -EINVAL
);
236 r
= ordered_hashmap_ensure_allocated(&client
->vendor_options
, &dhcp6_option_hash_ops
);
240 r
= ordered_hashmap_put(client
->vendor_options
, v
, v
);
244 sd_dhcp6_option_ref(v
);
249 static int client_ensure_duid(sd_dhcp6_client
*client
) {
250 if (client
->duid_len
!= 0)
253 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
257 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
258 * without further modification. Otherwise, if duid_type is supported, DUID
259 * is set based on that type. Otherwise, an error is returned.
261 static int dhcp6_client_set_duid_internal(
262 sd_dhcp6_client
*client
,
269 assert_return(client
, -EINVAL
);
270 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
271 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
274 r
= dhcp_validate_duid_len(duid_type
, duid_len
, true);
276 r
= dhcp_validate_duid_len(duid_type
, duid_len
, false);
278 return log_dhcp6_client_errno(client
, r
, "Failed to validate length of DUID: %m");
280 log_dhcp6_client(client
, "Using DUID of type %u of incorrect length, proceeding.", duid_type
);
283 client
->duid
.type
= htobe16(duid_type
);
284 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
285 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
289 if (client
->mac_addr_len
== 0)
290 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EOPNOTSUPP
), "Failed to set DUID-LLT, MAC address is not set.");
292 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
294 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LLT: %m");
297 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
299 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-EN: %m");
302 if (client
->mac_addr_len
== 0)
303 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EOPNOTSUPP
), "Failed to set DUID-LL, MAC address is not set.");
305 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
307 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LL: %m");
310 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
312 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-UUID: %m");
315 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "Invalid DUID type");
321 int sd_dhcp6_client_set_duid(
322 sd_dhcp6_client
*client
,
326 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
329 int sd_dhcp6_client_set_duid_llt(
330 sd_dhcp6_client
*client
,
332 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
335 static const char* const dhcp6_duid_type_table
[_DUID_TYPE_MAX
] = {
336 [DUID_TYPE_LLT
] = "DUID-LLT",
337 [DUID_TYPE_EN
] = "DUID-EN/Vendor",
338 [DUID_TYPE_LL
] = "DUID-LL",
339 [DUID_TYPE_UUID
] = "UUID",
341 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type
, DUIDType
);
343 int sd_dhcp6_client_duid_as_string(
344 sd_dhcp6_client
*client
,
346 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
350 assert_return(client
, -EINVAL
);
351 assert_return(client
->duid_len
> 0, -ENODATA
);
353 v
= dhcp6_duid_type_to_string(be16toh(client
->duid
.type
));
359 r
= asprintf(&s
, "%0x", client
->duid
.type
);
364 t
= hexmem(&client
->duid
.raw
.data
, client
->duid_len
);
368 p
= strjoin(s
, ":", t
);
377 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
378 assert_return(client
, -EINVAL
);
379 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
381 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
382 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
383 client
->iaid_set
= true;
388 int sd_dhcp6_client_get_iaid(sd_dhcp6_client
*client
, uint32_t *iaid
) {
389 assert_return(client
, -EINVAL
);
390 assert_return(iaid
, -EINVAL
);
392 if (!client
->iaid_set
)
395 *iaid
= be32toh(client
->ia_na
.ia_na
.id
);
400 int sd_dhcp6_client_set_fqdn(
401 sd_dhcp6_client
*client
,
404 assert_return(client
, -EINVAL
);
406 /* Make sure FQDN qualifies as DNS and as Linux hostname */
408 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
411 return free_and_strdup(&client
->fqdn
, fqdn
);
414 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
415 assert_return(client
, -EINVAL
);
416 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
418 client
->information_request
= enabled
;
423 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
424 assert_return(client
, -EINVAL
);
425 assert_return(enabled
, -EINVAL
);
427 *enabled
= client
->information_request
;
432 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
435 assert_return(client
, -EINVAL
);
436 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
438 if (option
<= 0 || option
>= UINT8_MAX
)
441 for (t
= 0; t
< client
->req_opts_len
; t
++)
442 if (client
->req_opts
[t
] == htobe16(option
))
445 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
446 client
->req_opts_len
+ 1))
449 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
454 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client
*client
, const char *mudurl
) {
456 assert_return(client
, -EINVAL
);
457 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
458 assert_return(mudurl
, -EINVAL
);
459 assert_return(strlen(mudurl
) <= UINT8_MAX
, -EINVAL
);
460 assert_return(http_url_is_valid(mudurl
), -EINVAL
);
462 return free_and_strdup(&client
->mudurl
, mudurl
);
465 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client
*client
, char **user_class
) {
466 _cleanup_strv_free_
char **s
= NULL
;
469 assert_return(client
, -EINVAL
);
470 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
472 assert_return(user_class
, -EINVAL
);
474 STRV_FOREACH(p
, user_class
)
475 if (strlen(*p
) > UINT16_MAX
)
476 return -ENAMETOOLONG
;
478 s
= strv_copy(user_class
);
482 client
->user_class
= TAKE_PTR(s
);
487 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client
*client
, char **vendor_class
) {
488 _cleanup_strv_free_
char **s
= NULL
;
491 assert_return(client
, -EINVAL
);
492 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
493 assert_return(vendor_class
, -EINVAL
);
495 STRV_FOREACH(p
, vendor_class
)
496 if (strlen(*p
) > UINT8_MAX
)
497 return -ENAMETOOLONG
;
499 s
= strv_copy(vendor_class
);
503 client
->vendor_class
= TAKE_PTR(s
);
508 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
509 assert_return(client
, -EINVAL
);
510 assert_return(delegation
, -EINVAL
);
512 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
517 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
518 assert_return(client
, -EINVAL
);
520 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
525 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
526 assert_return(client
, -EINVAL
);
527 assert_return(request
, -EINVAL
);
529 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
534 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
535 assert_return(client
, -EINVAL
);
537 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
542 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
543 assert_return(client
, -EINVAL
);
545 client
->transaction_id
= transaction_id
;
550 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
551 assert_return(client
, -EINVAL
);
557 *ret
= client
->lease
;
562 int sd_dhcp6_client_add_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
565 assert_return(client
, -EINVAL
);
566 assert_return(v
, -EINVAL
);
568 r
= ordered_hashmap_ensure_allocated(&client
->extra_options
, &dhcp6_option_hash_ops
);
572 r
= ordered_hashmap_put(client
->extra_options
, UINT_TO_PTR(v
->option
), v
);
576 sd_dhcp6_option_ref(v
);
580 static void client_notify(sd_dhcp6_client
*client
, int event
) {
583 if (client
->callback
)
584 client
->callback(client
, event
, client
->userdata
);
587 static int client_reset(sd_dhcp6_client
*client
) {
590 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
592 client
->receive_message
=
593 sd_event_source_unref(client
->receive_message
);
595 client
->transaction_id
= 0;
596 client
->transaction_start
= 0;
598 client
->retransmit_time
= 0;
599 client
->retransmit_count
= 0;
601 (void) event_source_disable(client
->timeout_resend
);
602 (void) event_source_disable(client
->timeout_resend_expire
);
603 (void) event_source_disable(client
->timeout_t1
);
604 (void) event_source_disable(client
->timeout_t2
);
606 client
->state
= DHCP6_STATE_STOPPED
;
611 static void client_stop(sd_dhcp6_client
*client
, int error
) {
612 DHCP6_CLIENT_DONT_DESTROY(client
);
616 client_notify(client
, error
);
618 client_reset(client
);
621 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
622 _cleanup_free_ DHCP6Message
*message
= NULL
;
623 struct in6_addr all_servers
=
624 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
625 struct sd_dhcp6_option
*j
;
626 size_t len
, optlen
= 512;
635 len
= sizeof(DHCP6Message
) + optlen
;
637 message
= malloc0(len
);
641 opt
= (uint8_t *)(message
+ 1);
643 message
->transaction_id
= client
->transaction_id
;
645 switch(client
->state
) {
646 case DHCP6_STATE_INFORMATION_REQUEST
:
647 message
->type
= DHCP6_INFORMATION_REQUEST
;
649 if (client
->mudurl
) {
650 r
= dhcp6_option_append(&opt
, &optlen
,
651 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
659 case DHCP6_STATE_SOLICITATION
:
660 message
->type
= DHCP6_SOLICIT
;
662 r
= dhcp6_option_append(&opt
, &optlen
,
663 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
667 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
668 r
= dhcp6_option_append_ia(&opt
, &optlen
,
675 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
680 if (client
->mudurl
) {
681 r
= dhcp6_option_append(&opt
, &optlen
,
682 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
688 if (client
->user_class
) {
689 r
= dhcp6_option_append_user_class(&opt
, &optlen
, client
->user_class
);
694 if (client
->vendor_class
) {
695 r
= dhcp6_option_append_vendor_class(&opt
, &optlen
, client
->vendor_class
);
700 if (!ordered_hashmap_isempty(client
->vendor_options
)) {
701 r
= dhcp6_option_append_vendor_option(&opt
, &optlen
,
702 client
->vendor_options
);
707 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
708 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
, &client
->hint_pd_prefix
);
718 case DHCP6_STATE_REQUEST
:
719 case DHCP6_STATE_RENEW
:
721 if (client
->state
== DHCP6_STATE_REQUEST
)
722 message
->type
= DHCP6_REQUEST
;
724 message
->type
= DHCP6_RENEW
;
726 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
727 client
->lease
->serverid_len
,
728 client
->lease
->serverid
);
732 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
733 r
= dhcp6_option_append_ia(&opt
, &optlen
,
740 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
745 if (client
->mudurl
) {
746 r
= dhcp6_option_append(&opt
, &optlen
,
747 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
753 if (client
->user_class
) {
754 r
= dhcp6_option_append_user_class(&opt
, &optlen
, client
->user_class
);
759 if (client
->vendor_class
) {
760 r
= dhcp6_option_append_vendor_class(&opt
, &optlen
, client
->vendor_class
);
765 if (!ordered_hashmap_isempty(client
->vendor_options
)) {
766 r
= dhcp6_option_append_vendor_option(&opt
, &optlen
, client
->vendor_options
);
771 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
772 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
, NULL
);
782 case DHCP6_STATE_REBIND
:
783 message
->type
= DHCP6_REBIND
;
785 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
786 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
792 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
797 if (client
->mudurl
) {
798 r
= dhcp6_option_append(&opt
, &optlen
,
799 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
805 if (client
->user_class
) {
806 r
= dhcp6_option_append_user_class(&opt
, &optlen
, client
->user_class
);
811 if (client
->vendor_class
) {
812 r
= dhcp6_option_append_vendor_class(&opt
, &optlen
, client
->vendor_class
);
817 if (!ordered_hashmap_isempty(client
->vendor_options
)) {
818 r
= dhcp6_option_append_vendor_option(&opt
, &optlen
, client
->vendor_options
);
823 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
824 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
, NULL
);
834 case DHCP6_STATE_STOPPED
:
835 case DHCP6_STATE_BOUND
:
839 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
840 client
->req_opts_len
* sizeof(be16_t
),
845 assert(client
->duid_len
);
846 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
847 client
->duid_len
, &client
->duid
);
851 elapsed_usec
= time_now
- client
->transaction_start
;
852 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
853 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
855 elapsed_time
= 0xffff;
857 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
858 sizeof(elapsed_time
), &elapsed_time
);
862 ORDERED_HASHMAP_FOREACH(j
, client
->extra_options
, i
) {
863 r
= dhcp6_option_append(&opt
, &optlen
, j
->option
, j
->length
, j
->data
);
868 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
873 log_dhcp6_client(client
, "Sent %s",
874 dhcp6_message_type_to_string(message
->type
));
879 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
880 sd_dhcp6_client
*client
= userdata
;
884 assert(client
->lease
);
886 (void) event_source_disable(client
->timeout_t2
);
888 log_dhcp6_client(client
, "Timeout T2");
890 client_start(client
, DHCP6_STATE_REBIND
);
895 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
896 sd_dhcp6_client
*client
= userdata
;
900 assert(client
->lease
);
902 (void) event_source_disable(client
->timeout_t1
);
904 log_dhcp6_client(client
, "Timeout T1");
906 client_start(client
, DHCP6_STATE_RENEW
);
911 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
912 sd_dhcp6_client
*client
= userdata
;
913 DHCP6_CLIENT_DONT_DESTROY(client
);
914 enum DHCP6State state
;
918 assert(client
->event
);
920 state
= client
->state
;
922 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
924 /* RFC 3315, section 18.1.4., says that "...the client may choose to
925 use a Solicit message to locate a new DHCP server..." */
926 if (state
== DHCP6_STATE_REBIND
)
927 client_start(client
, DHCP6_STATE_SOLICITATION
);
932 static usec_t
client_timeout_compute_random(usec_t val
) {
933 return val
- (random_u32() % USEC_PER_SEC
) * val
/ 10 / USEC_PER_SEC
;
936 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
938 sd_dhcp6_client
*client
= userdata
;
939 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
940 usec_t max_retransmit_duration
= 0;
941 uint8_t max_retransmit_count
= 0;
942 char time_string
[FORMAT_TIMESPAN_MAX
];
946 assert(client
->event
);
948 (void) event_source_disable(client
->timeout_resend
);
950 switch (client
->state
) {
951 case DHCP6_STATE_INFORMATION_REQUEST
:
952 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
953 max_retransmit_time
= DHCP6_INF_MAX_RT
;
957 case DHCP6_STATE_SOLICITATION
:
959 if (client
->retransmit_count
&& client
->lease
) {
960 client_start(client
, DHCP6_STATE_REQUEST
);
964 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
965 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
969 case DHCP6_STATE_REQUEST
:
970 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
971 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
972 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
976 case DHCP6_STATE_RENEW
:
977 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
978 max_retransmit_time
= DHCP6_REN_MAX_RT
;
980 /* RFC 3315, section 18.1.3. says max retransmit duration will
981 be the remaining time until T2. Instead of setting MRD,
982 wait for T2 to trigger with the same end result */
986 case DHCP6_STATE_REBIND
:
987 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
988 max_retransmit_time
= DHCP6_REB_MAX_RT
;
990 if (event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
993 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
, &expire
);
995 client_stop(client
, r
);
998 max_retransmit_duration
= expire
* USEC_PER_SEC
;
1003 case DHCP6_STATE_STOPPED
:
1004 case DHCP6_STATE_BOUND
:
1008 if (max_retransmit_count
> 0 &&
1009 client
->retransmit_count
>= max_retransmit_count
) {
1010 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
1014 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1018 r
= client_send_message(client
, time_now
);
1020 client
->retransmit_count
++;
1022 if (client
->retransmit_time
== 0) {
1023 client
->retransmit_time
=
1024 client_timeout_compute_random(init_retransmit_time
);
1026 if (client
->state
== DHCP6_STATE_SOLICITATION
)
1027 client
->retransmit_time
+= init_retransmit_time
/ 10;
1030 if (max_retransmit_time
> 0 &&
1031 client
->retransmit_time
> max_retransmit_time
/ 2)
1032 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
1034 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
1037 log_dhcp6_client(client
, "Next retransmission in %s",
1038 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
1040 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
1041 clock_boottime_or_monotonic(),
1042 time_now
+ client
->retransmit_time
, 10 * USEC_PER_MSEC
,
1043 client_timeout_resend
, client
,
1044 client
->event_priority
, "dhcp6-resend-timer", true);
1048 if (max_retransmit_duration
> 0 && event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
1050 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
1051 max_retransmit_duration
/ USEC_PER_SEC
);
1053 r
= event_reset_time(client
->event
, &client
->timeout_resend_expire
,
1054 clock_boottime_or_monotonic(),
1055 time_now
+ max_retransmit_duration
, USEC_PER_SEC
,
1056 client_timeout_resend_expire
, client
,
1057 client
->event_priority
, "dhcp6-resend-expire-timer", true);
1064 client_stop(client
, r
);
1069 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
1075 if (client
->iaid_set
)
1078 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, true, &iaid
);
1082 client
->ia_na
.ia_na
.id
= iaid
;
1083 client
->ia_pd
.ia_pd
.id
= iaid
;
1084 client
->iaid_set
= true;
1089 static int client_parse_message(
1090 sd_dhcp6_client
*client
,
1091 DHCP6Message
*message
,
1093 sd_dhcp6_lease
*lease
) {
1095 uint16_t ia_na_status
= 0, ia_pd_status
= 0;
1096 uint32_t lt_t1
= ~0, lt_t2
= ~0;
1097 usec_t irt
= IRT_DEFAULT
;
1098 bool clientid
= false;
1104 assert(len
>= sizeof(DHCP6Message
));
1107 len
-= sizeof(DHCP6Message
);
1110 DHCP6Option
*option
= (DHCP6Option
*) &message
->options
[pos
];
1111 uint16_t optcode
, optlen
;
1116 if (len
< pos
+ offsetof(DHCP6Option
, data
))
1119 optcode
= be16toh(option
->code
);
1120 optlen
= be16toh(option
->len
);
1121 optval
= option
->data
;
1123 if (len
< pos
+ offsetof(DHCP6Option
, data
) + optlen
)
1127 case SD_DHCP6_OPTION_CLIENTID
:
1129 log_dhcp6_client(client
, "%s contains multiple clientids",
1130 dhcp6_message_type_to_string(message
->type
));
1134 if (optlen
!= client
->duid_len
||
1135 memcmp(&client
->duid
, optval
, optlen
) != 0) {
1136 log_dhcp6_client(client
, "%s DUID does not match",
1137 dhcp6_message_type_to_string(message
->type
));
1145 case SD_DHCP6_OPTION_SERVERID
:
1146 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1148 log_dhcp6_client(client
, "%s contains multiple serverids",
1149 dhcp6_message_type_to_string(message
->type
));
1153 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
1159 case SD_DHCP6_OPTION_PREFERENCE
:
1163 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
1169 case SD_DHCP6_OPTION_STATUS_CODE
:
1170 status
= dhcp6_option_parse_status(option
, optlen
+ sizeof(DHCP6Option
));
1175 log_dhcp6_client(client
, "%s Status %s",
1176 dhcp6_message_type_to_string(message
->type
),
1177 dhcp6_message_status_to_string(status
));
1184 case SD_DHCP6_OPTION_IA_NA
:
1185 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1186 log_dhcp6_client(client
, "Information request ignoring IA NA option");
1191 r
= dhcp6_option_parse_ia(option
, &lease
->ia
, &ia_na_status
);
1192 if (r
< 0 && r
!= -ENOMSG
)
1195 if (ia_na_status
== DHCP6_STATUS_NO_ADDRS_AVAIL
) {
1196 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1200 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
1204 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
1205 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
1206 dhcp6_message_type_to_string(message
->type
));
1210 if (lease
->ia
.addresses
) {
1211 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
1212 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
1217 case SD_DHCP6_OPTION_IA_PD
:
1218 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1219 log_dhcp6_client(client
, "Information request ignoring IA PD option");
1224 r
= dhcp6_option_parse_ia(option
, &lease
->pd
, &ia_pd_status
);
1225 if (r
< 0 && r
!= -ENOMSG
)
1228 if (ia_pd_status
== DHCP6_STATUS_NO_PREFIX_AVAIL
) {
1229 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1233 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
1237 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
1238 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
1239 dhcp6_message_type_to_string(message
->type
));
1243 if (lease
->pd
.addresses
) {
1244 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
1245 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
1250 case SD_DHCP6_OPTION_RAPID_COMMIT
:
1251 r
= dhcp6_lease_set_rapid_commit(lease
);
1257 case SD_DHCP6_OPTION_DNS_SERVERS
:
1258 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
1264 case SD_DHCP6_OPTION_DOMAIN_LIST
:
1265 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
1271 case SD_DHCP6_OPTION_NTP_SERVER
:
1272 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
1278 case SD_DHCP6_OPTION_SNTP_SERVERS
:
1279 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
1285 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
1289 irt
= unaligned_read_be32((be32_t
*) optval
) * USEC_PER_SEC
;
1293 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1296 if (ia_na_status
> 0 && ia_pd_status
> 0) {
1297 log_dhcp6_client(client
, "No IA_PD prefix or IA_NA address received. Ignoring.");
1302 log_dhcp6_client(client
, "%s has incomplete options",
1303 dhcp6_message_type_to_string(message
->type
));
1307 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1308 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1310 log_dhcp6_client(client
, "%s has no server id",
1311 dhcp6_message_type_to_string(message
->type
));
1316 if (lease
->ia
.addresses
) {
1317 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1318 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1321 if (lease
->pd
.addresses
) {
1322 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1323 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1327 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
1332 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1333 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1340 if (reply
->type
!= DHCP6_REPLY
)
1343 r
= dhcp6_lease_new(&lease
);
1347 r
= client_parse_message(client
, reply
, len
, lease
);
1351 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1352 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1360 sd_dhcp6_lease_unref(client
->lease
);
1361 client
->lease
= TAKE_PTR(lease
);
1363 return DHCP6_STATE_BOUND
;
1366 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1367 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1368 uint8_t pref_advertise
= 0, pref_lease
= 0;
1371 if (advertise
->type
!= DHCP6_ADVERTISE
)
1374 r
= dhcp6_lease_new(&lease
);
1378 r
= client_parse_message(client
, advertise
, len
, lease
);
1382 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1386 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1388 if (r
< 0 || pref_advertise
> pref_lease
) {
1389 sd_dhcp6_lease_unref(client
->lease
);
1390 client
->lease
= TAKE_PTR(lease
);
1394 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1395 r
= DHCP6_STATE_REQUEST
;
1400 static int client_receive_message(
1406 sd_dhcp6_client
*client
= userdata
;
1407 DHCP6_CLIENT_DONT_DESTROY(client
);
1408 _cleanup_free_ DHCP6Message
*message
= NULL
;
1409 ssize_t buflen
, len
;
1414 assert(client
->event
);
1416 buflen
= next_datagram_size_fd(fd
);
1417 if (buflen
== -ENETDOWN
) {
1418 /* the link is down. Don't return an error or the I/O event
1419 source will be disconnected and we won't be able to receive
1420 packets again when the link comes back. */
1426 message
= malloc(buflen
);
1430 len
= recv(fd
, message
, buflen
, 0);
1432 /* see comment above for why we shouldn't error out on ENETDOWN. */
1433 if (IN_SET(errno
, EAGAIN
, EINTR
, ENETDOWN
))
1436 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1439 if ((size_t) len
< sizeof(DHCP6Message
)) {
1440 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1444 switch(message
->type
) {
1452 case DHCP6_INFORMATION_REQUEST
:
1453 case DHCP6_RELAY_FORW
:
1454 case DHCP6_RELAY_REPL
:
1457 case DHCP6_ADVERTISE
:
1459 case DHCP6_RECONFIGURE
:
1463 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1467 if (client
->transaction_id
!= (message
->transaction_id
&
1468 htobe32(0x00ffffff)))
1471 switch (client
->state
) {
1472 case DHCP6_STATE_INFORMATION_REQUEST
:
1473 r
= client_receive_reply(client
, message
, len
);
1477 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1479 client_start(client
, DHCP6_STATE_STOPPED
);
1483 case DHCP6_STATE_SOLICITATION
:
1484 r
= client_receive_advertise(client
, message
, len
);
1486 if (r
== DHCP6_STATE_REQUEST
) {
1487 client_start(client
, r
);
1492 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1493 case DHCP6_STATE_REQUEST
:
1494 case DHCP6_STATE_RENEW
:
1495 case DHCP6_STATE_REBIND
:
1497 r
= client_receive_reply(client
, message
, len
);
1501 if (r
== DHCP6_STATE_BOUND
) {
1503 r
= client_start(client
, DHCP6_STATE_BOUND
);
1505 client_stop(client
, r
);
1509 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1514 case DHCP6_STATE_BOUND
:
1518 case DHCP6_STATE_STOPPED
:
1522 log_dhcp6_client(client
, "Recv %s",
1523 dhcp6_message_type_to_string(message
->type
));
1528 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1529 uint32_t *lifetime_t2
) {
1530 assert_return(client
, -EINVAL
);
1531 assert_return(client
->lease
, -EINVAL
);
1533 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1534 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1535 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1540 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1541 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1542 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1550 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1552 usec_t timeout
, time_now
;
1553 char time_string
[FORMAT_TIMESPAN_MAX
];
1554 uint32_t lifetime_t1
, lifetime_t2
;
1556 assert_return(client
, -EINVAL
);
1557 assert_return(client
->event
, -EINVAL
);
1558 assert_return(client
->ifindex
> 0, -EINVAL
);
1559 assert_return(client
->state
!= state
, -EINVAL
);
1561 (void) event_source_disable(client
->timeout_resend_expire
);
1562 (void) event_source_disable(client
->timeout_resend
);
1563 client
->retransmit_time
= 0;
1564 client
->retransmit_count
= 0;
1566 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1570 if (!client
->receive_message
) {
1571 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1572 client
->fd
, EPOLLIN
, client_receive_message
,
1577 r
= sd_event_source_set_priority(client
->receive_message
,
1578 client
->event_priority
);
1582 r
= sd_event_source_set_description(client
->receive_message
,
1583 "dhcp6-receive-message");
1589 case DHCP6_STATE_STOPPED
:
1590 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1591 client
->state
= DHCP6_STATE_STOPPED
;
1597 case DHCP6_STATE_SOLICITATION
:
1598 client
->state
= DHCP6_STATE_SOLICITATION
;
1602 case DHCP6_STATE_INFORMATION_REQUEST
:
1603 case DHCP6_STATE_REQUEST
:
1604 case DHCP6_STATE_RENEW
:
1605 case DHCP6_STATE_REBIND
:
1607 client
->state
= state
;
1611 case DHCP6_STATE_BOUND
:
1613 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1617 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1618 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1619 lifetime_t1
, lifetime_t2
);
1624 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1626 log_dhcp6_client(client
, "T1 expires in %s",
1627 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1629 r
= event_reset_time(client
->event
, &client
->timeout_t1
,
1630 clock_boottime_or_monotonic(),
1631 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1632 client_timeout_t1
, client
,
1633 client
->event_priority
, "dhcp6-t1-timeout", true);
1637 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1639 log_dhcp6_client(client
, "T2 expires in %s",
1640 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1642 r
= event_reset_time(client
->event
, &client
->timeout_t2
,
1643 clock_boottime_or_monotonic(),
1644 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1645 client_timeout_t2
, client
,
1646 client
->event_priority
, "dhcp6-t2-timeout", true);
1650 client
->state
= state
;
1655 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1656 client
->transaction_start
= time_now
;
1658 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
1659 clock_boottime_or_monotonic(),
1661 client_timeout_resend
, client
,
1662 client
->event_priority
, "dhcp6-resend-timeout", true);
1669 client_reset(client
);
1673 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1674 assert_return(client
, -EINVAL
);
1676 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1678 client
->fd
= safe_close(client
->fd
);
1683 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1684 assert_return(client
, -EINVAL
);
1686 return client
->state
!= DHCP6_STATE_STOPPED
;
1689 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1690 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1693 assert_return(client
, -EINVAL
);
1694 assert_return(client
->event
, -EINVAL
);
1695 assert_return(client
->ifindex
> 0, -EINVAL
);
1696 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1698 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1701 if (!client
->information_request
&& !client
->request
)
1704 r
= client_reset(client
);
1708 r
= client_ensure_iaid(client
);
1712 r
= client_ensure_duid(client
);
1716 if (client
->fd
< 0) {
1717 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1719 _cleanup_free_
char *p
= NULL
;
1721 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1722 return log_dhcp6_client_errno(client
, r
,
1723 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1729 if (client
->information_request
) {
1730 usec_t t
= now(CLOCK_MONOTONIC
);
1732 if (t
< usec_add(client
->information_request_time_usec
, client
->information_refresh_time_usec
))
1735 client
->information_request_time_usec
= t
;
1736 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1739 log_dhcp6_client(client
, "Started in %s mode",
1740 client
->information_request
? "Information request":
1743 return client_start(client
, state
);
1746 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1749 assert_return(client
, -EINVAL
);
1750 assert_return(!client
->event
, -EBUSY
);
1753 client
->event
= sd_event_ref(event
);
1755 r
= sd_event_default(&client
->event
);
1760 client
->event_priority
= priority
;
1765 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1766 assert_return(client
, -EINVAL
);
1768 client
->event
= sd_event_unref(client
->event
);
1773 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1774 assert_return(client
, NULL
);
1776 return client
->event
;
1779 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1782 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1783 client
->timeout_resend_expire
= sd_event_source_unref(client
->timeout_resend_expire
);
1784 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
1785 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
1787 client_reset(client
);
1789 client
->fd
= safe_close(client
->fd
);
1791 sd_dhcp6_client_detach_event(client
);
1793 free(client
->req_opts
);
1795 free(client
->mudurl
);
1797 ordered_hashmap_free(client
->extra_options
);
1798 strv_free(client
->user_class
);
1799 strv_free(client
->vendor_class
);
1801 return mfree(client
);
1804 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1806 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1807 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1808 _cleanup_free_ be16_t
*req_opts
= NULL
;
1811 assert_return(ret
, -EINVAL
);
1813 req_opts
= new(be16_t
, ELEMENTSOF(default_req_opts
));
1817 for (t
= 0; t
< ELEMENTSOF(default_req_opts
); t
++)
1818 req_opts
[t
] = htobe16(default_req_opts
[t
]);
1820 client
= new(sd_dhcp6_client
, 1);
1824 *client
= (sd_dhcp6_client
) {
1826 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1827 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1829 .request
= DHCP6_REQUEST_IA_NA
,
1831 .req_opts_len
= ELEMENTSOF(default_req_opts
),
1832 .hint_pd_prefix
.iapdprefix
.lifetime_preferred
= (be32_t
) -1,
1833 .hint_pd_prefix
.iapdprefix
.lifetime_valid
= (be32_t
) -1,
1834 .req_opts
= TAKE_PTR(req_opts
),
1837 *ret
= TAKE_PTR(client
);