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-duid-internal.h"
16 #include "dhcp6-internal.h"
17 #include "dhcp6-lease-internal.h"
18 #include "dns-domain.h"
19 #include "event-util.h"
21 #include "hostname-util.h"
22 #include "in-addr-util.h"
23 #include "iovec-util.h"
24 #include "random-util.h"
25 #include "socket-util.h"
26 #include "sort-util.h"
30 #define DHCP6_CLIENT_DONT_DESTROY(client) \
31 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
33 static int client_start_transaction(sd_dhcp6_client
*client
, DHCP6State state
);
35 int sd_dhcp6_client_set_callback(
36 sd_dhcp6_client
*client
,
37 sd_dhcp6_client_callback_t cb
,
40 assert_return(client
, -EINVAL
);
42 client
->callback
= cb
;
43 client
->userdata
= userdata
;
48 int dhcp6_client_set_state_callback(
49 sd_dhcp6_client
*client
,
50 sd_dhcp6_client_callback_t cb
,
53 assert_return(client
, -EINVAL
);
55 client
->state_callback
= cb
;
56 client
->state_userdata
= userdata
;
61 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
62 assert_return(client
, -EINVAL
);
63 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
64 assert_return(ifindex
> 0, -EINVAL
);
66 client
->ifindex
= ifindex
;
70 int sd_dhcp6_client_set_ifname(sd_dhcp6_client
*client
, const char *ifname
) {
71 assert_return(client
, -EINVAL
);
72 assert_return(ifname
, -EINVAL
);
74 if (!ifname_valid_full(ifname
, IFNAME_VALID_ALTERNATIVE
))
77 return free_and_strdup(&client
->ifname
, ifname
);
80 int sd_dhcp6_client_get_ifname(sd_dhcp6_client
*client
, const char **ret
) {
83 assert_return(client
, -EINVAL
);
85 r
= get_ifname(client
->ifindex
, &client
->ifname
);
90 *ret
= client
->ifname
;
95 int sd_dhcp6_client_set_local_address(
96 sd_dhcp6_client
*client
,
97 const struct in6_addr
*local_address
) {
99 assert_return(client
, -EINVAL
);
100 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
101 assert_return(local_address
, -EINVAL
);
102 assert_return(in6_addr_is_link_local(local_address
) > 0, -EINVAL
);
104 client
->local_address
= *local_address
;
109 int sd_dhcp6_client_set_mac(
110 sd_dhcp6_client
*client
,
115 assert_return(client
, -EINVAL
);
116 assert_return(addr
, -EINVAL
);
117 assert_return(addr_len
<= sizeof(client
->hw_addr
.bytes
), -EINVAL
);
119 /* Unlike the other setters, it is OK to set a new MAC address while the client is running,
120 * as the MAC address is used only when setting DUID or IAID. */
122 if (arp_type
== ARPHRD_ETHER
)
123 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
124 else if (arp_type
== ARPHRD_INFINIBAND
)
125 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
127 client
->arp_type
= ARPHRD_NONE
;
128 client
->hw_addr
.length
= 0;
132 client
->arp_type
= arp_type
;
133 hw_addr_set(&client
->hw_addr
, addr
, addr_len
);
138 int sd_dhcp6_client_set_prefix_delegation_hint(
139 sd_dhcp6_client
*client
,
141 const struct in6_addr
*pd_prefix
) {
143 _cleanup_free_ DHCP6Address
*prefix
= NULL
;
145 assert_return(client
, -EINVAL
);
146 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
149 /* clear previous assignments. */
150 dhcp6_ia_clear_addresses(&client
->ia_pd
);
154 assert_return(prefixlen
> 0 && prefixlen
<= 128, -EINVAL
);
156 prefix
= new(DHCP6Address
, 1);
160 *prefix
= (DHCP6Address
) {
161 .iapdprefix
.address
= *pd_prefix
,
162 .iapdprefix
.prefixlen
= prefixlen
,
165 LIST_PREPEND(addresses
, client
->ia_pd
.addresses
, TAKE_PTR(prefix
));
169 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
172 assert_return(client
, -EINVAL
);
173 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
176 /* Clear the previous assignments. */
177 ordered_set_clear(client
->vendor_options
);
181 r
= ordered_set_ensure_put(&client
->vendor_options
, &dhcp6_option_hash_ops
, v
);
185 sd_dhcp6_option_ref(v
);
190 static int client_ensure_duid(sd_dhcp6_client
*client
) {
193 if (sd_dhcp_duid_is_set(&client
->duid
))
196 return sd_dhcp6_client_set_duid_en(client
);
200 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
201 * without further modification. Otherwise, if duid_type is supported, DUID
202 * is set based on that type. Otherwise, an error is returned.
204 int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client
*client
, uint64_t llt_time
) {
207 assert_return(client
, -EINVAL
);
208 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
210 r
= sd_dhcp_duid_set_llt(&client
->duid
, client
->hw_addr
.bytes
, client
->hw_addr
.length
, client
->arp_type
, llt_time
);
212 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LLT: %m");
217 int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client
*client
) {
220 assert_return(client
, -EINVAL
);
221 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
223 r
= sd_dhcp_duid_set_ll(&client
->duid
, client
->hw_addr
.bytes
, client
->hw_addr
.length
, client
->arp_type
);
225 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LL: %m");
230 int sd_dhcp6_client_set_duid_en(sd_dhcp6_client
*client
) {
233 assert_return(client
, -EINVAL
);
234 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
236 r
= sd_dhcp_duid_set_en(&client
->duid
);
238 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-EN: %m");
243 int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client
*client
) {
246 assert_return(client
, -EINVAL
);
247 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
249 r
= sd_dhcp_duid_set_uuid(&client
->duid
);
251 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-UUID: %m");
256 int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client
*client
, uint16_t duid_type
, const uint8_t *duid
, size_t duid_len
) {
259 assert_return(client
, -EINVAL
);
260 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
261 assert_return(duid
|| duid_len
== 0, -EINVAL
);
263 r
= sd_dhcp_duid_set(&client
->duid
, duid_type
, duid
, duid_len
);
265 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID: %m");
270 int sd_dhcp6_client_set_duid(sd_dhcp6_client
*client
, const sd_dhcp_duid
*duid
) {
271 assert_return(client
, -EINVAL
);
272 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
273 assert_return(sd_dhcp_duid_is_set(duid
), -EINVAL
);
275 client
->duid
= *duid
;
279 int sd_dhcp6_client_get_duid(sd_dhcp6_client
*client
, const sd_dhcp_duid
**ret
) {
280 assert_return(client
, -EINVAL
);
281 assert_return(ret
, -EINVAL
);
283 if (!sd_dhcp_duid_is_set(&client
->duid
))
286 *ret
= &client
->duid
;
290 int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client
*client
, char **ret
) {
291 assert_return(client
, -EINVAL
);
292 assert_return(ret
, -EINVAL
);
294 if (!sd_dhcp_duid_is_set(&client
->duid
))
297 return sd_dhcp_duid_to_string(&client
->duid
, ret
);
300 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
301 assert_return(client
, -EINVAL
);
302 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
304 client
->ia_na
.header
.id
= htobe32(iaid
);
305 client
->ia_pd
.header
.id
= htobe32(iaid
);
306 client
->iaid_set
= true;
311 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
317 if (client
->iaid_set
)
320 r
= dhcp_identifier_set_iaid(client
->dev
, &client
->hw_addr
,
321 /* legacy_unstable_byteorder = */ true,
326 client
->ia_na
.header
.id
= iaid
;
327 client
->ia_pd
.header
.id
= iaid
;
328 client
->iaid_set
= true;
333 int sd_dhcp6_client_get_iaid(sd_dhcp6_client
*client
, uint32_t *iaid
) {
334 assert_return(client
, -EINVAL
);
335 assert_return(iaid
, -EINVAL
);
337 if (!client
->iaid_set
)
340 *iaid
= be32toh(client
->ia_na
.header
.id
);
345 int sd_dhcp6_client_set_fqdn(
346 sd_dhcp6_client
*client
,
349 assert_return(client
, -EINVAL
);
350 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
352 /* Make sure FQDN qualifies as DNS and as Linux hostname */
354 !(hostname_is_valid(fqdn
, 0) && dns_name_is_valid(fqdn
) > 0))
357 return free_and_strdup(&client
->fqdn
, fqdn
);
360 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
361 assert_return(client
, -EINVAL
);
362 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
364 client
->information_request
= enabled
;
369 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
370 assert_return(client
, -EINVAL
);
371 assert_return(enabled
, -EINVAL
);
373 *enabled
= client
->information_request
;
378 static int be16_compare_func(const be16_t
*a
, const be16_t
*b
) {
379 return CMP(be16toh(*a
), be16toh(*b
));
382 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
385 assert_return(client
, -EINVAL
);
386 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
388 if (!dhcp6_option_can_request(option
))
391 opt
= htobe16(option
);
392 if (typesafe_bsearch(&opt
, client
->req_opts
, client
->n_req_opts
, be16_compare_func
))
395 if (!GREEDY_REALLOC(client
->req_opts
, client
->n_req_opts
+ 1))
398 client
->req_opts
[client
->n_req_opts
++] = opt
;
400 /* Sort immediately to make the above binary search will work for the next time. */
401 typesafe_qsort(client
->req_opts
, client
->n_req_opts
, be16_compare_func
);
405 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client
*client
, const char *mudurl
) {
406 assert_return(client
, -EINVAL
);
407 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
408 assert_return(mudurl
, -EINVAL
);
409 assert_return(strlen(mudurl
) <= UINT8_MAX
, -EINVAL
);
410 assert_return(http_url_is_valid(mudurl
), -EINVAL
);
412 return free_and_strdup(&client
->mudurl
, mudurl
);
415 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client
*client
, char * const *user_class
) {
418 assert_return(client
, -EINVAL
);
419 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
420 assert_return(!strv_isempty(user_class
), -EINVAL
);
422 STRV_FOREACH(p
, user_class
) {
423 size_t len
= strlen(*p
);
425 if (len
> UINT16_MAX
|| len
== 0)
429 s
= strv_copy(user_class
);
433 return strv_free_and_replace(client
->user_class
, s
);
436 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client
*client
, char * const *vendor_class
) {
439 assert_return(client
, -EINVAL
);
440 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
441 assert_return(!strv_isempty(vendor_class
), -EINVAL
);
443 STRV_FOREACH(p
, vendor_class
) {
444 size_t len
= strlen(*p
);
446 if (len
> UINT16_MAX
|| len
== 0)
450 s
= strv_copy(vendor_class
);
454 return strv_free_and_replace(client
->vendor_class
, s
);
457 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
458 assert_return(client
, -EINVAL
);
459 assert_return(delegation
, -EINVAL
);
461 *delegation
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
);
466 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
467 assert_return(client
, -EINVAL
);
468 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
470 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_PD
, delegation
);
475 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
476 assert_return(client
, -EINVAL
);
477 assert_return(request
, -EINVAL
);
479 *request
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
);
484 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
485 assert_return(client
, -EINVAL
);
486 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
488 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_NA
, request
);
493 int dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
495 assert_se(network_test_mode_enabled());
497 /* This is for tests or fuzzers. */
499 client
->transaction_id
= transaction_id
& htobe32(0x00ffffff);
504 int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client
*client
, int enable
) {
505 assert_return(client
, -EINVAL
);
506 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
508 client
->rapid_commit
= enable
;
512 int sd_dhcp6_client_set_send_release(sd_dhcp6_client
*client
, int enable
) {
513 assert_return(client
, -EINVAL
);
515 client
->send_release
= enable
;
519 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
520 assert_return(client
, -EINVAL
);
526 *ret
= client
->lease
;
531 int sd_dhcp6_client_add_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
534 assert_return(client
, -EINVAL
);
535 assert_return(v
, -EINVAL
);
537 r
= ordered_hashmap_ensure_put(&client
->extra_options
, &dhcp6_option_hash_ops
, UINT_TO_PTR(v
->option
), v
);
541 sd_dhcp6_option_ref(v
);
545 static void client_set_state(sd_dhcp6_client
*client
, DHCP6State state
) {
548 if (client
->state
== state
)
551 log_dhcp6_client(client
, "State changed: %s -> %s",
552 dhcp6_state_to_string(client
->state
), dhcp6_state_to_string(state
));
554 client
->state
= state
;
556 if (client
->state_callback
)
557 client
->state_callback(client
, state
, client
->state_userdata
);
560 int dhcp6_client_get_state(sd_dhcp6_client
*client
) {
561 assert_return(client
, -EINVAL
);
563 return client
->state
;
566 static void client_notify(sd_dhcp6_client
*client
, int event
) {
569 if (client
->callback
)
570 client
->callback(client
, event
, client
->userdata
);
573 static void client_cleanup(sd_dhcp6_client
*client
) {
576 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
578 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
579 * even though the lease is freed below. */
580 client
->information_request_time_usec
= 0;
581 client
->information_refresh_time_usec
= 0;
583 (void) event_source_disable(client
->receive_message
);
584 (void) event_source_disable(client
->timeout_resend
);
585 (void) event_source_disable(client
->timeout_expire
);
586 (void) event_source_disable(client
->timeout_t1
);
587 (void) event_source_disable(client
->timeout_t2
);
589 client_set_state(client
, DHCP6_STATE_STOPPED
);
592 static void client_stop(sd_dhcp6_client
*client
, int error
) {
593 DHCP6_CLIENT_DONT_DESTROY(client
);
597 client_notify(client
, error
);
599 client_cleanup(client
);
602 static int client_append_common_options_in_managed_mode(
603 sd_dhcp6_client
*client
,
606 const DHCP6IA
*ia_na
,
607 const DHCP6IA
*ia_pd
) {
612 assert(IN_SET(client
->state
,
613 DHCP6_STATE_SOLICITATION
,
617 DHCP6_STATE_STOPPING
));
622 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
) && ia_na
) {
623 r
= dhcp6_option_append_ia(buf
, offset
, ia_na
);
628 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
) && ia_pd
) {
629 r
= dhcp6_option_append_ia(buf
, offset
, ia_pd
);
634 if (client
->state
!= DHCP6_STATE_STOPPING
) {
635 r
= dhcp6_option_append_fqdn(buf
, offset
, client
->fqdn
);
640 r
= dhcp6_option_append_user_class(buf
, offset
, client
->user_class
);
644 r
= dhcp6_option_append_vendor_class(buf
, offset
, client
->vendor_class
);
648 r
= dhcp6_option_append_vendor_option(buf
, offset
, client
->vendor_options
);
655 static DHCP6MessageType
client_message_type_from_state(sd_dhcp6_client
*client
) {
658 switch (client
->state
) {
659 case DHCP6_STATE_INFORMATION_REQUEST
:
660 return DHCP6_MESSAGE_INFORMATION_REQUEST
;
661 case DHCP6_STATE_SOLICITATION
:
662 return DHCP6_MESSAGE_SOLICIT
;
663 case DHCP6_STATE_REQUEST
:
664 return DHCP6_MESSAGE_REQUEST
;
665 case DHCP6_STATE_RENEW
:
666 return DHCP6_MESSAGE_RENEW
;
667 case DHCP6_STATE_REBIND
:
668 return DHCP6_MESSAGE_REBIND
;
669 case DHCP6_STATE_STOPPING
:
670 return DHCP6_MESSAGE_RELEASE
;
672 assert_not_reached();
676 static int client_append_oro(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
677 _cleanup_free_ be16_t
*p
= NULL
;
686 switch (client
->state
) {
687 case DHCP6_STATE_INFORMATION_REQUEST
:
688 n
= client
->n_req_opts
;
689 p
= new(be16_t
, n
+ 2);
693 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
694 p
[n
++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
); /* RFC 8415 section 21.23 */
695 p
[n
++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT
); /* RFC 8415 section 21.25 */
697 typesafe_qsort(p
, n
, be16_compare_func
);
701 case DHCP6_STATE_SOLICITATION
:
702 n
= client
->n_req_opts
;
703 p
= new(be16_t
, n
+ 1);
707 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
708 p
[n
++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT
); /* RFC 8415 section 21.24 */
710 typesafe_qsort(p
, n
, be16_compare_func
);
714 case DHCP6_STATE_STOPPING
:
718 n
= client
->n_req_opts
;
719 req_opts
= client
->req_opts
;
725 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_ORO
, n
* sizeof(be16_t
), req_opts
);
728 static int client_append_mudurl(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
737 if (client
->state
== DHCP6_STATE_STOPPING
)
740 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_MUD_URL_V6
,
741 strlen(client
->mudurl
), client
->mudurl
);
744 int dhcp6_client_send_message(sd_dhcp6_client
*client
) {
745 _cleanup_free_
uint8_t *buf
= NULL
;
746 struct sd_dhcp6_option
*j
;
747 usec_t elapsed_usec
, time_now
;
749 DHCP6Message
*message
;
754 assert(client
->event
);
756 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &time_now
);
760 if (!GREEDY_REALLOC0(buf
, offsetof(DHCP6Message
, options
)))
763 message
= (DHCP6Message
*) buf
;
764 message
->transaction_id
= client
->transaction_id
;
765 message
->type
= client_message_type_from_state(client
);
766 offset
= offsetof(DHCP6Message
, options
);
768 switch (client
->state
) {
769 case DHCP6_STATE_INFORMATION_REQUEST
:
772 case DHCP6_STATE_SOLICITATION
:
773 if (client
->rapid_commit
) {
774 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
779 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
780 &client
->ia_na
, &client
->ia_pd
);
785 case DHCP6_STATE_REQUEST
:
786 case DHCP6_STATE_RENEW
:
787 case DHCP6_STATE_STOPPING
:
788 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_SERVERID
,
789 client
->lease
->serverid_len
,
790 client
->lease
->serverid
);
795 case DHCP6_STATE_REBIND
:
797 assert(client
->lease
);
799 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
800 client
->lease
->ia_na
, client
->lease
->ia_pd
);
805 case DHCP6_STATE_BOUND
:
806 case DHCP6_STATE_STOPPED
:
808 assert_not_reached();
811 r
= client_append_mudurl(client
, &buf
, &offset
);
815 r
= client_append_oro(client
, &buf
, &offset
);
819 assert(sd_dhcp_duid_is_set(&client
->duid
));
820 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_CLIENTID
,
821 client
->duid
.size
, &client
->duid
.duid
);
825 ORDERED_HASHMAP_FOREACH(j
, client
->extra_options
) {
826 r
= dhcp6_option_append(&buf
, &offset
, j
->option
, j
->length
, j
->data
);
831 /* RFC 8415 Section 21.9.
832 * A client MUST include an Elapsed Time option in messages to indicate how long the client has
833 * been trying to complete a DHCP message exchange. */
834 elapsed_usec
= MIN(usec_sub_unsigned(time_now
, client
->transaction_start
) / USEC_PER_MSEC
/ 10, (usec_t
) UINT16_MAX
);
835 elapsed_time
= htobe16(elapsed_usec
);
836 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_ELAPSED_TIME
, sizeof(elapsed_time
), &elapsed_time
);
840 r
= dhcp6_network_send_udp_socket(client
->fd
, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS
, buf
, offset
);
844 log_dhcp6_client(client
, "Sent %s",
845 dhcp6_message_type_to_string(client_message_type_from_state(client
)));
849 static usec_t
client_timeout_compute_random(usec_t val
) {
850 return usec_sub_unsigned(val
, random_u64_range(val
/ 10));
853 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
854 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
855 usec_t init_retransmit_time
, max_retransmit_time
;
858 assert(client
->event
);
860 switch (client
->state
) {
861 case DHCP6_STATE_INFORMATION_REQUEST
:
862 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
863 max_retransmit_time
= DHCP6_INF_MAX_RT
;
866 case DHCP6_STATE_SOLICITATION
:
868 if (client
->retransmit_count
> 0 && client
->lease
) {
869 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
873 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
874 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
877 case DHCP6_STATE_REQUEST
:
879 if (client
->retransmit_count
>= DHCP6_REQ_MAX_RC
) {
880 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
884 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
885 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
888 case DHCP6_STATE_RENEW
:
889 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
890 max_retransmit_time
= DHCP6_REN_MAX_RT
;
892 /* RFC 3315, section 18.1.3. says max retransmit duration will
893 be the remaining time until T2. Instead of setting MRD,
894 wait for T2 to trigger with the same end result */
897 case DHCP6_STATE_REBIND
:
898 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
899 max_retransmit_time
= DHCP6_REB_MAX_RT
;
901 /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */
904 case DHCP6_STATE_STOPPED
:
905 case DHCP6_STATE_STOPPING
:
906 case DHCP6_STATE_BOUND
:
908 assert_not_reached();
911 r
= dhcp6_client_send_message(client
);
913 client
->retransmit_count
++;
915 if (client
->retransmit_time
== 0) {
916 client
->retransmit_time
= client_timeout_compute_random(init_retransmit_time
);
918 if (client
->state
== DHCP6_STATE_SOLICITATION
)
919 client
->retransmit_time
+= init_retransmit_time
/ 10;
921 } else if (client
->retransmit_time
> max_retransmit_time
/ 2)
922 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
924 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
926 log_dhcp6_client(client
, "Next retransmission in %s",
927 FORMAT_TIMESPAN(client
->retransmit_time
, USEC_PER_SEC
));
929 r
= event_reset_time_relative(client
->event
, &client
->timeout_resend
,
931 client
->retransmit_time
, 10 * USEC_PER_MSEC
,
932 client_timeout_resend
, client
,
933 client
->event_priority
, "dhcp6-resend-timer", true);
935 client_stop(client
, r
);
940 static int client_start_transaction(sd_dhcp6_client
*client
, DHCP6State state
) {
944 assert(client
->event
);
947 case DHCP6_STATE_INFORMATION_REQUEST
:
948 case DHCP6_STATE_SOLICITATION
:
949 assert(client
->state
== DHCP6_STATE_STOPPED
);
951 case DHCP6_STATE_REQUEST
:
952 assert(client
->state
== DHCP6_STATE_SOLICITATION
);
954 case DHCP6_STATE_RENEW
:
955 assert(client
->state
== DHCP6_STATE_BOUND
);
957 case DHCP6_STATE_REBIND
:
958 assert(IN_SET(client
->state
, DHCP6_STATE_BOUND
, DHCP6_STATE_RENEW
));
960 case DHCP6_STATE_STOPPED
:
961 case DHCP6_STATE_STOPPING
:
962 case DHCP6_STATE_BOUND
:
964 assert_not_reached();
967 client_set_state(client
, state
);
969 client
->retransmit_time
= 0;
970 client
->retransmit_count
= 0;
971 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
973 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &client
->transaction_start
);
977 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
980 client_timeout_resend
, client
,
981 client
->event_priority
, "dhcp6-resend-timeout", true);
985 r
= sd_event_source_set_enabled(client
->receive_message
, SD_EVENT_ON
);
992 client_stop(client
, r
);
996 static int client_timeout_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
997 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
998 DHCP6_CLIENT_DONT_DESTROY(client
);
1001 (void) event_source_disable(client
->timeout_expire
);
1002 (void) event_source_disable(client
->timeout_t2
);
1003 (void) event_source_disable(client
->timeout_t1
);
1005 state
= client
->state
;
1007 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
1009 /* RFC 3315, section 18.1.4., says that "...the client may choose to
1010 use a Solicit message to locate a new DHCP server..." */
1011 if (state
== DHCP6_STATE_REBIND
)
1012 (void) client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1017 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1018 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1020 (void) event_source_disable(client
->timeout_t2
);
1021 (void) event_source_disable(client
->timeout_t1
);
1023 log_dhcp6_client(client
, "Timeout T2");
1025 (void) client_start_transaction(client
, DHCP6_STATE_REBIND
);
1030 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1031 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1033 (void) event_source_disable(client
->timeout_t1
);
1035 log_dhcp6_client(client
, "Timeout T1");
1037 (void) client_start_transaction(client
, DHCP6_STATE_RENEW
);
1042 static int client_enter_bound_state(sd_dhcp6_client
*client
) {
1043 usec_t lifetime_t1
, lifetime_t2
, lifetime_valid
;
1047 assert(client
->lease
);
1048 assert(IN_SET(client
->state
,
1049 DHCP6_STATE_SOLICITATION
,
1050 DHCP6_STATE_REQUEST
,
1052 DHCP6_STATE_REBIND
));
1054 (void) event_source_disable(client
->receive_message
);
1055 (void) event_source_disable(client
->timeout_resend
);
1057 r
= sd_dhcp6_lease_get_t1(client
->lease
, &lifetime_t1
);
1061 r
= sd_dhcp6_lease_get_t2(client
->lease
, &lifetime_t2
);
1065 r
= sd_dhcp6_lease_get_valid_lifetime(client
->lease
, &lifetime_valid
);
1069 lifetime_t2
= client_timeout_compute_random(lifetime_t2
);
1070 lifetime_t1
= client_timeout_compute_random(MIN(lifetime_t1
, lifetime_t2
));
1072 if (lifetime_t1
== USEC_INFINITY
) {
1073 log_dhcp6_client(client
, "Infinite T1");
1074 event_source_disable(client
->timeout_t1
);
1076 log_dhcp6_client(client
, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1
, USEC_PER_SEC
));
1077 r
= event_reset_time_relative(client
->event
, &client
->timeout_t1
,
1079 lifetime_t1
, 10 * USEC_PER_SEC
,
1080 client_timeout_t1
, client
,
1081 client
->event_priority
, "dhcp6-t1-timeout", true);
1086 if (lifetime_t2
== USEC_INFINITY
) {
1087 log_dhcp6_client(client
, "Infinite T2");
1088 event_source_disable(client
->timeout_t2
);
1090 log_dhcp6_client(client
, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2
, USEC_PER_SEC
));
1091 r
= event_reset_time_relative(client
->event
, &client
->timeout_t2
,
1093 lifetime_t2
, 10 * USEC_PER_SEC
,
1094 client_timeout_t2
, client
,
1095 client
->event_priority
, "dhcp6-t2-timeout", true);
1100 if (lifetime_valid
== USEC_INFINITY
) {
1101 log_dhcp6_client(client
, "Infinite valid lifetime");
1102 event_source_disable(client
->timeout_expire
);
1104 log_dhcp6_client(client
, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid
, USEC_PER_SEC
));
1106 r
= event_reset_time_relative(client
->event
, &client
->timeout_expire
,
1108 lifetime_valid
, USEC_PER_SEC
,
1109 client_timeout_expire
, client
,
1110 client
->event_priority
, "dhcp6-lease-expire", true);
1115 client_set_state(client
, DHCP6_STATE_BOUND
);
1116 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1120 client_stop(client
, r
);
1124 static int log_invalid_message_type(sd_dhcp6_client
*client
, const DHCP6Message
*message
) {
1125 const char *type_str
;
1130 type_str
= dhcp6_message_type_to_string(message
->type
);
1132 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1133 "Received unexpected %s message, ignoring.", type_str
);
1135 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1136 "Received unsupported message type %u, ignoring.", message
->type
);
1139 static int client_process_information(
1140 sd_dhcp6_client
*client
,
1141 DHCP6Message
*message
,
1143 const triple_timestamp
*timestamp
,
1144 const struct in6_addr
*server_address
) {
1146 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1152 if (message
->type
!= DHCP6_MESSAGE_REPLY
)
1153 return log_invalid_message_type(client
, message
);
1155 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1157 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1159 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1161 sd_dhcp6_lease_unref(client
->lease
);
1162 client
->lease
= TAKE_PTR(lease
);
1164 /* Do not call client_stop() here, as it frees the acquired lease. */
1165 (void) event_source_disable(client
->receive_message
);
1166 (void) event_source_disable(client
->timeout_resend
);
1167 client_set_state(client
, DHCP6_STATE_STOPPED
);
1169 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1173 static int client_process_reply(
1174 sd_dhcp6_client
*client
,
1175 DHCP6Message
*message
,
1177 const triple_timestamp
*timestamp
,
1178 const struct in6_addr
*server_address
) {
1180 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1186 if (message
->type
!= DHCP6_MESSAGE_REPLY
)
1187 return log_invalid_message_type(client
, message
);
1189 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1190 if (r
== -EADDRNOTAVAIL
) {
1192 /* If NoBinding status code is received, we cannot request the address anymore.
1193 * Let's restart transaction from the beginning. */
1195 if (client
->state
== DHCP6_STATE_REQUEST
)
1196 /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
1197 client_cleanup(client
);
1199 /* We need to notify the previous lease was expired. */
1200 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
1202 return client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1205 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1207 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1209 sd_dhcp6_lease_unref(client
->lease
);
1210 client
->lease
= TAKE_PTR(lease
);
1212 return client_enter_bound_state(client
);
1215 static int client_process_advertise_or_rapid_commit_reply(
1216 sd_dhcp6_client
*client
,
1217 DHCP6Message
*message
,
1219 const triple_timestamp
*timestamp
,
1220 const struct in6_addr
*server_address
) {
1222 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1223 uint8_t pref_advertise
, pref_lease
= 0;
1229 if (!IN_SET(message
->type
, DHCP6_MESSAGE_ADVERTISE
, DHCP6_MESSAGE_REPLY
))
1230 return log_invalid_message_type(client
, message
);
1232 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1234 return log_dhcp6_client_errno(client
, r
, "Failed to process received %s message, ignoring: %m",
1235 dhcp6_message_type_to_string(message
->type
));
1237 if (message
->type
== DHCP6_MESSAGE_REPLY
) {
1240 if (!client
->rapid_commit
)
1241 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1242 "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
1244 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1249 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1250 "Received reply message without rapid commit flag, ignoring.");
1252 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1254 sd_dhcp6_lease_unref(client
->lease
);
1255 client
->lease
= TAKE_PTR(lease
);
1257 return client_enter_bound_state(client
);
1260 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1264 if (client
->lease
) {
1265 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1270 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1272 if (!client
->lease
|| pref_advertise
> pref_lease
) {
1273 /* If this is the first advertise message or has higher preference, then save the lease. */
1274 sd_dhcp6_lease_unref(client
->lease
);
1275 client
->lease
= TAKE_PTR(lease
);
1278 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1279 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
1284 static int client_receive_message(
1290 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1291 DHCP6_CLIENT_DONT_DESTROY(client
);
1292 /* This needs to be initialized with zero. See #20741. */
1293 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL
) control
= {};
1295 union sockaddr_union sa
= {};
1296 struct msghdr msg
= {
1298 .msg_namelen
= sizeof(sa
),
1301 .msg_control
= &control
,
1302 .msg_controllen
= sizeof(control
),
1305 _cleanup_free_ DHCP6Message
*message
= NULL
;
1306 struct in6_addr
*server_address
= NULL
;
1307 ssize_t buflen
, len
;
1309 buflen
= next_datagram_size_fd(fd
);
1310 if (ERRNO_IS_NEG_TRANSIENT(buflen
) || ERRNO_IS_NEG_DISCONNECT(buflen
))
1313 log_dhcp6_client_errno(client
, buflen
, "Failed to determine datagram size to read, ignoring: %m");
1317 message
= malloc(buflen
);
1321 iov
= IOVEC_MAKE(message
, buflen
);
1323 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
1324 if (ERRNO_IS_NEG_TRANSIENT(len
) || ERRNO_IS_NEG_DISCONNECT(len
))
1327 log_dhcp6_client_errno(client
, len
, "Could not receive message from UDP socket, ignoring: %m");
1330 if ((size_t) len
< sizeof(DHCP6Message
)) {
1331 log_dhcp6_client(client
, "Too small to be DHCPv6 message: ignoring");
1335 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1336 if (msg
.msg_namelen
> 0) {
1337 if (msg
.msg_namelen
!= sizeof(struct sockaddr_in6
) || sa
.in6
.sin6_family
!= AF_INET6
) {
1338 log_dhcp6_client(client
, "Received message from invalid source, ignoring.");
1342 server_address
= &sa
.in6
.sin6_addr
;
1345 triple_timestamp_from_cmsg(&t
, &msg
);
1347 if (client
->transaction_id
!= (message
->transaction_id
& htobe32(0x00ffffff)))
1350 switch (client
->state
) {
1351 case DHCP6_STATE_INFORMATION_REQUEST
:
1352 if (client_process_information(client
, message
, len
, &t
, server_address
) < 0)
1356 case DHCP6_STATE_SOLICITATION
:
1357 if (client_process_advertise_or_rapid_commit_reply(client
, message
, len
, &t
, server_address
) < 0)
1361 case DHCP6_STATE_REQUEST
:
1362 case DHCP6_STATE_RENEW
:
1363 case DHCP6_STATE_REBIND
:
1364 if (client_process_reply(client
, message
, len
, &t
, server_address
) < 0)
1368 case DHCP6_STATE_BOUND
:
1369 case DHCP6_STATE_STOPPED
:
1370 case DHCP6_STATE_STOPPING
:
1372 assert_not_reached();
1378 static int client_send_release(sd_dhcp6_client
*client
) {
1379 sd_dhcp6_lease
*lease
;
1383 if (!client
->send_release
)
1386 if (sd_dhcp6_client_get_lease(client
, &lease
) < 0)
1389 if (!lease
->ia_na
&& !lease
->ia_pd
)
1392 client_set_state(client
, DHCP6_STATE_STOPPING
);
1393 return dhcp6_client_send_message(client
);
1396 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1402 /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
1403 * engine is about to release its UDP socket unconditionally. */
1404 r
= client_send_release(client
);
1406 log_dhcp6_client_errno(client
, r
,
1407 "Failed to send DHCPv6 release message, ignoring: %m");
1409 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1411 client
->receive_message
= sd_event_source_unref(client
->receive_message
);
1412 client
->fd
= safe_close(client
->fd
);
1417 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1421 return client
->state
!= DHCP6_STATE_STOPPED
;
1424 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1425 DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1428 assert_return(client
, -EINVAL
);
1429 assert_return(client
->event
, -EINVAL
);
1430 assert_return(client
->ifindex
> 0, -EINVAL
);
1431 assert_return(in6_addr_is_link_local(&client
->local_address
) > 0, -EINVAL
);
1432 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1433 assert_return(client
->information_request
|| client
->request_ia
!= 0, -EINVAL
);
1435 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1436 * request may be stored. */
1437 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
1439 r
= client_ensure_iaid(client
);
1443 r
= client_ensure_duid(client
);
1447 if (client
->fd
< 0) {
1448 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1450 return log_dhcp6_client_errno(client
, r
,
1451 "Failed to bind to UDP socket at address %s: %m",
1452 IN6_ADDR_TO_STRING(&client
->local_address
));
1457 if (!client
->receive_message
) {
1458 _cleanup_(sd_event_source_disable_unrefp
) sd_event_source
*s
= NULL
;
1460 r
= sd_event_add_io(client
->event
, &s
, client
->fd
, EPOLLIN
, client_receive_message
, client
);
1464 r
= sd_event_source_set_priority(s
, client
->event_priority
);
1468 r
= sd_event_source_set_description(s
, "dhcp6-receive-message");
1472 client
->receive_message
= TAKE_PTR(s
);
1475 if (client
->information_request
) {
1476 usec_t t
= now(CLOCK_MONOTONIC
);
1478 if (t
< usec_add(client
->information_request_time_usec
, client
->information_refresh_time_usec
))
1481 client
->information_request_time_usec
= t
;
1482 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1485 log_dhcp6_client(client
, "Starting in %s mode",
1486 client
->information_request
? "Information request" : "Solicit");
1488 return client_start_transaction(client
, state
);
1491 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1494 assert_return(client
, -EINVAL
);
1495 assert_return(!client
->event
, -EBUSY
);
1496 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1499 client
->event
= sd_event_ref(event
);
1501 r
= sd_event_default(&client
->event
);
1506 client
->event_priority
= priority
;
1511 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1512 assert_return(client
, -EINVAL
);
1513 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1515 client
->event
= sd_event_unref(client
->event
);
1520 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1521 assert_return(client
, NULL
);
1523 return client
->event
;
1526 int sd_dhcp6_client_attach_device(sd_dhcp6_client
*client
, sd_device
*dev
) {
1527 assert_return(client
, -EINVAL
);
1529 return device_unref_and_replace(client
->dev
, dev
);
1532 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1536 sd_dhcp6_lease_unref(client
->lease
);
1538 sd_event_source_disable_unref(client
->receive_message
);
1539 sd_event_source_disable_unref(client
->timeout_resend
);
1540 sd_event_source_disable_unref(client
->timeout_expire
);
1541 sd_event_source_disable_unref(client
->timeout_t1
);
1542 sd_event_source_disable_unref(client
->timeout_t2
);
1543 sd_event_unref(client
->event
);
1545 client
->fd
= safe_close(client
->fd
);
1547 sd_device_unref(client
->dev
);
1549 free(client
->req_opts
);
1551 free(client
->mudurl
);
1552 dhcp6_ia_clear_addresses(&client
->ia_pd
);
1553 ordered_hashmap_free(client
->extra_options
);
1554 ordered_set_free(client
->vendor_options
);
1555 strv_free(client
->user_class
);
1556 strv_free(client
->vendor_class
);
1557 free(client
->ifname
);
1559 return mfree(client
);
1562 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1564 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1565 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1567 assert_return(ret
, -EINVAL
);
1569 client
= new(sd_dhcp6_client
, 1);
1573 *client
= (sd_dhcp6_client
) {
1575 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1576 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1578 .request_ia
= DHCP6_REQUEST_IA_NA
| DHCP6_REQUEST_IA_PD
,
1580 .rapid_commit
= true,
1583 *ret
= TAKE_PTR(client
);