1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
6 #include <linux/if_arp.h>
7 #include <linux/if_infiniband.h>
9 #include "sd-dhcp6-client.h"
10 #include "sd-dhcp6-option.h"
12 #include "alloc-util.h"
13 #include "device-util.h"
14 #include "dhcp-duid-internal.h"
15 #include "dhcp6-client-internal.h"
16 #include "dhcp6-internal.h"
17 #include "dhcp6-lease-internal.h"
18 #include "dns-domain.h"
19 #include "errno-util.h"
20 #include "event-util.h"
22 #include "hostname-util.h"
23 #include "in-addr-util.h"
24 #include "iovec-util.h"
25 #include "ordered-set.h"
26 #include "random-util.h"
27 #include "socket-util.h"
28 #include "sort-util.h"
29 #include "string-util.h"
33 #define DHCP6_CLIENT_DONT_DESTROY(client) \
34 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
36 static int client_start_transaction(sd_dhcp6_client
*client
, DHCP6State state
);
38 int sd_dhcp6_client_set_callback(
39 sd_dhcp6_client
*client
,
40 sd_dhcp6_client_callback_t cb
,
43 assert_return(client
, -EINVAL
);
45 client
->callback
= cb
;
46 client
->userdata
= userdata
;
51 int dhcp6_client_set_state_callback(
52 sd_dhcp6_client
*client
,
53 sd_dhcp6_client_callback_t cb
,
56 assert_return(client
, -EINVAL
);
58 client
->state_callback
= cb
;
59 client
->state_userdata
= userdata
;
64 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
65 assert_return(client
, -EINVAL
);
66 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
67 assert_return(ifindex
> 0, -EINVAL
);
69 client
->ifindex
= ifindex
;
73 int sd_dhcp6_client_set_ifname(sd_dhcp6_client
*client
, const char *ifname
) {
74 assert_return(client
, -EINVAL
);
75 assert_return(ifname
, -EINVAL
);
77 if (!ifname_valid_full(ifname
, IFNAME_VALID_ALTERNATIVE
))
80 return free_and_strdup(&client
->ifname
, ifname
);
83 int sd_dhcp6_client_get_ifname(sd_dhcp6_client
*client
, const char **ret
) {
86 assert_return(client
, -EINVAL
);
88 r
= get_ifname(client
->ifindex
, &client
->ifname
);
93 *ret
= client
->ifname
;
98 int sd_dhcp6_client_set_local_address(
99 sd_dhcp6_client
*client
,
100 const struct in6_addr
*local_address
) {
102 assert_return(client
, -EINVAL
);
103 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
104 assert_return(local_address
, -EINVAL
);
105 assert_return(in6_addr_is_link_local(local_address
) > 0, -EINVAL
);
107 client
->local_address
= *local_address
;
112 int sd_dhcp6_client_set_mac(
113 sd_dhcp6_client
*client
,
118 assert_return(client
, -EINVAL
);
119 assert_return(addr
, -EINVAL
);
120 assert_return(addr_len
<= sizeof(client
->hw_addr
.bytes
), -EINVAL
);
122 /* Unlike the other setters, it is OK to set a new MAC address while the client is running,
123 * as the MAC address is used only when setting DUID or IAID. */
125 if (arp_type
== ARPHRD_ETHER
)
126 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
127 else if (arp_type
== ARPHRD_INFINIBAND
)
128 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
130 client
->arp_type
= ARPHRD_NONE
;
131 client
->hw_addr
.length
= 0;
135 client
->arp_type
= arp_type
;
136 hw_addr_set(&client
->hw_addr
, addr
, addr_len
);
141 int sd_dhcp6_client_set_prefix_delegation_hint(
142 sd_dhcp6_client
*client
,
144 const struct in6_addr
*pd_prefix
) {
146 _cleanup_free_ DHCP6Address
*prefix
= NULL
;
148 assert_return(client
, -EINVAL
);
149 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
152 /* clear previous assignments. */
153 dhcp6_ia_clear_addresses(&client
->ia_pd
);
157 assert_return(prefixlen
> 0 && prefixlen
<= 128, -EINVAL
);
159 prefix
= new(DHCP6Address
, 1);
163 *prefix
= (DHCP6Address
) {
164 .iapdprefix
.address
= *pd_prefix
,
165 .iapdprefix
.prefixlen
= prefixlen
,
168 LIST_PREPEND(addresses
, client
->ia_pd
.addresses
, TAKE_PTR(prefix
));
172 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
175 assert_return(client
, -EINVAL
);
176 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
179 /* Clear the previous assignments. */
180 ordered_set_clear(client
->vendor_options
);
184 r
= ordered_set_ensure_put(&client
->vendor_options
, &dhcp6_option_hash_ops
, v
);
188 sd_dhcp6_option_ref(v
);
193 static int client_ensure_duid(sd_dhcp6_client
*client
) {
196 if (sd_dhcp_duid_is_set(&client
->duid
))
199 return sd_dhcp6_client_set_duid_en(client
);
203 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
204 * without further modification. Otherwise, if duid_type is supported, DUID
205 * is set based on that type. Otherwise, an error is returned.
207 int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client
*client
, uint64_t llt_time
) {
210 assert_return(client
, -EINVAL
);
211 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
213 r
= sd_dhcp_duid_set_llt(&client
->duid
, client
->hw_addr
.bytes
, client
->hw_addr
.length
, client
->arp_type
, llt_time
);
215 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LLT: %m");
220 int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client
*client
) {
223 assert_return(client
, -EINVAL
);
224 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
226 r
= sd_dhcp_duid_set_ll(&client
->duid
, client
->hw_addr
.bytes
, client
->hw_addr
.length
, client
->arp_type
);
228 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LL: %m");
233 int sd_dhcp6_client_set_duid_en(sd_dhcp6_client
*client
) {
236 assert_return(client
, -EINVAL
);
237 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
239 r
= sd_dhcp_duid_set_en(&client
->duid
);
241 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-EN: %m");
246 int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client
*client
) {
249 assert_return(client
, -EINVAL
);
250 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
252 r
= sd_dhcp_duid_set_uuid(&client
->duid
);
254 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-UUID: %m");
259 int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client
*client
, uint16_t duid_type
, const uint8_t *duid
, size_t duid_len
) {
262 assert_return(client
, -EINVAL
);
263 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
264 assert_return(duid
|| duid_len
== 0, -EINVAL
);
266 r
= sd_dhcp_duid_set(&client
->duid
, duid_type
, duid
, duid_len
);
268 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID: %m");
273 int sd_dhcp6_client_set_duid(sd_dhcp6_client
*client
, const sd_dhcp_duid
*duid
) {
274 assert_return(client
, -EINVAL
);
275 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
276 assert_return(sd_dhcp_duid_is_set(duid
), -EINVAL
);
278 client
->duid
= *duid
;
282 int sd_dhcp6_client_get_duid(sd_dhcp6_client
*client
, const sd_dhcp_duid
**ret
) {
283 assert_return(client
, -EINVAL
);
284 assert_return(ret
, -EINVAL
);
286 if (!sd_dhcp_duid_is_set(&client
->duid
))
289 *ret
= &client
->duid
;
293 int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client
*client
, char **ret
) {
294 assert_return(client
, -EINVAL
);
295 assert_return(ret
, -EINVAL
);
297 if (!sd_dhcp_duid_is_set(&client
->duid
))
300 return sd_dhcp_duid_to_string(&client
->duid
, ret
);
303 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
304 assert_return(client
, -EINVAL
);
305 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
307 client
->ia_na
.header
.id
= htobe32(iaid
);
308 client
->ia_pd
.header
.id
= htobe32(iaid
);
309 client
->iaid_set
= true;
314 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
320 if (client
->iaid_set
)
323 r
= dhcp_identifier_set_iaid(client
->dev
, &client
->hw_addr
,
324 /* legacy_unstable_byteorder = */ true,
329 client
->ia_na
.header
.id
= iaid
;
330 client
->ia_pd
.header
.id
= iaid
;
331 client
->iaid_set
= true;
336 int sd_dhcp6_client_get_iaid(sd_dhcp6_client
*client
, uint32_t *iaid
) {
337 assert_return(client
, -EINVAL
);
338 assert_return(iaid
, -EINVAL
);
340 if (!client
->iaid_set
)
343 *iaid
= be32toh(client
->ia_na
.header
.id
);
348 int sd_dhcp6_client_set_fqdn(
349 sd_dhcp6_client
*client
,
352 assert_return(client
, -EINVAL
);
353 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
355 /* Make sure FQDN qualifies as DNS and as Linux hostname */
357 !(hostname_is_valid(fqdn
, 0) && dns_name_is_valid(fqdn
) > 0))
360 return free_and_strdup(&client
->fqdn
, fqdn
);
363 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
364 assert_return(client
, -EINVAL
);
365 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
367 client
->information_request
= enabled
;
372 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
373 assert_return(client
, -EINVAL
);
374 assert_return(enabled
, -EINVAL
);
376 *enabled
= client
->information_request
;
381 static int be16_compare_func(const be16_t
*a
, const be16_t
*b
) {
382 return CMP(be16toh(*a
), be16toh(*b
));
385 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
388 assert_return(client
, -EINVAL
);
389 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
391 if (!dhcp6_option_can_request(option
))
394 opt
= htobe16(option
);
395 if (typesafe_bsearch(&opt
, client
->req_opts
, client
->n_req_opts
, be16_compare_func
))
398 if (!GREEDY_REALLOC(client
->req_opts
, client
->n_req_opts
+ 1))
401 client
->req_opts
[client
->n_req_opts
++] = opt
;
403 /* Sort immediately to make the above binary search will work for the next time. */
404 typesafe_qsort(client
->req_opts
, client
->n_req_opts
, be16_compare_func
);
408 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client
*client
, const char *mudurl
) {
409 assert_return(client
, -EINVAL
);
410 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
411 assert_return(mudurl
, -EINVAL
);
412 assert_return(strlen(mudurl
) <= UINT8_MAX
, -EINVAL
);
413 assert_return(http_url_is_valid(mudurl
), -EINVAL
);
415 return free_and_strdup(&client
->mudurl
, mudurl
);
418 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client
*client
, char * const *user_class
) {
421 assert_return(client
, -EINVAL
);
422 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
423 assert_return(!strv_isempty(user_class
), -EINVAL
);
425 STRV_FOREACH(p
, user_class
) {
426 size_t len
= strlen(*p
);
428 if (len
> UINT16_MAX
|| len
== 0)
432 s
= strv_copy(user_class
);
436 return strv_free_and_replace(client
->user_class
, s
);
439 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client
*client
, char * const *vendor_class
) {
442 assert_return(client
, -EINVAL
);
443 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
444 assert_return(!strv_isempty(vendor_class
), -EINVAL
);
446 STRV_FOREACH(p
, vendor_class
) {
447 size_t len
= strlen(*p
);
449 if (len
> UINT16_MAX
|| len
== 0)
453 s
= strv_copy(vendor_class
);
457 return strv_free_and_replace(client
->vendor_class
, s
);
460 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
461 assert_return(client
, -EINVAL
);
462 assert_return(delegation
, -EINVAL
);
464 *delegation
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
);
469 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
470 assert_return(client
, -EINVAL
);
471 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
473 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_PD
, delegation
);
478 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
479 assert_return(client
, -EINVAL
);
480 assert_return(request
, -EINVAL
);
482 *request
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
);
487 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
488 assert_return(client
, -EINVAL
);
489 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
491 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_NA
, request
);
496 int dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
498 assert_se(network_test_mode_enabled());
500 /* This is for tests or fuzzers. */
502 client
->transaction_id
= transaction_id
& htobe32(0x00ffffff);
507 int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client
*client
, int enable
) {
508 assert_return(client
, -EINVAL
);
509 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
511 client
->rapid_commit
= enable
;
515 int sd_dhcp6_client_set_send_release(sd_dhcp6_client
*client
, int enable
) {
516 assert_return(client
, -EINVAL
);
518 client
->send_release
= enable
;
522 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
523 assert_return(client
, -EINVAL
);
529 *ret
= client
->lease
;
534 int sd_dhcp6_client_add_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
537 assert_return(client
, -EINVAL
);
538 assert_return(v
, -EINVAL
);
540 r
= ordered_hashmap_ensure_put(&client
->extra_options
, &dhcp6_option_hash_ops
, UINT_TO_PTR(v
->option
), v
);
544 sd_dhcp6_option_ref(v
);
548 static void client_set_state(sd_dhcp6_client
*client
, DHCP6State state
) {
551 if (client
->state
== state
)
554 log_dhcp6_client(client
, "State changed: %s -> %s",
555 dhcp6_state_to_string(client
->state
), dhcp6_state_to_string(state
));
557 client
->state
= state
;
559 if (client
->state_callback
)
560 client
->state_callback(client
, state
, client
->state_userdata
);
563 int dhcp6_client_get_state(sd_dhcp6_client
*client
) {
564 assert_return(client
, -EINVAL
);
566 return client
->state
;
569 static void client_notify(sd_dhcp6_client
*client
, int event
) {
572 if (client
->callback
)
573 client
->callback(client
, event
, client
->userdata
);
576 static void client_cleanup(sd_dhcp6_client
*client
) {
579 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
581 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
582 * even though the lease is freed below. */
583 client
->information_request_time_usec
= 0;
584 client
->information_refresh_time_usec
= 0;
586 (void) event_source_disable(client
->receive_message
);
587 (void) event_source_disable(client
->timeout_resend
);
588 (void) event_source_disable(client
->timeout_expire
);
589 (void) event_source_disable(client
->timeout_t1
);
590 (void) event_source_disable(client
->timeout_t2
);
592 client_set_state(client
, DHCP6_STATE_STOPPED
);
595 static void client_stop(sd_dhcp6_client
*client
, int error
) {
596 DHCP6_CLIENT_DONT_DESTROY(client
);
600 client_notify(client
, error
);
602 client_cleanup(client
);
605 static int client_append_common_options_in_managed_mode(
606 sd_dhcp6_client
*client
,
609 const DHCP6IA
*ia_na
,
610 const DHCP6IA
*ia_pd
) {
615 assert(IN_SET(client
->state
,
616 DHCP6_STATE_SOLICITATION
,
620 DHCP6_STATE_STOPPING
));
625 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
) && ia_na
) {
626 r
= dhcp6_option_append_ia(buf
, offset
, ia_na
);
631 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
) && ia_pd
) {
632 r
= dhcp6_option_append_ia(buf
, offset
, ia_pd
);
637 if (client
->state
!= DHCP6_STATE_STOPPING
) {
638 r
= dhcp6_option_append_fqdn(buf
, offset
, client
->fqdn
);
643 r
= dhcp6_option_append_user_class(buf
, offset
, client
->user_class
);
647 r
= dhcp6_option_append_vendor_class(buf
, offset
, client
->vendor_class
);
651 r
= dhcp6_option_append_vendor_option(buf
, offset
, client
->vendor_options
);
658 static DHCP6MessageType
client_message_type_from_state(sd_dhcp6_client
*client
) {
661 switch (client
->state
) {
662 case DHCP6_STATE_INFORMATION_REQUEST
:
663 return DHCP6_MESSAGE_INFORMATION_REQUEST
;
664 case DHCP6_STATE_SOLICITATION
:
665 return DHCP6_MESSAGE_SOLICIT
;
666 case DHCP6_STATE_REQUEST
:
667 return DHCP6_MESSAGE_REQUEST
;
668 case DHCP6_STATE_RENEW
:
669 return DHCP6_MESSAGE_RENEW
;
670 case DHCP6_STATE_REBIND
:
671 return DHCP6_MESSAGE_REBIND
;
672 case DHCP6_STATE_STOPPING
:
673 return DHCP6_MESSAGE_RELEASE
;
675 assert_not_reached();
679 static int client_append_oro(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
680 _cleanup_free_ be16_t
*p
= NULL
;
689 switch (client
->state
) {
690 case DHCP6_STATE_INFORMATION_REQUEST
:
691 n
= client
->n_req_opts
;
692 p
= new(be16_t
, n
+ 2);
696 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
697 p
[n
++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
); /* RFC 8415 section 21.23 */
698 p
[n
++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT
); /* RFC 8415 section 21.25 */
700 typesafe_qsort(p
, n
, be16_compare_func
);
704 case DHCP6_STATE_SOLICITATION
:
705 n
= client
->n_req_opts
;
706 p
= new(be16_t
, n
+ 1);
710 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
711 p
[n
++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT
); /* RFC 8415 section 21.24 */
713 typesafe_qsort(p
, n
, be16_compare_func
);
717 case DHCP6_STATE_STOPPING
:
721 n
= client
->n_req_opts
;
722 req_opts
= client
->req_opts
;
728 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_ORO
, n
* sizeof(be16_t
), req_opts
);
731 static int client_append_mudurl(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
740 if (client
->state
== DHCP6_STATE_STOPPING
)
743 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_MUD_URL_V6
,
744 strlen(client
->mudurl
), client
->mudurl
);
747 int dhcp6_client_send_message(sd_dhcp6_client
*client
) {
748 _cleanup_free_
uint8_t *buf
= NULL
;
749 struct sd_dhcp6_option
*j
;
750 usec_t elapsed_usec
, time_now
;
752 DHCP6Message
*message
;
757 assert(client
->event
);
759 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &time_now
);
763 if (!GREEDY_REALLOC0(buf
, offsetof(DHCP6Message
, options
)))
766 message
= (DHCP6Message
*) buf
;
767 message
->transaction_id
= client
->transaction_id
;
768 message
->type
= client_message_type_from_state(client
);
769 offset
= offsetof(DHCP6Message
, options
);
771 switch (client
->state
) {
772 case DHCP6_STATE_INFORMATION_REQUEST
:
775 case DHCP6_STATE_SOLICITATION
:
776 if (client
->rapid_commit
) {
777 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
782 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
783 &client
->ia_na
, &client
->ia_pd
);
788 case DHCP6_STATE_REQUEST
:
789 case DHCP6_STATE_RENEW
:
790 case DHCP6_STATE_STOPPING
:
791 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_SERVERID
,
792 client
->lease
->serverid_len
,
793 client
->lease
->serverid
);
798 case DHCP6_STATE_REBIND
:
800 assert(client
->lease
);
802 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
803 client
->lease
->ia_na
, client
->lease
->ia_pd
);
808 case DHCP6_STATE_BOUND
:
809 case DHCP6_STATE_STOPPED
:
811 assert_not_reached();
814 r
= client_append_mudurl(client
, &buf
, &offset
);
818 r
= client_append_oro(client
, &buf
, &offset
);
822 assert(sd_dhcp_duid_is_set(&client
->duid
));
823 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_CLIENTID
,
824 client
->duid
.size
, &client
->duid
.duid
);
828 ORDERED_HASHMAP_FOREACH(j
, client
->extra_options
) {
829 r
= dhcp6_option_append(&buf
, &offset
, j
->option
, j
->length
, j
->data
);
834 /* RFC 8415 Section 21.9.
835 * A client MUST include an Elapsed Time option in messages to indicate how long the client has
836 * been trying to complete a DHCP message exchange. */
837 elapsed_usec
= MIN(usec_sub_unsigned(time_now
, client
->transaction_start
) / USEC_PER_MSEC
/ 10, (usec_t
) UINT16_MAX
);
838 elapsed_time
= htobe16(elapsed_usec
);
839 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_ELAPSED_TIME
, sizeof(elapsed_time
), &elapsed_time
);
843 r
= dhcp6_network_send_udp_socket(client
->fd
, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS
, buf
, offset
);
847 log_dhcp6_client(client
, "Sent %s",
848 dhcp6_message_type_to_string(client_message_type_from_state(client
)));
852 static usec_t
client_timeout_compute_random(usec_t val
) {
853 return usec_sub_unsigned(val
, random_u64_range(val
/ 10));
856 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
857 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
858 usec_t init_retransmit_time
, max_retransmit_time
;
861 assert(client
->event
);
863 switch (client
->state
) {
864 case DHCP6_STATE_INFORMATION_REQUEST
:
865 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
866 max_retransmit_time
= DHCP6_INF_MAX_RT
;
869 case DHCP6_STATE_SOLICITATION
:
871 if (client
->retransmit_count
> 0 && client
->lease
) {
872 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
876 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
877 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
880 case DHCP6_STATE_REQUEST
:
882 if (client
->retransmit_count
>= DHCP6_REQ_MAX_RC
) {
883 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
887 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
888 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
891 case DHCP6_STATE_RENEW
:
892 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
893 max_retransmit_time
= DHCP6_REN_MAX_RT
;
895 /* RFC 3315, section 18.1.3. says max retransmit duration will
896 be the remaining time until T2. Instead of setting MRD,
897 wait for T2 to trigger with the same end result */
900 case DHCP6_STATE_REBIND
:
901 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
902 max_retransmit_time
= DHCP6_REB_MAX_RT
;
904 /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */
907 case DHCP6_STATE_STOPPED
:
908 case DHCP6_STATE_STOPPING
:
909 case DHCP6_STATE_BOUND
:
911 assert_not_reached();
914 r
= dhcp6_client_send_message(client
);
916 client
->retransmit_count
++;
918 if (client
->retransmit_time
== 0) {
919 client
->retransmit_time
= client_timeout_compute_random(init_retransmit_time
);
921 if (client
->state
== DHCP6_STATE_SOLICITATION
)
922 client
->retransmit_time
+= init_retransmit_time
/ 10;
924 } else if (client
->retransmit_time
> max_retransmit_time
/ 2)
925 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
927 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
929 log_dhcp6_client(client
, "Next retransmission in %s",
930 FORMAT_TIMESPAN(client
->retransmit_time
, USEC_PER_SEC
));
932 r
= event_reset_time_relative(client
->event
, &client
->timeout_resend
,
934 client
->retransmit_time
, 10 * USEC_PER_MSEC
,
935 client_timeout_resend
, client
,
936 client
->event_priority
, "dhcp6-resend-timer", true);
938 client_stop(client
, r
);
943 static int client_start_transaction(sd_dhcp6_client
*client
, DHCP6State state
) {
947 assert(client
->event
);
950 case DHCP6_STATE_INFORMATION_REQUEST
:
951 case DHCP6_STATE_SOLICITATION
:
952 assert(client
->state
== DHCP6_STATE_STOPPED
);
954 case DHCP6_STATE_REQUEST
:
955 assert(client
->state
== DHCP6_STATE_SOLICITATION
);
957 case DHCP6_STATE_RENEW
:
958 assert(client
->state
== DHCP6_STATE_BOUND
);
960 case DHCP6_STATE_REBIND
:
961 assert(IN_SET(client
->state
, DHCP6_STATE_BOUND
, DHCP6_STATE_RENEW
));
963 case DHCP6_STATE_STOPPED
:
964 case DHCP6_STATE_STOPPING
:
965 case DHCP6_STATE_BOUND
:
967 assert_not_reached();
970 client_set_state(client
, state
);
972 client
->retransmit_time
= 0;
973 client
->retransmit_count
= 0;
974 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
976 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &client
->transaction_start
);
980 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
983 client_timeout_resend
, client
,
984 client
->event_priority
, "dhcp6-resend-timeout", true);
988 r
= sd_event_source_set_enabled(client
->receive_message
, SD_EVENT_ON
);
995 client_stop(client
, r
);
999 static int client_timeout_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1000 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1001 DHCP6_CLIENT_DONT_DESTROY(client
);
1004 (void) event_source_disable(client
->timeout_expire
);
1005 (void) event_source_disable(client
->timeout_t2
);
1006 (void) event_source_disable(client
->timeout_t1
);
1008 state
= client
->state
;
1010 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
1012 /* RFC 3315, section 18.1.4., says that "...the client may choose to
1013 use a Solicit message to locate a new DHCP server..." */
1014 if (state
== DHCP6_STATE_REBIND
)
1015 (void) client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1020 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1021 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1023 (void) event_source_disable(client
->timeout_t2
);
1024 (void) event_source_disable(client
->timeout_t1
);
1026 log_dhcp6_client(client
, "Timeout T2");
1028 (void) client_start_transaction(client
, DHCP6_STATE_REBIND
);
1033 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1034 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1036 (void) event_source_disable(client
->timeout_t1
);
1038 log_dhcp6_client(client
, "Timeout T1");
1040 (void) client_start_transaction(client
, DHCP6_STATE_RENEW
);
1045 static int client_enter_bound_state(sd_dhcp6_client
*client
) {
1046 usec_t lifetime_t1
, lifetime_t2
, lifetime_valid
;
1050 assert(client
->lease
);
1051 assert(IN_SET(client
->state
,
1052 DHCP6_STATE_SOLICITATION
,
1053 DHCP6_STATE_REQUEST
,
1055 DHCP6_STATE_REBIND
));
1057 (void) event_source_disable(client
->receive_message
);
1058 (void) event_source_disable(client
->timeout_resend
);
1060 r
= sd_dhcp6_lease_get_t1(client
->lease
, &lifetime_t1
);
1064 r
= sd_dhcp6_lease_get_t2(client
->lease
, &lifetime_t2
);
1068 r
= sd_dhcp6_lease_get_valid_lifetime(client
->lease
, &lifetime_valid
);
1072 lifetime_t2
= client_timeout_compute_random(lifetime_t2
);
1073 lifetime_t1
= client_timeout_compute_random(MIN(lifetime_t1
, lifetime_t2
));
1075 if (lifetime_t1
== USEC_INFINITY
) {
1076 log_dhcp6_client(client
, "Infinite T1");
1077 event_source_disable(client
->timeout_t1
);
1079 log_dhcp6_client(client
, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1
, USEC_PER_SEC
));
1080 r
= event_reset_time_relative(client
->event
, &client
->timeout_t1
,
1082 lifetime_t1
, 10 * USEC_PER_SEC
,
1083 client_timeout_t1
, client
,
1084 client
->event_priority
, "dhcp6-t1-timeout", true);
1089 if (lifetime_t2
== USEC_INFINITY
) {
1090 log_dhcp6_client(client
, "Infinite T2");
1091 event_source_disable(client
->timeout_t2
);
1093 log_dhcp6_client(client
, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2
, USEC_PER_SEC
));
1094 r
= event_reset_time_relative(client
->event
, &client
->timeout_t2
,
1096 lifetime_t2
, 10 * USEC_PER_SEC
,
1097 client_timeout_t2
, client
,
1098 client
->event_priority
, "dhcp6-t2-timeout", true);
1103 if (lifetime_valid
== USEC_INFINITY
) {
1104 log_dhcp6_client(client
, "Infinite valid lifetime");
1105 event_source_disable(client
->timeout_expire
);
1107 log_dhcp6_client(client
, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid
, USEC_PER_SEC
));
1109 r
= event_reset_time_relative(client
->event
, &client
->timeout_expire
,
1111 lifetime_valid
, USEC_PER_SEC
,
1112 client_timeout_expire
, client
,
1113 client
->event_priority
, "dhcp6-lease-expire", true);
1118 client_set_state(client
, DHCP6_STATE_BOUND
);
1119 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1123 client_stop(client
, r
);
1127 static int log_invalid_message_type(sd_dhcp6_client
*client
, const DHCP6Message
*message
) {
1128 const char *type_str
;
1133 type_str
= dhcp6_message_type_to_string(message
->type
);
1135 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1136 "Received unexpected %s message, ignoring.", type_str
);
1138 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1139 "Received unsupported message type %u, ignoring.", message
->type
);
1142 static int client_process_information(
1143 sd_dhcp6_client
*client
,
1144 DHCP6Message
*message
,
1146 const triple_timestamp
*timestamp
,
1147 const struct in6_addr
*server_address
) {
1149 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1155 if (message
->type
!= DHCP6_MESSAGE_REPLY
)
1156 return log_invalid_message_type(client
, message
);
1158 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1160 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1162 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1164 sd_dhcp6_lease_unref(client
->lease
);
1165 client
->lease
= TAKE_PTR(lease
);
1167 /* Do not call client_stop() here, as it frees the acquired lease. */
1168 (void) event_source_disable(client
->receive_message
);
1169 (void) event_source_disable(client
->timeout_resend
);
1170 client_set_state(client
, DHCP6_STATE_STOPPED
);
1172 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1176 static int client_process_reply(
1177 sd_dhcp6_client
*client
,
1178 DHCP6Message
*message
,
1180 const triple_timestamp
*timestamp
,
1181 const struct in6_addr
*server_address
) {
1183 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1189 if (message
->type
!= DHCP6_MESSAGE_REPLY
)
1190 return log_invalid_message_type(client
, message
);
1192 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1193 if (r
== -EADDRNOTAVAIL
) {
1195 /* If NoBinding status code is received, we cannot request the address anymore.
1196 * Let's restart transaction from the beginning. */
1198 if (client
->state
== DHCP6_STATE_REQUEST
)
1199 /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
1200 client_cleanup(client
);
1202 /* We need to notify the previous lease was expired. */
1203 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
1205 return client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1208 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1210 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1212 sd_dhcp6_lease_unref(client
->lease
);
1213 client
->lease
= TAKE_PTR(lease
);
1215 return client_enter_bound_state(client
);
1218 static int client_process_advertise_or_rapid_commit_reply(
1219 sd_dhcp6_client
*client
,
1220 DHCP6Message
*message
,
1222 const triple_timestamp
*timestamp
,
1223 const struct in6_addr
*server_address
) {
1225 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1226 uint8_t pref_advertise
, pref_lease
= 0;
1232 if (!IN_SET(message
->type
, DHCP6_MESSAGE_ADVERTISE
, DHCP6_MESSAGE_REPLY
))
1233 return log_invalid_message_type(client
, message
);
1235 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1237 return log_dhcp6_client_errno(client
, r
, "Failed to process received %s message, ignoring: %m",
1238 dhcp6_message_type_to_string(message
->type
));
1240 if (message
->type
== DHCP6_MESSAGE_REPLY
) {
1243 if (!client
->rapid_commit
)
1244 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1245 "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
1247 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1252 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1253 "Received reply message without rapid commit flag, ignoring.");
1255 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1257 sd_dhcp6_lease_unref(client
->lease
);
1258 client
->lease
= TAKE_PTR(lease
);
1260 return client_enter_bound_state(client
);
1263 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1267 if (client
->lease
) {
1268 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1273 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1275 if (!client
->lease
|| pref_advertise
> pref_lease
) {
1276 /* If this is the first advertise message or has higher preference, then save the lease. */
1277 sd_dhcp6_lease_unref(client
->lease
);
1278 client
->lease
= TAKE_PTR(lease
);
1281 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1282 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
1287 static int client_receive_message(
1293 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1294 DHCP6_CLIENT_DONT_DESTROY(client
);
1295 /* This needs to be initialized with zero. See #20741.
1296 * The issue is fixed on glibc-2.35 (8fba672472ae0055387e9315fc2eddfa6775ca79). */
1297 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL
) control
= {};
1299 union sockaddr_union sa
= {};
1300 struct msghdr msg
= {
1302 .msg_namelen
= sizeof(sa
),
1305 .msg_control
= &control
,
1306 .msg_controllen
= sizeof(control
),
1309 _cleanup_free_ DHCP6Message
*message
= NULL
;
1310 struct in6_addr
*server_address
= NULL
;
1311 ssize_t buflen
, len
;
1313 buflen
= next_datagram_size_fd(fd
);
1314 if (ERRNO_IS_NEG_TRANSIENT(buflen
) || ERRNO_IS_NEG_DISCONNECT(buflen
))
1317 log_dhcp6_client_errno(client
, buflen
, "Failed to determine datagram size to read, ignoring: %m");
1321 message
= malloc(buflen
);
1325 iov
= IOVEC_MAKE(message
, buflen
);
1327 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
1328 if (ERRNO_IS_NEG_TRANSIENT(len
) || ERRNO_IS_NEG_DISCONNECT(len
))
1331 log_dhcp6_client_errno(client
, len
, "Could not receive message from UDP socket, ignoring: %m");
1334 if ((size_t) len
< sizeof(DHCP6Message
)) {
1335 log_dhcp6_client(client
, "Too small to be DHCPv6 message: ignoring");
1339 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1340 if (msg
.msg_namelen
> 0) {
1341 if (msg
.msg_namelen
!= sizeof(struct sockaddr_in6
) || sa
.in6
.sin6_family
!= AF_INET6
) {
1342 log_dhcp6_client(client
, "Received message from invalid source, ignoring.");
1346 server_address
= &sa
.in6
.sin6_addr
;
1349 triple_timestamp_from_cmsg(&t
, &msg
);
1351 if (client
->transaction_id
!= (message
->transaction_id
& htobe32(0x00ffffff)))
1354 switch (client
->state
) {
1355 case DHCP6_STATE_INFORMATION_REQUEST
:
1356 if (client_process_information(client
, message
, len
, &t
, server_address
) < 0)
1360 case DHCP6_STATE_SOLICITATION
:
1361 if (client_process_advertise_or_rapid_commit_reply(client
, message
, len
, &t
, server_address
) < 0)
1365 case DHCP6_STATE_REQUEST
:
1366 case DHCP6_STATE_RENEW
:
1367 case DHCP6_STATE_REBIND
:
1368 if (client_process_reply(client
, message
, len
, &t
, server_address
) < 0)
1372 case DHCP6_STATE_BOUND
:
1373 case DHCP6_STATE_STOPPED
:
1374 case DHCP6_STATE_STOPPING
:
1376 assert_not_reached();
1382 static int client_send_release(sd_dhcp6_client
*client
) {
1383 sd_dhcp6_lease
*lease
;
1387 if (!client
->send_release
)
1390 if (sd_dhcp6_client_get_lease(client
, &lease
) < 0)
1393 if (!lease
->ia_na
&& !lease
->ia_pd
)
1396 client_set_state(client
, DHCP6_STATE_STOPPING
);
1397 return dhcp6_client_send_message(client
);
1400 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1406 /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
1407 * engine is about to release its UDP socket unconditionally. */
1408 r
= client_send_release(client
);
1410 log_dhcp6_client_errno(client
, r
,
1411 "Failed to send DHCPv6 release message, ignoring: %m");
1413 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1415 client
->receive_message
= sd_event_source_unref(client
->receive_message
);
1416 client
->fd
= safe_close(client
->fd
);
1421 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1425 return client
->state
!= DHCP6_STATE_STOPPED
;
1428 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1429 DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1432 assert_return(client
, -EINVAL
);
1433 assert_return(client
->event
, -EINVAL
);
1434 assert_return(client
->ifindex
> 0, -EINVAL
);
1435 assert_return(in6_addr_is_link_local(&client
->local_address
) > 0, -EINVAL
);
1436 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1437 assert_return(client
->information_request
|| client
->request_ia
!= 0, -EINVAL
);
1439 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1440 * request may be stored. */
1441 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
1443 r
= client_ensure_iaid(client
);
1447 r
= client_ensure_duid(client
);
1451 if (client
->fd
< 0) {
1452 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1454 return log_dhcp6_client_errno(client
, r
,
1455 "Failed to bind to UDP socket at address %s: %m",
1456 IN6_ADDR_TO_STRING(&client
->local_address
));
1461 if (!client
->receive_message
) {
1462 _cleanup_(sd_event_source_disable_unrefp
) sd_event_source
*s
= NULL
;
1464 r
= sd_event_add_io(client
->event
, &s
, client
->fd
, EPOLLIN
, client_receive_message
, client
);
1468 r
= sd_event_source_set_priority(s
, client
->event_priority
);
1472 r
= sd_event_source_set_description(s
, "dhcp6-receive-message");
1476 client
->receive_message
= TAKE_PTR(s
);
1479 if (client
->information_request
) {
1480 usec_t t
= now(CLOCK_MONOTONIC
);
1482 if (t
< usec_add(client
->information_request_time_usec
, client
->information_refresh_time_usec
))
1485 client
->information_request_time_usec
= t
;
1486 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1489 log_dhcp6_client(client
, "Starting in %s mode",
1490 client
->information_request
? "Information request" : "Solicit");
1492 return client_start_transaction(client
, state
);
1495 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1498 assert_return(client
, -EINVAL
);
1499 assert_return(!client
->event
, -EBUSY
);
1500 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1503 client
->event
= sd_event_ref(event
);
1505 r
= sd_event_default(&client
->event
);
1510 client
->event_priority
= priority
;
1515 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1516 assert_return(client
, -EINVAL
);
1517 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1519 client
->event
= sd_event_unref(client
->event
);
1524 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1525 assert_return(client
, NULL
);
1527 return client
->event
;
1530 int sd_dhcp6_client_attach_device(sd_dhcp6_client
*client
, sd_device
*dev
) {
1531 assert_return(client
, -EINVAL
);
1533 return device_unref_and_replace(client
->dev
, dev
);
1536 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1540 sd_dhcp6_lease_unref(client
->lease
);
1542 sd_event_source_disable_unref(client
->receive_message
);
1543 sd_event_source_disable_unref(client
->timeout_resend
);
1544 sd_event_source_disable_unref(client
->timeout_expire
);
1545 sd_event_source_disable_unref(client
->timeout_t1
);
1546 sd_event_source_disable_unref(client
->timeout_t2
);
1547 sd_event_unref(client
->event
);
1549 client
->fd
= safe_close(client
->fd
);
1551 sd_device_unref(client
->dev
);
1553 free(client
->req_opts
);
1555 free(client
->mudurl
);
1556 dhcp6_ia_clear_addresses(&client
->ia_pd
);
1557 ordered_hashmap_free(client
->extra_options
);
1558 ordered_set_free(client
->vendor_options
);
1559 strv_free(client
->user_class
);
1560 strv_free(client
->vendor_class
);
1561 free(client
->ifname
);
1563 return mfree(client
);
1566 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1568 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1569 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1571 assert_return(ret
, -EINVAL
);
1573 client
= new(sd_dhcp6_client
, 1);
1577 *client
= (sd_dhcp6_client
) {
1579 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1580 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1582 .request_ia
= DHCP6_REQUEST_IA_NA
| DHCP6_REQUEST_IA_PD
,
1584 .rapid_commit
= true,
1587 *ret
= TAKE_PTR(client
);