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"
24 #include "iovec-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 dhcp6_client_set_state_callback(
50 sd_dhcp6_client
*client
,
51 sd_dhcp6_client_callback_t cb
,
54 assert_return(client
, -EINVAL
);
56 client
->state_callback
= cb
;
57 client
->state_userdata
= userdata
;
62 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
63 assert_return(client
, -EINVAL
);
64 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
65 assert_return(ifindex
> 0, -EINVAL
);
67 client
->ifindex
= ifindex
;
71 int sd_dhcp6_client_set_ifname(sd_dhcp6_client
*client
, const char *ifname
) {
72 assert_return(client
, -EINVAL
);
73 assert_return(ifname
, -EINVAL
);
75 if (!ifname_valid_full(ifname
, IFNAME_VALID_ALTERNATIVE
))
78 return free_and_strdup(&client
->ifname
, ifname
);
81 int sd_dhcp6_client_get_ifname(sd_dhcp6_client
*client
, const char **ret
) {
84 assert_return(client
, -EINVAL
);
86 r
= get_ifname(client
->ifindex
, &client
->ifname
);
91 *ret
= client
->ifname
;
96 int sd_dhcp6_client_set_local_address(
97 sd_dhcp6_client
*client
,
98 const struct in6_addr
*local_address
) {
100 assert_return(client
, -EINVAL
);
101 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
102 assert_return(local_address
, -EINVAL
);
103 assert_return(in6_addr_is_link_local(local_address
) > 0, -EINVAL
);
105 client
->local_address
= *local_address
;
110 int sd_dhcp6_client_set_mac(
111 sd_dhcp6_client
*client
,
116 assert_return(client
, -EINVAL
);
117 assert_return(addr
, -EINVAL
);
118 assert_return(addr_len
<= sizeof(client
->hw_addr
.bytes
), -EINVAL
);
120 /* Unlike the other setters, it is OK to set a new MAC address while the client is running,
121 * as the MAC address is used only when setting DUID or IAID. */
123 if (arp_type
== ARPHRD_ETHER
)
124 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
125 else if (arp_type
== ARPHRD_INFINIBAND
)
126 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
128 client
->arp_type
= ARPHRD_NONE
;
129 client
->hw_addr
.length
= 0;
133 client
->arp_type
= arp_type
;
134 hw_addr_set(&client
->hw_addr
, addr
, addr_len
);
139 int sd_dhcp6_client_set_prefix_delegation_hint(
140 sd_dhcp6_client
*client
,
142 const struct in6_addr
*pd_prefix
) {
144 _cleanup_free_ DHCP6Address
*prefix
= NULL
;
146 assert_return(client
, -EINVAL
);
147 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
150 /* clear previous assignments. */
151 dhcp6_ia_clear_addresses(&client
->ia_pd
);
155 assert_return(prefixlen
> 0 && prefixlen
<= 128, -EINVAL
);
157 prefix
= new(DHCP6Address
, 1);
161 *prefix
= (DHCP6Address
) {
162 .iapdprefix
.address
= *pd_prefix
,
163 .iapdprefix
.prefixlen
= prefixlen
,
166 LIST_PREPEND(addresses
, client
->ia_pd
.addresses
, TAKE_PTR(prefix
));
170 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
173 assert_return(client
, -EINVAL
);
174 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
177 /* Clear the previous assignments. */
178 ordered_set_clear(client
->vendor_options
);
182 r
= ordered_set_ensure_put(&client
->vendor_options
, &dhcp6_option_hash_ops
, v
);
186 sd_dhcp6_option_ref(v
);
191 static int client_ensure_duid(sd_dhcp6_client
*client
) {
194 if (client
->duid_len
!= 0)
197 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
201 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
202 * without further modification. Otherwise, if duid_type is supported, DUID
203 * is set based on that type. Otherwise, an error is returned.
205 int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client
*client
, uint64_t llt_time
) {
208 assert_return(client
, -EINVAL
);
209 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
211 r
= dhcp_identifier_set_duid_llt(&client
->hw_addr
, client
->arp_type
, llt_time
, &client
->duid
, &client
->duid_len
);
213 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LLT: %m");
218 int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client
*client
) {
221 assert_return(client
, -EINVAL
);
222 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
224 r
= dhcp_identifier_set_duid_ll(&client
->hw_addr
, client
->arp_type
, &client
->duid
, &client
->duid_len
);
226 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LL: %m");
231 int sd_dhcp6_client_set_duid_en(sd_dhcp6_client
*client
) {
234 assert_return(client
, -EINVAL
);
235 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
237 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
239 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-EN: %m");
244 int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client
*client
) {
247 assert_return(client
, -EINVAL
);
248 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
250 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
252 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-UUID: %m");
257 int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client
*client
, uint16_t duid_type
, const uint8_t *duid
, size_t duid_len
) {
260 assert_return(client
, -EINVAL
);
261 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
262 assert_return(duid
|| duid_len
== 0, -EINVAL
);
264 r
= dhcp_identifier_set_duid_raw(duid_type
, duid
, duid_len
, &client
->duid
, &client
->duid_len
);
266 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID: %m");
271 int sd_dhcp6_client_duid_as_string(
272 sd_dhcp6_client
*client
,
274 _cleanup_free_
char *p
= NULL
, *s
= NULL
, *t
= NULL
;
278 assert_return(client
, -EINVAL
);
279 assert_return(client
->duid_len
> offsetof(struct duid
, raw
.data
), -ENODATA
);
280 assert_return(duid
, -EINVAL
);
282 v
= duid_type_to_string(be16toh(client
->duid
.type
));
288 r
= asprintf(&s
, "%0x", client
->duid
.type
);
293 t
= hexmem(client
->duid
.raw
.data
, client
->duid_len
- offsetof(struct duid
, raw
.data
));
297 p
= strjoin(s
, ":", t
);
306 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
307 assert_return(client
, -EINVAL
);
308 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
310 client
->ia_na
.header
.id
= htobe32(iaid
);
311 client
->ia_pd
.header
.id
= htobe32(iaid
);
312 client
->iaid_set
= true;
317 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
323 if (client
->iaid_set
)
326 r
= dhcp_identifier_set_iaid(client
->dev
, &client
->hw_addr
,
327 /* legacy_unstable_byteorder = */ true,
332 client
->ia_na
.header
.id
= iaid
;
333 client
->ia_pd
.header
.id
= iaid
;
334 client
->iaid_set
= true;
339 int sd_dhcp6_client_get_iaid(sd_dhcp6_client
*client
, uint32_t *iaid
) {
340 assert_return(client
, -EINVAL
);
341 assert_return(iaid
, -EINVAL
);
343 if (!client
->iaid_set
)
346 *iaid
= be32toh(client
->ia_na
.header
.id
);
351 int sd_dhcp6_client_set_fqdn(
352 sd_dhcp6_client
*client
,
355 assert_return(client
, -EINVAL
);
356 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
358 /* Make sure FQDN qualifies as DNS and as Linux hostname */
360 !(hostname_is_valid(fqdn
, 0) && dns_name_is_valid(fqdn
) > 0))
363 return free_and_strdup(&client
->fqdn
, fqdn
);
366 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
367 assert_return(client
, -EINVAL
);
368 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
370 client
->information_request
= enabled
;
375 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
376 assert_return(client
, -EINVAL
);
377 assert_return(enabled
, -EINVAL
);
379 *enabled
= client
->information_request
;
384 static int be16_compare_func(const be16_t
*a
, const be16_t
*b
) {
385 return CMP(be16toh(*a
), be16toh(*b
));
388 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
391 assert_return(client
, -EINVAL
);
392 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
394 if (!dhcp6_option_can_request(option
))
397 opt
= htobe16(option
);
398 if (typesafe_bsearch(&opt
, client
->req_opts
, client
->n_req_opts
, be16_compare_func
))
401 if (!GREEDY_REALLOC(client
->req_opts
, client
->n_req_opts
+ 1))
404 client
->req_opts
[client
->n_req_opts
++] = opt
;
406 /* Sort immediately to make the above binary search will work for the next time. */
407 typesafe_qsort(client
->req_opts
, client
->n_req_opts
, be16_compare_func
);
411 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client
*client
, const char *mudurl
) {
412 assert_return(client
, -EINVAL
);
413 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
414 assert_return(mudurl
, -EINVAL
);
415 assert_return(strlen(mudurl
) <= UINT8_MAX
, -EINVAL
);
416 assert_return(http_url_is_valid(mudurl
), -EINVAL
);
418 return free_and_strdup(&client
->mudurl
, mudurl
);
421 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client
*client
, char * const *user_class
) {
424 assert_return(client
, -EINVAL
);
425 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
426 assert_return(!strv_isempty(user_class
), -EINVAL
);
428 STRV_FOREACH(p
, user_class
) {
429 size_t len
= strlen(*p
);
431 if (len
> UINT16_MAX
|| len
== 0)
435 s
= strv_copy(user_class
);
439 return strv_free_and_replace(client
->user_class
, s
);
442 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client
*client
, char * const *vendor_class
) {
445 assert_return(client
, -EINVAL
);
446 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
447 assert_return(!strv_isempty(vendor_class
), -EINVAL
);
449 STRV_FOREACH(p
, vendor_class
) {
450 size_t len
= strlen(*p
);
452 if (len
> UINT16_MAX
|| len
== 0)
456 s
= strv_copy(vendor_class
);
460 return strv_free_and_replace(client
->vendor_class
, s
);
463 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
464 assert_return(client
, -EINVAL
);
465 assert_return(delegation
, -EINVAL
);
467 *delegation
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
);
472 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
473 assert_return(client
, -EINVAL
);
474 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
476 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_PD
, delegation
);
481 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
482 assert_return(client
, -EINVAL
);
483 assert_return(request
, -EINVAL
);
485 *request
= FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
);
490 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
491 assert_return(client
, -EINVAL
);
492 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
494 SET_FLAG(client
->request_ia
, DHCP6_REQUEST_IA_NA
, request
);
499 int dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
501 assert_se(network_test_mode_enabled());
503 /* This is for tests or fuzzers. */
505 client
->transaction_id
= transaction_id
& htobe32(0x00ffffff);
510 int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client
*client
, int enable
) {
511 assert_return(client
, -EINVAL
);
512 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
514 client
->rapid_commit
= enable
;
518 int sd_dhcp6_client_set_send_release(sd_dhcp6_client
*client
, int enable
) {
519 assert_return(client
, -EINVAL
);
520 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
522 client
->send_release
= enable
;
526 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
527 assert_return(client
, -EINVAL
);
533 *ret
= client
->lease
;
538 int sd_dhcp6_client_add_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
541 assert_return(client
, -EINVAL
);
542 assert_return(v
, -EINVAL
);
544 r
= ordered_hashmap_ensure_put(&client
->extra_options
, &dhcp6_option_hash_ops
, UINT_TO_PTR(v
->option
), v
);
548 sd_dhcp6_option_ref(v
);
552 static void client_set_state(sd_dhcp6_client
*client
, DHCP6State state
) {
555 if (client
->state
== state
)
558 log_dhcp6_client(client
, "State changed: %s -> %s",
559 dhcp6_state_to_string(client
->state
), dhcp6_state_to_string(state
));
561 client
->state
= state
;
563 if (client
->state_callback
)
564 client
->state_callback(client
, state
, client
->state_userdata
);
567 int dhcp6_client_get_state(sd_dhcp6_client
*client
) {
568 assert_return(client
, -EINVAL
);
570 return client
->state
;
573 static void client_notify(sd_dhcp6_client
*client
, int event
) {
576 if (client
->callback
)
577 client
->callback(client
, event
, client
->userdata
);
580 static void client_cleanup(sd_dhcp6_client
*client
) {
583 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
585 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
586 * even though the lease is freed below. */
587 client
->information_request_time_usec
= 0;
588 client
->information_refresh_time_usec
= 0;
590 (void) event_source_disable(client
->receive_message
);
591 (void) event_source_disable(client
->timeout_resend
);
592 (void) event_source_disable(client
->timeout_expire
);
593 (void) event_source_disable(client
->timeout_t1
);
594 (void) event_source_disable(client
->timeout_t2
);
596 client_set_state(client
, DHCP6_STATE_STOPPED
);
599 static void client_stop(sd_dhcp6_client
*client
, int error
) {
600 DHCP6_CLIENT_DONT_DESTROY(client
);
604 client_notify(client
, error
);
606 client_cleanup(client
);
609 static int client_append_common_options_in_managed_mode(
610 sd_dhcp6_client
*client
,
613 const DHCP6IA
*ia_na
,
614 const DHCP6IA
*ia_pd
) {
619 assert(IN_SET(client
->state
,
620 DHCP6_STATE_SOLICITATION
,
624 DHCP6_STATE_STOPPING
));
629 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_NA
) && ia_na
) {
630 r
= dhcp6_option_append_ia(buf
, offset
, ia_na
);
635 if (FLAGS_SET(client
->request_ia
, DHCP6_REQUEST_IA_PD
) && ia_pd
) {
636 r
= dhcp6_option_append_ia(buf
, offset
, ia_pd
);
641 if (client
->state
!= DHCP6_STATE_STOPPING
) {
642 r
= dhcp6_option_append_fqdn(buf
, offset
, client
->fqdn
);
647 r
= dhcp6_option_append_user_class(buf
, offset
, client
->user_class
);
651 r
= dhcp6_option_append_vendor_class(buf
, offset
, client
->vendor_class
);
655 r
= dhcp6_option_append_vendor_option(buf
, offset
, client
->vendor_options
);
662 static DHCP6MessageType
client_message_type_from_state(sd_dhcp6_client
*client
) {
665 switch (client
->state
) {
666 case DHCP6_STATE_INFORMATION_REQUEST
:
667 return DHCP6_MESSAGE_INFORMATION_REQUEST
;
668 case DHCP6_STATE_SOLICITATION
:
669 return DHCP6_MESSAGE_SOLICIT
;
670 case DHCP6_STATE_REQUEST
:
671 return DHCP6_MESSAGE_REQUEST
;
672 case DHCP6_STATE_RENEW
:
673 return DHCP6_MESSAGE_RENEW
;
674 case DHCP6_STATE_REBIND
:
675 return DHCP6_MESSAGE_REBIND
;
676 case DHCP6_STATE_STOPPING
:
677 return DHCP6_MESSAGE_RELEASE
;
679 assert_not_reached();
683 static int client_append_oro(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
684 _cleanup_free_ be16_t
*p
= NULL
;
693 switch (client
->state
) {
694 case DHCP6_STATE_INFORMATION_REQUEST
:
695 n
= client
->n_req_opts
;
696 p
= new(be16_t
, n
+ 2);
700 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
701 p
[n
++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
); /* RFC 8415 section 21.23 */
702 p
[n
++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT
); /* RFC 8415 section 21.25 */
704 typesafe_qsort(p
, n
, be16_compare_func
);
708 case DHCP6_STATE_SOLICITATION
:
709 n
= client
->n_req_opts
;
710 p
= new(be16_t
, n
+ 1);
714 memcpy_safe(p
, client
->req_opts
, n
* sizeof(be16_t
));
715 p
[n
++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT
); /* RFC 8415 section 21.24 */
717 typesafe_qsort(p
, n
, be16_compare_func
);
721 case DHCP6_STATE_STOPPING
:
725 n
= client
->n_req_opts
;
726 req_opts
= client
->req_opts
;
732 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_ORO
, n
* sizeof(be16_t
), req_opts
);
735 static int client_append_mudurl(sd_dhcp6_client
*client
, uint8_t **buf
, size_t *offset
) {
744 if (client
->state
== DHCP6_STATE_STOPPING
)
747 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_MUD_URL_V6
,
748 strlen(client
->mudurl
), client
->mudurl
);
751 int dhcp6_client_send_message(sd_dhcp6_client
*client
) {
752 _cleanup_free_
uint8_t *buf
= NULL
;
753 struct in6_addr all_servers
=
754 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
755 struct sd_dhcp6_option
*j
;
756 usec_t elapsed_usec
, time_now
;
758 DHCP6Message
*message
;
763 assert(client
->event
);
765 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &time_now
);
769 if (!GREEDY_REALLOC0(buf
, offsetof(DHCP6Message
, options
)))
772 message
= (DHCP6Message
*) buf
;
773 message
->transaction_id
= client
->transaction_id
;
774 message
->type
= client_message_type_from_state(client
);
775 offset
= offsetof(DHCP6Message
, options
);
777 switch (client
->state
) {
778 case DHCP6_STATE_INFORMATION_REQUEST
:
781 case DHCP6_STATE_SOLICITATION
:
782 if (client
->rapid_commit
) {
783 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
788 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
789 &client
->ia_na
, &client
->ia_pd
);
794 case DHCP6_STATE_REQUEST
:
795 case DHCP6_STATE_RENEW
:
796 case DHCP6_STATE_STOPPING
:
797 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_SERVERID
,
798 client
->lease
->serverid_len
,
799 client
->lease
->serverid
);
804 case DHCP6_STATE_REBIND
:
806 assert(client
->lease
);
808 r
= client_append_common_options_in_managed_mode(client
, &buf
, &offset
,
809 client
->lease
->ia_na
, client
->lease
->ia_pd
);
814 case DHCP6_STATE_BOUND
:
815 case DHCP6_STATE_STOPPED
:
817 assert_not_reached();
820 r
= client_append_mudurl(client
, &buf
, &offset
);
824 r
= client_append_oro(client
, &buf
, &offset
);
828 assert(client
->duid_len
> 0);
829 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_CLIENTID
,
830 client
->duid_len
, &client
->duid
);
834 ORDERED_HASHMAP_FOREACH(j
, client
->extra_options
) {
835 r
= dhcp6_option_append(&buf
, &offset
, j
->option
, j
->length
, j
->data
);
840 /* RFC 8415 Section 21.9.
841 * A client MUST include an Elapsed Time option in messages to indicate how long the client has
842 * been trying to complete a DHCP message exchange. */
843 elapsed_usec
= MIN(usec_sub_unsigned(time_now
, client
->transaction_start
) / USEC_PER_MSEC
/ 10, (usec_t
) UINT16_MAX
);
844 elapsed_time
= htobe16(elapsed_usec
);
845 r
= dhcp6_option_append(&buf
, &offset
, SD_DHCP6_OPTION_ELAPSED_TIME
, sizeof(elapsed_time
), &elapsed_time
);
849 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, buf
, offset
);
853 log_dhcp6_client(client
, "Sent %s",
854 dhcp6_message_type_to_string(client_message_type_from_state(client
)));
858 static usec_t
client_timeout_compute_random(usec_t val
) {
859 return usec_sub_unsigned(val
, random_u64_range(val
/ 10));
862 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
863 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
864 usec_t init_retransmit_time
, max_retransmit_time
;
867 assert(client
->event
);
869 switch (client
->state
) {
870 case DHCP6_STATE_INFORMATION_REQUEST
:
871 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
872 max_retransmit_time
= DHCP6_INF_MAX_RT
;
875 case DHCP6_STATE_SOLICITATION
:
877 if (client
->retransmit_count
> 0 && client
->lease
) {
878 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
882 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
883 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
886 case DHCP6_STATE_REQUEST
:
888 if (client
->retransmit_count
>= DHCP6_REQ_MAX_RC
) {
889 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
893 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
894 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
897 case DHCP6_STATE_RENEW
:
898 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
899 max_retransmit_time
= DHCP6_REN_MAX_RT
;
901 /* RFC 3315, section 18.1.3. says max retransmit duration will
902 be the remaining time until T2. Instead of setting MRD,
903 wait for T2 to trigger with the same end result */
906 case DHCP6_STATE_REBIND
:
907 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
908 max_retransmit_time
= DHCP6_REB_MAX_RT
;
910 /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */
913 case DHCP6_STATE_STOPPED
:
914 case DHCP6_STATE_STOPPING
:
915 case DHCP6_STATE_BOUND
:
917 assert_not_reached();
920 r
= dhcp6_client_send_message(client
);
922 client
->retransmit_count
++;
924 if (client
->retransmit_time
== 0) {
925 client
->retransmit_time
= client_timeout_compute_random(init_retransmit_time
);
927 if (client
->state
== DHCP6_STATE_SOLICITATION
)
928 client
->retransmit_time
+= init_retransmit_time
/ 10;
930 } else if (client
->retransmit_time
> max_retransmit_time
/ 2)
931 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
933 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
935 log_dhcp6_client(client
, "Next retransmission in %s",
936 FORMAT_TIMESPAN(client
->retransmit_time
, USEC_PER_SEC
));
938 r
= event_reset_time_relative(client
->event
, &client
->timeout_resend
,
940 client
->retransmit_time
, 10 * USEC_PER_MSEC
,
941 client_timeout_resend
, client
,
942 client
->event_priority
, "dhcp6-resend-timer", true);
944 client_stop(client
, r
);
949 static int client_start_transaction(sd_dhcp6_client
*client
, DHCP6State state
) {
953 assert(client
->event
);
956 case DHCP6_STATE_INFORMATION_REQUEST
:
957 case DHCP6_STATE_SOLICITATION
:
958 assert(client
->state
== DHCP6_STATE_STOPPED
);
960 case DHCP6_STATE_REQUEST
:
961 assert(client
->state
== DHCP6_STATE_SOLICITATION
);
963 case DHCP6_STATE_RENEW
:
964 assert(client
->state
== DHCP6_STATE_BOUND
);
966 case DHCP6_STATE_REBIND
:
967 assert(IN_SET(client
->state
, DHCP6_STATE_BOUND
, DHCP6_STATE_RENEW
));
969 case DHCP6_STATE_STOPPED
:
970 case DHCP6_STATE_STOPPING
:
971 case DHCP6_STATE_BOUND
:
973 assert_not_reached();
976 client_set_state(client
, state
);
978 client
->retransmit_time
= 0;
979 client
->retransmit_count
= 0;
980 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
982 r
= sd_event_now(client
->event
, CLOCK_BOOTTIME
, &client
->transaction_start
);
986 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
989 client_timeout_resend
, client
,
990 client
->event_priority
, "dhcp6-resend-timeout", true);
994 r
= sd_event_source_set_enabled(client
->receive_message
, SD_EVENT_ON
);
1001 client_stop(client
, r
);
1005 static int client_timeout_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1006 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1007 DHCP6_CLIENT_DONT_DESTROY(client
);
1010 (void) event_source_disable(client
->timeout_expire
);
1011 (void) event_source_disable(client
->timeout_t2
);
1012 (void) event_source_disable(client
->timeout_t1
);
1014 state
= client
->state
;
1016 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
1018 /* RFC 3315, section 18.1.4., says that "...the client may choose to
1019 use a Solicit message to locate a new DHCP server..." */
1020 if (state
== DHCP6_STATE_REBIND
)
1021 (void) client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1026 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1027 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1029 (void) event_source_disable(client
->timeout_t2
);
1030 (void) event_source_disable(client
->timeout_t1
);
1032 log_dhcp6_client(client
, "Timeout T2");
1034 (void) client_start_transaction(client
, DHCP6_STATE_REBIND
);
1039 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
1040 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1042 (void) event_source_disable(client
->timeout_t1
);
1044 log_dhcp6_client(client
, "Timeout T1");
1046 (void) client_start_transaction(client
, DHCP6_STATE_RENEW
);
1051 static int client_enter_bound_state(sd_dhcp6_client
*client
) {
1052 usec_t lifetime_t1
, lifetime_t2
, lifetime_valid
;
1056 assert(client
->lease
);
1057 assert(IN_SET(client
->state
,
1058 DHCP6_STATE_SOLICITATION
,
1059 DHCP6_STATE_REQUEST
,
1061 DHCP6_STATE_REBIND
));
1063 (void) event_source_disable(client
->receive_message
);
1064 (void) event_source_disable(client
->timeout_resend
);
1066 r
= sd_dhcp6_lease_get_t1(client
->lease
, &lifetime_t1
);
1070 r
= sd_dhcp6_lease_get_t2(client
->lease
, &lifetime_t2
);
1074 r
= sd_dhcp6_lease_get_valid_lifetime(client
->lease
, &lifetime_valid
);
1078 lifetime_t2
= client_timeout_compute_random(lifetime_t2
);
1079 lifetime_t1
= client_timeout_compute_random(MIN(lifetime_t1
, lifetime_t2
));
1081 if (lifetime_t1
== USEC_INFINITY
) {
1082 log_dhcp6_client(client
, "Infinite T1");
1083 event_source_disable(client
->timeout_t1
);
1085 log_dhcp6_client(client
, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1
, USEC_PER_SEC
));
1086 r
= event_reset_time_relative(client
->event
, &client
->timeout_t1
,
1088 lifetime_t1
, 10 * USEC_PER_SEC
,
1089 client_timeout_t1
, client
,
1090 client
->event_priority
, "dhcp6-t1-timeout", true);
1095 if (lifetime_t2
== USEC_INFINITY
) {
1096 log_dhcp6_client(client
, "Infinite T2");
1097 event_source_disable(client
->timeout_t2
);
1099 log_dhcp6_client(client
, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2
, USEC_PER_SEC
));
1100 r
= event_reset_time_relative(client
->event
, &client
->timeout_t2
,
1102 lifetime_t2
, 10 * USEC_PER_SEC
,
1103 client_timeout_t2
, client
,
1104 client
->event_priority
, "dhcp6-t2-timeout", true);
1109 if (lifetime_valid
== USEC_INFINITY
) {
1110 log_dhcp6_client(client
, "Infinite valid lifetime");
1111 event_source_disable(client
->timeout_expire
);
1113 log_dhcp6_client(client
, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid
, USEC_PER_SEC
));
1115 r
= event_reset_time_relative(client
->event
, &client
->timeout_expire
,
1117 lifetime_valid
, USEC_PER_SEC
,
1118 client_timeout_expire
, client
,
1119 client
->event_priority
, "dhcp6-lease-expire", true);
1124 client_set_state(client
, DHCP6_STATE_BOUND
);
1125 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1129 client_stop(client
, r
);
1133 static int log_invalid_message_type(sd_dhcp6_client
*client
, const DHCP6Message
*message
) {
1134 const char *type_str
;
1139 type_str
= dhcp6_message_type_to_string(message
->type
);
1141 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1142 "Received unexpected %s message, ignoring.", type_str
);
1144 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1145 "Received unsupported message type %u, ignoring.", message
->type
);
1148 static int client_process_information(
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
);
1166 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1168 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1170 sd_dhcp6_lease_unref(client
->lease
);
1171 client
->lease
= TAKE_PTR(lease
);
1173 /* Do not call client_stop() here, as it frees the acquired lease. */
1174 (void) event_source_disable(client
->receive_message
);
1175 (void) event_source_disable(client
->timeout_resend
);
1176 client_set_state(client
, DHCP6_STATE_STOPPED
);
1178 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1182 static int client_process_reply(
1183 sd_dhcp6_client
*client
,
1184 DHCP6Message
*message
,
1186 const triple_timestamp
*timestamp
,
1187 const struct in6_addr
*server_address
) {
1189 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1195 if (message
->type
!= DHCP6_MESSAGE_REPLY
)
1196 return log_invalid_message_type(client
, message
);
1198 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1199 if (r
== -EADDRNOTAVAIL
) {
1201 /* If NoBinding status code is received, we cannot request the address anymore.
1202 * Let's restart transaction from the beginning. */
1204 if (client
->state
== DHCP6_STATE_REQUEST
)
1205 /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
1206 client_cleanup(client
);
1208 /* We need to notify the previous lease was expired. */
1209 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
1211 return client_start_transaction(client
, DHCP6_STATE_SOLICITATION
);
1214 return log_dhcp6_client_errno(client
, r
, "Failed to process received reply message, ignoring: %m");
1216 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1218 sd_dhcp6_lease_unref(client
->lease
);
1219 client
->lease
= TAKE_PTR(lease
);
1221 return client_enter_bound_state(client
);
1224 static int client_process_advertise_or_rapid_commit_reply(
1225 sd_dhcp6_client
*client
,
1226 DHCP6Message
*message
,
1228 const triple_timestamp
*timestamp
,
1229 const struct in6_addr
*server_address
) {
1231 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1232 uint8_t pref_advertise
, pref_lease
= 0;
1238 if (!IN_SET(message
->type
, DHCP6_MESSAGE_ADVERTISE
, DHCP6_MESSAGE_REPLY
))
1239 return log_invalid_message_type(client
, message
);
1241 r
= dhcp6_lease_new_from_message(client
, message
, len
, timestamp
, server_address
, &lease
);
1243 return log_dhcp6_client_errno(client
, r
, "Failed to process received %s message, ignoring: %m",
1244 dhcp6_message_type_to_string(message
->type
));
1246 if (message
->type
== DHCP6_MESSAGE_REPLY
) {
1249 if (!client
->rapid_commit
)
1250 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1251 "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
1253 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1258 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
1259 "Received reply message without rapid commit flag, ignoring.");
1261 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1263 sd_dhcp6_lease_unref(client
->lease
);
1264 client
->lease
= TAKE_PTR(lease
);
1266 return client_enter_bound_state(client
);
1269 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1273 if (client
->lease
) {
1274 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1279 log_dhcp6_client(client
, "Processed %s message", dhcp6_message_type_to_string(message
->type
));
1281 if (!client
->lease
|| pref_advertise
> pref_lease
) {
1282 /* If this is the first advertise message or has higher preference, then save the lease. */
1283 sd_dhcp6_lease_unref(client
->lease
);
1284 client
->lease
= TAKE_PTR(lease
);
1287 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1288 (void) client_start_transaction(client
, DHCP6_STATE_REQUEST
);
1293 static int client_receive_message(
1299 sd_dhcp6_client
*client
= ASSERT_PTR(userdata
);
1300 DHCP6_CLIENT_DONT_DESTROY(client
);
1301 /* This needs to be initialized with zero. See #20741. */
1302 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL
) control
= {};
1304 union sockaddr_union sa
= {};
1305 struct msghdr msg
= {
1307 .msg_namelen
= sizeof(sa
),
1310 .msg_control
= &control
,
1311 .msg_controllen
= sizeof(control
),
1314 _cleanup_free_ DHCP6Message
*message
= NULL
;
1315 struct in6_addr
*server_address
= NULL
;
1316 ssize_t buflen
, len
;
1318 buflen
= next_datagram_size_fd(fd
);
1319 if (ERRNO_IS_NEG_TRANSIENT(buflen
) || ERRNO_IS_NEG_DISCONNECT(buflen
))
1322 log_dhcp6_client_errno(client
, buflen
, "Failed to determine datagram size to read, ignoring: %m");
1326 message
= malloc(buflen
);
1330 iov
= IOVEC_MAKE(message
, buflen
);
1332 len
= recvmsg_safe(fd
, &msg
, MSG_DONTWAIT
);
1333 if (ERRNO_IS_NEG_TRANSIENT(len
) || ERRNO_IS_NEG_DISCONNECT(len
))
1336 log_dhcp6_client_errno(client
, len
, "Could not receive message from UDP socket, ignoring: %m");
1339 if ((size_t) len
< sizeof(DHCP6Message
)) {
1340 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1344 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1345 if (msg
.msg_namelen
> 0) {
1346 if (msg
.msg_namelen
!= sizeof(struct sockaddr_in6
) || sa
.in6
.sin6_family
!= AF_INET6
) {
1347 log_dhcp6_client(client
, "Received message from invalid source, ignoring.");
1351 server_address
= &sa
.in6
.sin6_addr
;
1354 triple_timestamp_from_cmsg(&t
, &msg
);
1356 if (client
->transaction_id
!= (message
->transaction_id
& htobe32(0x00ffffff)))
1359 switch (client
->state
) {
1360 case DHCP6_STATE_INFORMATION_REQUEST
:
1361 if (client_process_information(client
, message
, len
, &t
, server_address
) < 0)
1365 case DHCP6_STATE_SOLICITATION
:
1366 if (client_process_advertise_or_rapid_commit_reply(client
, message
, len
, &t
, server_address
) < 0)
1370 case DHCP6_STATE_REQUEST
:
1371 case DHCP6_STATE_RENEW
:
1372 case DHCP6_STATE_REBIND
:
1373 if (client_process_reply(client
, message
, len
, &t
, server_address
) < 0)
1377 case DHCP6_STATE_BOUND
:
1378 case DHCP6_STATE_STOPPED
:
1379 case DHCP6_STATE_STOPPING
:
1381 assert_not_reached();
1387 static int client_send_release(sd_dhcp6_client
*client
) {
1388 sd_dhcp6_lease
*lease
;
1392 if (!client
->send_release
)
1395 if (sd_dhcp6_client_get_lease(client
, &lease
) < 0)
1398 if (!lease
->ia_na
&& !lease
->ia_pd
)
1401 client_set_state(client
, DHCP6_STATE_STOPPING
);
1402 return dhcp6_client_send_message(client
);
1405 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1411 /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
1412 * engine is about to release its UDP socket unconditionally. */
1413 r
= client_send_release(client
);
1415 log_dhcp6_client_errno(client
, r
,
1416 "Failed to send DHCP6 release message, ignoring: %m");
1418 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1420 client
->receive_message
= sd_event_source_unref(client
->receive_message
);
1421 client
->fd
= safe_close(client
->fd
);
1426 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1427 assert_return(client
, -EINVAL
);
1429 return client
->state
!= DHCP6_STATE_STOPPED
;
1432 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1433 DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1436 assert_return(client
, -EINVAL
);
1437 assert_return(client
->event
, -EINVAL
);
1438 assert_return(client
->ifindex
> 0, -EINVAL
);
1439 assert_return(in6_addr_is_link_local(&client
->local_address
) > 0, -EINVAL
);
1440 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1441 assert_return(client
->information_request
|| client
->request_ia
!= 0, -EINVAL
);
1443 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1444 * request may be stored. */
1445 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
1447 r
= client_ensure_iaid(client
);
1451 r
= client_ensure_duid(client
);
1455 if (client
->fd
< 0) {
1456 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1458 return log_dhcp6_client_errno(client
, r
,
1459 "Failed to bind to UDP socket at address %s: %m",
1460 IN6_ADDR_TO_STRING(&client
->local_address
));
1465 if (!client
->receive_message
) {
1466 _cleanup_(sd_event_source_disable_unrefp
) sd_event_source
*s
= NULL
;
1468 r
= sd_event_add_io(client
->event
, &s
, client
->fd
, EPOLLIN
, client_receive_message
, client
);
1472 r
= sd_event_source_set_priority(s
, client
->event_priority
);
1476 r
= sd_event_source_set_description(s
, "dhcp6-receive-message");
1480 client
->receive_message
= TAKE_PTR(s
);
1483 if (client
->information_request
) {
1484 usec_t t
= now(CLOCK_MONOTONIC
);
1486 if (t
< usec_add(client
->information_request_time_usec
, client
->information_refresh_time_usec
))
1489 client
->information_request_time_usec
= t
;
1490 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1493 log_dhcp6_client(client
, "Starting in %s mode",
1494 client
->information_request
? "Information request" : "Solicit");
1496 return client_start_transaction(client
, state
);
1499 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1502 assert_return(client
, -EINVAL
);
1503 assert_return(!client
->event
, -EBUSY
);
1504 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1507 client
->event
= sd_event_ref(event
);
1509 r
= sd_event_default(&client
->event
);
1514 client
->event_priority
= priority
;
1519 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1520 assert_return(client
, -EINVAL
);
1521 assert_return(!sd_dhcp6_client_is_running(client
), -EBUSY
);
1523 client
->event
= sd_event_unref(client
->event
);
1528 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1529 assert_return(client
, NULL
);
1531 return client
->event
;
1534 int sd_dhcp6_client_attach_device(sd_dhcp6_client
*client
, sd_device
*dev
) {
1535 assert_return(client
, -EINVAL
);
1537 return device_unref_and_replace(client
->dev
, dev
);
1540 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1544 sd_dhcp6_lease_unref(client
->lease
);
1546 sd_event_source_disable_unref(client
->receive_message
);
1547 sd_event_source_disable_unref(client
->timeout_resend
);
1548 sd_event_source_disable_unref(client
->timeout_expire
);
1549 sd_event_source_disable_unref(client
->timeout_t1
);
1550 sd_event_source_disable_unref(client
->timeout_t2
);
1551 sd_event_unref(client
->event
);
1553 client
->fd
= safe_close(client
->fd
);
1555 sd_device_unref(client
->dev
);
1557 free(client
->req_opts
);
1559 free(client
->mudurl
);
1560 dhcp6_ia_clear_addresses(&client
->ia_pd
);
1561 ordered_hashmap_free(client
->extra_options
);
1562 ordered_set_free(client
->vendor_options
);
1563 strv_free(client
->user_class
);
1564 strv_free(client
->vendor_class
);
1565 free(client
->ifname
);
1567 return mfree(client
);
1570 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1572 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1573 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1575 assert_return(ret
, -EINVAL
);
1577 client
= new(sd_dhcp6_client
, 1);
1581 *client
= (sd_dhcp6_client
) {
1583 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1584 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1586 .request_ia
= DHCP6_REQUEST_IA_NA
| DHCP6_REQUEST_IA_PD
,
1588 .rapid_commit
= true,
1591 *ret
= TAKE_PTR(client
);