1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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 "device-util.h"
15 #include "dhcp-identifier.h"
16 #include "dhcp6-internal.h"
17 #include "dhcp6-lease-internal.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"
25 #include "random-util.h"
26 #include "socket-util.h"
27 #include "sort-util.h"
31 #define DHCP6_CLIENT_DONT_DESTROY(client) \
32 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
34 static int client_start_transaction(sd_dhcp6_client
*client
, DHCP6State state
);
36 int sd_dhcp6_client_set_callback(
37 sd_dhcp6_client
*client
,
38 sd_dhcp6_client_callback_t cb
,
41 assert_return(client
, -EINVAL
);
43 client
->callback
= cb
;
44 client
->userdata
= userdata
;
49 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
50 assert_return(client
, -EINVAL
);
51 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
52 assert_return(ifindex
> 0, -EINVAL
);
54 client
->ifindex
= ifindex
;
58 int sd_dhcp6_client_set_ifname(sd_dhcp6_client
*client
, const char *ifname
) {
59 assert_return(client
, -EINVAL
);
60 assert_return(ifname
, -EINVAL
);
62 if (!ifname_valid_full(ifname
, IFNAME_VALID_ALTERNATIVE
))
65 return free_and_strdup(&client
->ifname
, ifname
);
68 int sd_dhcp6_client_get_ifname(sd_dhcp6_client
*client
, const char **ret
) {
71 assert_return(client
, -EINVAL
);
73 r
= get_ifname(client
->ifindex
, &client
->ifname
);
78 *ret
= client
->ifname
;
83 int sd_dhcp6_client_set_local_address(
84 sd_dhcp6_client
*client
,
85 const struct in6_addr
*local_address
) {
87 assert_return(client
, -EINVAL
);
88 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
89 assert_return(local_address
, -EINVAL
);
90 assert_return(in6_addr_is_link_local(local_address
) > 0, -EINVAL
);
92 client
->local_address
= *local_address
;
97 int sd_dhcp6_client_set_mac(
98 sd_dhcp6_client
*client
,
103 assert_return(client
, -EINVAL
);
104 assert_return(addr
, -EINVAL
);
105 assert_return(addr_len
<= sizeof(client
->hw_addr
.bytes
), -EINVAL
);
107 /* Unlike the other setters, it is OK to set a new MAC address while the client is running,
108 * as the MAC address is used only when setting DUID or IAID. */
110 if (arp_type
== ARPHRD_ETHER
)
111 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
112 else if (arp_type
== ARPHRD_INFINIBAND
)
113 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
115 client
->arp_type
= ARPHRD_NONE
;
116 client
->hw_addr
.length
= 0;
120 client
->arp_type
= arp_type
;
121 hw_addr_set(&client
->hw_addr
, addr
, addr_len
);
126 int sd_dhcp6_client_set_prefix_delegation_hint(
127 sd_dhcp6_client
*client
,
129 const struct in6_addr
*pd_prefix
) {
131 _cleanup_free_ DHCP6Address
*prefix
= NULL
;
133 assert_return(client
, -EINVAL
);
134 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
137 /* clear previous assignments. */
138 dhcp6_ia_clear_addresses(&client
->ia_pd
);
142 assert_return(prefixlen
> 0 && prefixlen
<= 128, -EINVAL
);
144 prefix
= new(DHCP6Address
, 1);
148 *prefix
= (DHCP6Address
) {
149 .iapdprefix
.address
= *pd_prefix
,
150 .iapdprefix
.prefixlen
= prefixlen
,
153 LIST_PREPEND(addresses
, client
->ia_pd
.addresses
, TAKE_PTR(prefix
));
157 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
160 assert_return(client
, -EINVAL
);
161 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
164 /* Clear the previous assignments. */
165 ordered_set_clear(client
->vendor_options
);
169 r
= ordered_set_ensure_put(&client
->vendor_options
, &dhcp6_option_hash_ops
, v
);
173 sd_dhcp6_option_ref(v
);
178 static int client_ensure_duid(sd_dhcp6_client
*client
) {
181 if (client
->duid_len
!= 0)
184 return dhcp_identifier_set_duid_en(client
->test_mode
, &client
->duid
, &client
->duid_len
);
188 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
189 * without further modification. Otherwise, if duid_type is supported, DUID
190 * is set based on that type. Otherwise, an error is returned.
192 static int dhcp6_client_set_duid_internal(
193 sd_dhcp6_client
*client
,
200 assert_return(client
, -EINVAL
);
201 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
202 assert_return(duid_len
== 0 || duid
, -EINVAL
);
205 r
= dhcp_validate_duid_len(duid_type
, duid_len
, true);
207 r
= dhcp_validate_duid_len(duid_type
, duid_len
, false);
209 return log_dhcp6_client_errno(client
, r
, "Failed to validate length of DUID: %m");
211 log_dhcp6_client(client
, "Using DUID of type %i of incorrect length, proceeding.", duid_type
);
214 client
->duid
.type
= htobe16(duid_type
);
215 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
216 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
219 r
= dhcp_identifier_set_duid(duid_type
, &client
->hw_addr
, client
->arp_type
, llt_time
,
220 client
->test_mode
, &client
->duid
, &client
->duid_len
);
221 if (r
== -EOPNOTSUPP
)
222 return log_dhcp6_client_errno(client
, r
,
223 "Failed to set %s. MAC address is not set or "
224 "interface type is not supported.",
225 duid_type_to_string(duid_type
));
227 return log_dhcp6_client_errno(client
, r
, "Failed to set %s: %m",
228 duid_type_to_string(duid_type
));
234 int sd_dhcp6_client_set_duid(
235 sd_dhcp6_client
*client
,
239 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
242 int sd_dhcp6_client_set_duid_llt(
243 sd_dhcp6_client
*client
,
245 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
248 int sd_dhcp6_client_duid_as_string(
249 sd_dhcp6_client
*client
,
251 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
255 assert_return(client
, -EINVAL
);
256 assert_return(client
->duid_len
> 0, -ENODATA
);
257 assert_return(duid
, -EINVAL
);
259 v
= duid_type_to_string(be16toh(client
->duid
.type
));
265 r
= asprintf(&s
, "%0x", client
->duid
.type
);
270 t
= hexmem(&client
->duid
.raw
.data
, client
->duid_len
);
274 p
= strjoin(s
, ":", t
);
283 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
284 assert_return(client
, -EINVAL
);
285 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
287 client
->ia_na
.header
.id
= htobe32(iaid
);
288 client
->ia_pd
.header
.id
= htobe32(iaid
);
289 client
->iaid_set
= true;
294 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
300 if (client
->iaid_set
)
303 r
= dhcp_identifier_set_iaid(client
->dev
, &client
->hw_addr
,
304 /* legacy_unstable_byteorder = */ true,
309 client
->ia_na
.header
.id
= iaid
;
310 client
->ia_pd
.header
.id
= iaid
;
311 client
->iaid_set
= true;
316 int sd_dhcp6_client_get_iaid(sd_dhcp6_client
*client
, uint32_t *iaid
) {
317 assert_return(client
, -EINVAL
);
318 assert_return(iaid
, -EINVAL
);
320 if (!client
->iaid_set
)
323 *iaid
= be32toh(client
->ia_na
.header
.id
);
328 void dhcp6_client_set_test_mode(sd_dhcp6_client
*client
, bool test_mode
) {
331 client
->test_mode
= test_mode
;
334 int sd_dhcp6_client_set_fqdn(
335 sd_dhcp6_client
*client
,
338 assert_return(client
, -EINVAL
);
339 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
341 /* Make sure FQDN qualifies as DNS and as Linux hostname */
343 !(hostname_is_valid(fqdn
, 0) && dns_name_is_valid(fqdn
) > 0))
346 return free_and_strdup(&client
->fqdn
, fqdn
);
349 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
350 assert_return(client
, -EINVAL
);
351 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
353 client
->information_request
= enabled
;
358 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
359 assert_return(client
, -EINVAL
);
360 assert_return(enabled
, -EINVAL
);
362 *enabled
= client
->information_request
;
367 static int be16_compare_func(const be16_t
*a
, const be16_t
*b
) {
368 return CMP(be16toh(*a
), be16toh(*b
));
371 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
374 assert_return(client
, -EINVAL
);
375 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
377 if (!dhcp6_option_can_request(option
))
380 opt
= htobe16(option
);
381 if (typesafe_bsearch(&opt
, client
->req_opts
, client
->n_req_opts
, be16_compare_func
))
384 if (!GREEDY_REALLOC(client
->req_opts
, client
->n_req_opts
+ 1))
387 client
->req_opts
[client
->n_req_opts
++] = opt
;
389 /* Sort immediately to make the above binary search will work for the next time. */
390 typesafe_qsort(client
->req_opts
, client
->n_req_opts
, be16_compare_func
);
394 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client
*client
, const char *mudurl
) {
395 assert_return(client
, -EINVAL
);
396 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
397 assert_return(mudurl
, -EINVAL
);
398 assert_return(strlen(mudurl
) <= UINT8_MAX
, -EINVAL
);
399 assert_return(http_url_is_valid(mudurl
), -EINVAL
);
401 return free_and_strdup(&client
->mudurl
, mudurl
);
404 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client
*client
, char * const *user_class
) {
407 assert_return(client
, -EINVAL
);
408 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
409 assert_return(!strv_isempty(user_class
), -EINVAL
);
411 STRV_FOREACH(p
, user_class
) {
412 size_t len
= strlen(*p
);
414 if (len
> UINT16_MAX
|| len
== 0)
418 s
= strv_copy(user_class
);
422 return strv_free_and_replace(client
->user_class
, s
);
425 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client
*client
, char * const *vendor_class
) {
428 assert_return(client
, -EINVAL
);
429 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
430 assert_return(!strv_isempty(vendor_class
), -EINVAL
);
432 STRV_FOREACH(p
, vendor_class
) {
433 size_t len
= strlen(*p
);
435 if (len
> UINT16_MAX
|| len
== 0)
439 s
= strv_copy(vendor_class
);
443 return strv_free_and_replace(client
->vendor_class
, s
);
446 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
447 assert_return(client
, -EINVAL
);
448 assert_return(delegation
, -EINVAL
);
450 *delegation
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
);
455 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
456 assert_return(client
, -EINVAL
);
457 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
459 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_PD
, delegation
);
464 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
465 assert_return(client
, -EINVAL
);
466 assert_return(request
, -EINVAL
);
468 *request
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
);
473 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
474 assert_return(client
, -EINVAL
);
475 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
477 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_NA
, request
);
482 int dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
484 assert(client
->test_mode
);
486 /* This is for tests or fuzzers. */
488 client
->transaction_id
= transaction_id
& htobe32(0x00ffffff);
493 int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client
*client
, int enable
) {
494 assert_return(client
, -EINVAL
);
495 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
497 client
->rapid_commit
= enable
;
501 int sd_dhcp6_client_set_send_release(sd_dhcp6_client
*client
, int enable
) {
502 assert_return(client
, -EINVAL
);
503 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
505 client
->send_release
= enable
;
509 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
510 assert_return(client
, -EINVAL
);
516 *ret
= client
->lease
;
521 int sd_dhcp6_client_add_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
524 assert_return(client
, -EINVAL
);
525 assert_return(v
, -EINVAL
);
527 r
= ordered_hashmap_ensure_put(&client
->extra_options
, &dhcp6_option_hash_ops
, UINT_TO_PTR(v
->option
), v
);
531 sd_dhcp6_option_ref(v
);
535 static void client_set_state(sd_dhcp6_client
*client
, DHCP6State state
) {
538 if (client
->state
== state
)
541 log_dhcp6_client(client
, "State changed: %s -> %s",
542 dhcp6_state_to_string(client
->state
), dhcp6_state_to_string(state
));
544 client
->state
= state
;
547 static void client_notify(sd_dhcp6_client
*client
, int event
) {
550 if (client
->callback
)
551 client
->callback(client
, event
, client
->userdata
);
554 static void client_cleanup(sd_dhcp6_client
*client
) {
557 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
559 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
560 * even though the lease is freed below. */
561 client
->information_request_time_usec
= 0;
562 client
->information_refresh_time_usec
= 0;
564 (void) event_source_disable(client
->receive_message
);
565 (void) event_source_disable(client
->timeout_resend
);
566 (void) event_source_disable(client
->timeout_expire
);
567 (void) event_source_disable(client
->timeout_t1
);
568 (void) event_source_disable(client
->timeout_t2
);
570 client_set_state(client
, DHCP6_STATE_STOPPED
);
573 static void client_stop(sd_dhcp6_client
*client
, int error
) {
574 DHCP6_CLIENT_DONT_DESTROY(client
);
578 client_notify(client
, error
);
580 client_cleanup(client
);
583 static int client_append_common_options_in_managed_mode(
584 sd_dhcp6_client
*client
,
587 const DHCP6IA
*ia_na
,
588 const DHCP6IA
*ia_pd
) {
593 assert(IN_SET(client
->state
,
594 DHCP6_STATE_SOLICITATION
,
598 DHCP6_STATE_STOPPING
));
603 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
) && ia_na
) {
604 r
= dhcp6_option_append_ia(buf
, offset
, ia_na
);
609 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
) && ia_pd
) {
610 r
= dhcp6_option_append_ia(buf
, offset
, ia_pd
);
615 if (client
->state
!= DHCP6_STATE_STOPPING
) {
616 r
= dhcp6_option_append_fqdn(buf
, offset
, client
->fqdn
);
621 r
= dhcp6_option_append_user_class(buf
, offset
, client
->user_class
);
625 r
= dhcp6_option_append_vendor_class(buf
, offset
, client
->vendor_class
);
629 r
= dhcp6_option_append_vendor_option(buf
, offset
, client
->vendor_options
);
636 static DHCP6MessageType
client_message_type_from_state(sd_dhcp6_client
*client
) {
639 switch (client
->state
) {
640 case DHCP6_STATE_INFORMATION_REQUEST
:
641 return DHCP6_MESSAGE_INFORMATION_REQUEST
;
642 case DHCP6_STATE_SOLICITATION
:
643 return DHCP6_MESSAGE_SOLICIT
;
644 case DHCP6_STATE_REQUEST
:
645 return DHCP6_MESSAGE_REQUEST
;
646 case DHCP6_STATE_RENEW
:
647 return DHCP6_MESSAGE_RENEW
;
648 case DHCP6_STATE_REBIND
:
649 return DHCP6_MESSAGE_REBIND
;
650 case DHCP6_STATE_STOPPING
:
651 return DHCP6_MESSAGE_RELEASE
;
653 assert_not_reached();
657 static int client_append_oro(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
658 _cleanup_free_ be16_t
*p
= NULL
;
667 switch (client
->state
) {
668 case DHCP6_STATE_INFORMATION_REQUEST
:
669 n
= client
->n_req_opts
;
670 p
= new(be16_t
, n
+ 2);
674 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
675 p
[n
++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
); /* RFC 8415 section 21.23 */
676 p
[n
++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT
); /* RFC 8415 section 21.25 */
678 typesafe_qsort(p
, n
, be16_compare_func
);
682 case DHCP6_STATE_SOLICITATION
:
683 n
= client
->n_req_opts
;
684 p
= new(be16_t
, n
+ 1);
688 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
689 p
[n
++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT
); /* RFC 8415 section 21.24 */
691 typesafe_qsort(p
, n
, be16_compare_func
);
695 case DHCP6_STATE_STOPPING
:
699 n
= client
->n_req_opts
;
700 req_opts
= client
->req_opts
;
706 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_ORO
, n
* sizeof(be16_t
), req_opts
);
709 static int client_append_mudurl(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
718 if (client
->state
== DHCP6_STATE_STOPPING
)
721 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_MUD_URL_V6
,
722 strlen(client
->mudurl
), client
->mudurl
);
725 int dhcp6_client_send_message(sd_dhcp6_client
*client
) {
726 _cleanup_free_
uint8_t *buf
= NULL
;
727 struct in6_addr all_servers
=
728 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
729 struct sd_dhcp6_option
*j
;
730 usec_t elapsed_usec
, time_now
;
732 DHCP6Message
*message
;
737 assert(client
->event
);
739 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &time_now
);
743 if (!GREEDY_REALLOC0(buf
, offsetof(DHCP6Message
, options
)))
746 message
= (DHCP6Message
*) buf
;
747 message
->transaction_id
= client
->transaction_id
;
748 message
->type
= client_message_type_from_state(client
);
749 offset
= offsetof(DHCP6Message
, options
);
751 switch (client
->state
) {
752 case DHCP6_STATE_INFORMATION_REQUEST
:
755 case DHCP6_STATE_SOLICITATION
:
756 if (client
->rapid_commit
) {
757 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
762 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
763 &client
->ia_na
, &client
->ia_pd
);
768 case DHCP6_STATE_REQUEST
:
769 case DHCP6_STATE_RENEW
:
770 case DHCP6_STATE_STOPPING
:
771 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_SERVERID
,
772 client
->lease
->serverid_len
,
773 client
->lease
->serverid
);
778 case DHCP6_STATE_REBIND
:
780 assert(client
->lease
);
782 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
783 client
->lease
->ia_na
, client
->lease
->ia_pd
);
788 case DHCP6_STATE_BOUND
:
789 case DHCP6_STATE_STOPPED
:
791 assert_not_reached();
794 r
= client_append_mudurl(client
, &buf
, &offset
);
798 r
= client_append_oro(client
, &buf
, &offset
);
802 assert(client
->duid_len
> 0);
803 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_CLIENTID
,
804 client
->duid_len
, &client
->duid
);
808 ORDERED_HASHMAP_FOREACH(j
, client
->extra_options
) {
809 r
= dhcp6_option_append(&buf
, &offset
, j
->option
, j
->length
, j
->data
);
814 /* RFC 8415 Section 21.9.
815 * A client MUST include an Elapsed Time option in messages to indicate how long the client has
816 * been trying to complete a DHCP message exchange. */
817 elapsed_usec
= MIN(usec_sub_unsigned(time_now
, client
->transaction_start
) / USEC_PER_MSEC
/ 10, (usec_t
) UINT16_MAX
);
818 elapsed_time
= htobe16(elapsed_usec
);
819 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_ELAPSED_TIME
, sizeof(elapsed_time
), &elapsed_time
);
823 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, buf
, offset
);
827 log_dhcp6_client(client
, "Sent %s",
828 dhcp6_message_type_to_string(client_message_type_from_state(client
)));
832 static usec_t
client_timeout_compute_random(usec_t val
) {
833 return usec_sub_unsigned(val
, random_u64_range(val
/ 10));
836 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
837 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
838 usec_t init_retransmit_time
, max_retransmit_time
;
841 assert(client
->event
);
843 switch (client
->state
) {
844 case DHCP6_STATE_INFORMATION_REQUEST
:
845 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
846 max_retransmit_time
= DHCP6_INF_MAX_RT
;
849 case DHCP6_STATE_SOLICITATION
:
851 if (client
->retransmit_count
> 0 && client
->lease
) {
852 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
856 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
857 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
860 case DHCP6_STATE_REQUEST
:
862 if (client
->retransmit_count
>= DHCP6_REQ_MAX_RC
) {
863 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
867 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
868 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
871 case DHCP6_STATE_RENEW
:
872 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
873 max_retransmit_time
= DHCP6_REN_MAX_RT
;
875 /* RFC 3315, section 18.1.3. says max retransmit duration will
876 be the remaining time until T2. Instead of setting MRD,
877 wait for T2 to trigger with the same end result */
880 case DHCP6_STATE_REBIND
:
881 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
882 max_retransmit_time
= DHCP6_REB_MAX_RT
;
884 /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */
887 case DHCP6_STATE_STOPPED
:
888 case DHCP6_STATE_STOPPING
:
889 case DHCP6_STATE_BOUND
:
891 assert_not_reached();
894 r
= dhcp6_client_send_message(client
);
896 client
->retransmit_count
++;
898 if (client
->retransmit_time
== 0) {
899 client
->retransmit_time
= client_timeout_compute_random(init_retransmit_time
);
901 if (client
->state
== DHCP6_STATE_SOLICITATION
)
902 client
->retransmit_time
+= init_retransmit_time
/ 10;
904 } else if (client
->retransmit_time
> max_retransmit_time
/ 2)
905 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
907 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
909 log_dhcp6_client(client
, "Next retransmission in %s",
910 FORMAT_TIMESPAN(client
->retransmit_time
, USEC_PER_SEC
));
912 r
= event_reset_time_relative(client
->event
, &client
->timeout_resend
,
914 client
->retransmit_time
, 10 * USEC_PER_MSEC
,
915 client_timeout_resend
, client
,
916 client
->event_priority
, "dhcp6-resend-timer", true);
918 client_stop(client
, r
);
923 static int client_start_transaction(sd_dhcp6_client
*client
, DHCP6State state
) {
927 assert(client
->event
);
930 case DHCP6_STATE_INFORMATION_REQUEST
:
931 case DHCP6_STATE_SOLICITATION
:
932 assert(client
->state
== DHCP6_STATE_STOPPED
);
934 case DHCP6_STATE_REQUEST
:
935 assert(client
->state
== DHCP6_STATE_SOLICITATION
);
937 case DHCP6_STATE_RENEW
:
938 assert(client
->state
== DHCP6_STATE_BOUND
);
940 case DHCP6_STATE_REBIND
:
941 assert(IN_SET(client
->state
, DHCP6_STATE_BOUND
, DHCP6_STATE_RENEW
));
943 case DHCP6_STATE_STOPPED
:
944 case DHCP6_STATE_STOPPING
:
945 case DHCP6_STATE_BOUND
:
947 assert_not_reached();
950 client_set_state(client
, state
);
952 client
->retransmit_time
= 0;
953 client
->retransmit_count
= 0;
954 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
956 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &client
->transaction_start
);
960 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
963 client_timeout_resend
, client
,
964 client
->event_priority
, "dhcp6-resend-timeout", true);
968 r
= sd_event_source_set_enabled(client
->receive_message
, SD_EVENT_ON
);
975 client_stop(client
, r
);
979 static int client_timeout_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
980 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
981 DHCP6_CLIENT_DONT_DESTROY(client
);
984 (void) event_source_disable(client
->timeout_expire
);
985 (void) event_source_disable(client
->timeout_t2
);
986 (void) event_source_disable(client
->timeout_t1
);
988 state
= client
->state
;
990 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
992 /* RFC 3315, section 18.1.4., says that "...the client may choose to
993 use a Solicit message to locate a new DHCP server..." */
994 if (state
== DHCP6_STATE_REBIND
)
995 (void) client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1000 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1001 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1003 (void) event_source_disable(client
->timeout_t2
);
1004 (void) event_source_disable(client
->timeout_t1
);
1006 log_dhcp6_client(client
, "Timeout T2");
1008 (void) client_start_transaction(client
, DHCP6_STATE_REBIND
);
1013 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1014 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1016 (void) event_source_disable(client
->timeout_t1
);
1018 log_dhcp6_client(client
, "Timeout T1");
1020 (void) client_start_transaction(client
, DHCP6_STATE_RENEW
);
1025 static int client_enter_bound_state(sd_dhcp6_client
*client
) {
1026 usec_t lifetime_t1
, lifetime_t2
, lifetime_valid
;
1030 assert(client
->lease
);
1031 assert(IN_SET(client
->state
,
1032 DHCP6_STATE_SOLICITATION
,
1033 DHCP6_STATE_REQUEST
,
1035 DHCP6_STATE_REBIND
));
1037 (void) event_source_disable(client
->receive_message
);
1038 (void) event_source_disable(client
->timeout_resend
);
1040 r
= dhcp6_lease_get_lifetime(client
->lease
, &lifetime_t1
, &lifetime_t2
, &lifetime_valid
);
1044 lifetime_t2
= client_timeout_compute_random(lifetime_t2
);
1045 lifetime_t1
= client_timeout_compute_random(MIN(lifetime_t1
, lifetime_t2
));
1047 if (lifetime_t1
== USEC_INFINITY
) {
1048 log_dhcp6_client(client
, "Infinite T1");
1049 event_source_disable(client
->timeout_t1
);
1051 log_dhcp6_client(client
, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1
, USEC_PER_SEC
));
1052 r
= event_reset_time_relative(client
->event
, &client
->timeout_t1
,
1054 lifetime_t1
, 10 * USEC_PER_SEC
,
1055 client_timeout_t1
, client
,
1056 client
->event_priority
, "dhcp6-t1-timeout", true);
1061 if (lifetime_t2
== USEC_INFINITY
) {
1062 log_dhcp6_client(client
, "Infinite T2");
1063 event_source_disable(client
->timeout_t2
);
1065 log_dhcp6_client(client
, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2
, USEC_PER_SEC
));
1066 r
= event_reset_time_relative(client
->event
, &client
->timeout_t2
,
1068 lifetime_t2
, 10 * USEC_PER_SEC
,
1069 client_timeout_t2
, client
,
1070 client
->event_priority
, "dhcp6-t2-timeout", true);
1075 if (lifetime_valid
== USEC_INFINITY
) {
1076 log_dhcp6_client(client
, "Infinite valid lifetime");
1077 event_source_disable(client
->timeout_expire
);
1079 log_dhcp6_client(client
, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid
, USEC_PER_SEC
));
1081 r
= event_reset_time_relative(client
->event
, &client
->timeout_expire
,
1083 lifetime_valid
, USEC_PER_SEC
,
1084 client_timeout_expire
, client
,
1085 client
->event_priority
, "dhcp6-lease-expire", true);
1090 client_set_state(client
, DHCP6_STATE_BOUND
);
1091 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1095 client_stop(client
, r
);
1099 static int log_invalid_message_type(sd_dhcp6_client
*client
, const DHCP6Message
*message
) {
1100 const char *type_str
;
1105 type_str
= dhcp6_message_type_to_string(message
->type
);
1107 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1108 "Received unexpected %s message, ignoring.", type_str
);
1110 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1111 "Received unsupported message type %u, ignoring.", message
->type
);
1114 static int client_process_information(
1115 sd_dhcp6_client
*client
,
1116 DHCP6Message
*message
,
1118 const triple_timestamp
*timestamp
,
1119 const struct in6_addr
*server_address
) {
1121 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1127 if (message
->type
!= DHCP6_MESSAGE_REPLY
)
1128 return log_invalid_message_type(client
, message
);
1130 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1132 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1134 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1136 sd_dhcp6_lease_unref(client
->lease
);
1137 client
->lease
= TAKE_PTR(lease
);
1139 /* Do not call client_stop() here, as it frees the acquired lease. */
1140 (void) event_source_disable(client
->receive_message
);
1141 (void) event_source_disable(client
->timeout_resend
);
1142 client_set_state(client
, DHCP6_STATE_STOPPED
);
1144 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1148 static int client_process_reply(
1149 sd_dhcp6_client
*client
,
1150 DHCP6Message
*message
,
1152 const triple_timestamp
*timestamp
,
1153 const struct in6_addr
*server_address
) {
1155 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1161 if (message
->type
!= DHCP6_MESSAGE_REPLY
)
1162 return log_invalid_message_type(client
, message
);
1164 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1165 if (r
== -EADDRNOTAVAIL
) {
1167 /* If NoBinding status code is received, we cannot request the address anymore.
1168 * Let's restart transaction from the beginning. */
1170 if (client
->state
== DHCP6_STATE_REQUEST
)
1171 /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
1172 client_cleanup(client
);
1174 /* We need to notify the previous lease was expired. */
1175 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
1177 return client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1180 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1182 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1184 sd_dhcp6_lease_unref(client
->lease
);
1185 client
->lease
= TAKE_PTR(lease
);
1187 return client_enter_bound_state(client
);
1190 static int client_process_advertise_or_rapid_commit_reply(
1191 sd_dhcp6_client
*client
,
1192 DHCP6Message
*message
,
1194 const triple_timestamp
*timestamp
,
1195 const struct in6_addr
*server_address
) {
1197 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1198 uint8_t pref_advertise
, pref_lease
= 0;
1204 if (!IN_SET(message
->type
, DHCP6_MESSAGE_ADVERTISE
, DHCP6_MESSAGE_REPLY
))
1205 return log_invalid_message_type(client
, message
);
1207 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1209 return log_dhcp6_client_errno(client
, r
, "Failed to process received %s message, ignoring: %m",
1210 dhcp6_message_type_to_string(message
->type
));
1212 if (message
->type
== DHCP6_MESSAGE_REPLY
) {
1215 if (!client
->rapid_commit
)
1216 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1217 "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
1219 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1224 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1225 "Received reply message without rapid commit flag, ignoring.");
1227 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1229 sd_dhcp6_lease_unref(client
->lease
);
1230 client
->lease
= TAKE_PTR(lease
);
1232 return client_enter_bound_state(client
);
1235 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1239 if (client
->lease
) {
1240 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1245 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1247 if (!client
->lease
|| pref_advertise
> pref_lease
) {
1248 /* If this is the first advertise message or has higher preference, then save the lease. */
1249 sd_dhcp6_lease_unref(client
->lease
);
1250 client
->lease
= TAKE_PTR(lease
);
1253 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1254 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
1259 static int client_receive_message(
1265 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1266 DHCP6_CLIENT_DONT_DESTROY(client
);
1267 /* This needs to be initialized with zero. See #20741. */
1268 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL
) control
= {};
1270 union sockaddr_union sa
= {};
1271 struct msghdr msg
= {
1273 .msg_namelen
= sizeof(sa
),
1276 .msg_control
= &control
,
1277 .msg_controllen
= sizeof(control
),
1279 struct cmsghdr
*cmsg
;
1280 triple_timestamp t
= {};
1281 _cleanup_free_ DHCP6Message
*message
= NULL
;
1282 struct in6_addr
*server_address
= NULL
;
1283 ssize_t buflen
, len
;
1285 buflen
= next_datagram_size_fd(fd
);
1287 if (ERRNO_IS_TRANSIENT(buflen
) || ERRNO_IS_DISCONNECT(buflen
))
1290 log_dhcp6_client_errno(client
, buflen
, "Failed to determine datagram size to read, ignoring: %m");
1294 message
= malloc(buflen
);
1298 iov
= IOVEC_MAKE(message
, buflen
);
1300 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
1302 if (ERRNO_IS_TRANSIENT(len
) || ERRNO_IS_DISCONNECT(len
))
1305 log_dhcp6_client_errno(client
, len
, "Could not receive message from UDP socket, ignoring: %m");
1308 if ((size_t) len
< sizeof(DHCP6Message
)) {
1309 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1313 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1314 if (msg
.msg_namelen
> 0) {
1315 if (msg
.msg_namelen
!= sizeof(struct sockaddr_in6
) || sa
.in6
.sin6_family
!= AF_INET6
) {
1316 log_dhcp6_client(client
, "Received message from invalid source, ignoring.");
1320 server_address
= &sa
.in6
.sin6_addr
;
1323 CMSG_FOREACH(cmsg
, &msg
) {
1324 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
1325 cmsg
->cmsg_type
== SO_TIMESTAMP
&&
1326 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct timeval
)))
1327 triple_timestamp_from_realtime(&t
, timeval_load(CMSG_TYPED_DATA(cmsg
, struct timeval
)));
1330 if (client
->transaction_id
!= (message
->transaction_id
& htobe32(0x00ffffff)))
1333 switch (client
->state
) {
1334 case DHCP6_STATE_INFORMATION_REQUEST
:
1335 if (client_process_information(client
, message
, len
, &t
, server_address
) < 0)
1339 case DHCP6_STATE_SOLICITATION
:
1340 if (client_process_advertise_or_rapid_commit_reply(client
, message
, len
, &t
, server_address
) < 0)
1344 case DHCP6_STATE_REQUEST
:
1345 case DHCP6_STATE_RENEW
:
1346 case DHCP6_STATE_REBIND
:
1347 if (client_process_reply(client
, message
, len
, &t
, server_address
) < 0)
1351 case DHCP6_STATE_BOUND
:
1352 case DHCP6_STATE_STOPPED
:
1353 case DHCP6_STATE_STOPPING
:
1355 assert_not_reached();
1361 static int client_send_release(sd_dhcp6_client
*client
) {
1362 sd_dhcp6_lease
*lease
;
1366 if (!client
->send_release
)
1369 if (sd_dhcp6_client_get_lease(client
, &lease
) < 0)
1372 if (!lease
->ia_na
&& !lease
->ia_pd
)
1375 client_set_state(client
, DHCP6_STATE_STOPPING
);
1376 return dhcp6_client_send_message(client
);
1379 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1385 /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
1386 * engine is about to release its UDP socket unconditionally. */
1387 r
= client_send_release(client
);
1389 log_dhcp6_client_errno(client
, r
,
1390 "Failed to send DHCP6 release message, ignoring: %m");
1392 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1394 client
->receive_message
= sd_event_source_unref(client
->receive_message
);
1395 client
->fd
= safe_close(client
->fd
);
1400 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1401 assert_return(client
, -EINVAL
);
1403 return client
->state
!= DHCP6_STATE_STOPPED
;
1406 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1407 DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1410 assert_return(client
, -EINVAL
);
1411 assert_return(client
->event
, -EINVAL
);
1412 assert_return(client
->ifindex
> 0, -EINVAL
);
1413 assert_return(in6_addr_is_link_local(&client
->local_address
) > 0, -EINVAL
);
1414 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1415 assert_return(client
->information_request
|| client
->request_ia
!= 0, -EINVAL
);
1417 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1418 * request may be stored. */
1419 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
1421 r
= client_ensure_iaid(client
);
1425 r
= client_ensure_duid(client
);
1429 if (client
->fd
< 0) {
1430 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1432 return log_dhcp6_client_errno(client
, r
,
1433 "Failed to bind to UDP socket at address %s: %m",
1434 IN6_ADDR_TO_STRING(&client
->local_address
));
1439 if (!client
->receive_message
) {
1440 _cleanup_(sd_event_source_disable_unrefp
) sd_event_source
*s
= NULL
;
1442 r
= sd_event_add_io(client
->event
, &s
, client
->fd
, EPOLLIN
, client_receive_message
, client
);
1446 r
= sd_event_source_set_priority(s
, client
->event_priority
);
1450 r
= sd_event_source_set_description(s
, "dhcp6-receive-message");
1454 client
->receive_message
= TAKE_PTR(s
);
1457 if (client
->information_request
) {
1458 usec_t t
= now(CLOCK_MONOTONIC
);
1460 if (t
< usec_add(client
->information_request_time_usec
, client
->information_refresh_time_usec
))
1463 client
->information_request_time_usec
= t
;
1464 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1467 log_dhcp6_client(client
, "Starting in %s mode",
1468 client
->information_request
? "Information request" : "Solicit");
1470 return client_start_transaction(client
, state
);
1473 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1476 assert_return(client
, -EINVAL
);
1477 assert_return(!client
->event
, -EBUSY
);
1478 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1481 client
->event
= sd_event_ref(event
);
1483 r
= sd_event_default(&client
->event
);
1488 client
->event_priority
= priority
;
1493 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1494 assert_return(client
, -EINVAL
);
1495 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1497 client
->event
= sd_event_unref(client
->event
);
1502 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1503 assert_return(client
, NULL
);
1505 return client
->event
;
1508 int sd_dhcp6_client_attach_device(sd_dhcp6_client
*client
, sd_device
*dev
) {
1509 assert_return(client
, -EINVAL
);
1511 return device_unref_and_replace(client
->dev
, dev
);
1514 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1518 sd_dhcp6_lease_unref(client
->lease
);
1520 sd_event_source_disable_unref(client
->receive_message
);
1521 sd_event_source_disable_unref(client
->timeout_resend
);
1522 sd_event_source_disable_unref(client
->timeout_expire
);
1523 sd_event_source_disable_unref(client
->timeout_t1
);
1524 sd_event_source_disable_unref(client
->timeout_t2
);
1525 sd_event_unref(client
->event
);
1527 client
->fd
= safe_close(client
->fd
);
1529 sd_device_unref(client
->dev
);
1531 free(client
->req_opts
);
1533 free(client
->mudurl
);
1534 dhcp6_ia_clear_addresses(&client
->ia_pd
);
1535 ordered_hashmap_free(client
->extra_options
);
1536 ordered_set_free(client
->vendor_options
);
1537 strv_free(client
->user_class
);
1538 strv_free(client
->vendor_class
);
1539 free(client
->ifname
);
1541 return mfree(client
);
1544 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1546 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1547 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1549 assert_return(ret
, -EINVAL
);
1551 client
= new(sd_dhcp6_client
, 1);
1555 *client
= (sd_dhcp6_client
) {
1557 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1558 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1560 .request_ia
= DHCP6_REQUEST_IA_NA
| DHCP6_REQUEST_IA_PD
,
1562 .rapid_commit
= true,
1565 *ret
= TAKE_PTR(client
);