1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
8 #include <linux/if_arp.h>
9 #include <linux/if_infiniband.h>
11 #include "sd-dhcp6-client.h"
13 #include "alloc-util.h"
14 #include "dhcp-identifier.h"
15 #include "dhcp6-internal.h"
16 #include "dhcp6-lease-internal.h"
17 #include "dhcp6-protocol.h"
18 #include "dns-domain.h"
19 #include "event-util.h"
21 #include "hostname-util.h"
22 #include "in-addr-util.h"
23 #include "network-internal.h"
24 #include "random-util.h"
25 #include "socket-util.h"
26 #include "string-table.h"
30 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
32 #define IRT_DEFAULT (1 * USEC_PER_DAY)
33 #define IRT_MINIMUM (600 * USEC_PER_SEC)
35 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
37 DHCP6_REQUEST_IA_NA
= 1,
38 DHCP6_REQUEST_IA_TA
= 2, /* currently not used */
39 DHCP6_REQUEST_IA_PD
= 4,
42 struct sd_dhcp6_client
{
45 enum DHCP6State state
;
49 DHCP6Address hint_pd_prefix
;
50 struct in6_addr local_address
;
51 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
56 sd_event_source
*timeout_t1
;
57 sd_event_source
*timeout_t2
;
59 be32_t transaction_id
;
60 usec_t transaction_start
;
61 struct sd_dhcp6_lease
*lease
;
63 bool information_request
;
66 size_t req_opts_allocated
;
72 sd_event_source
*receive_message
;
73 usec_t retransmit_time
;
74 uint8_t retransmit_count
;
75 sd_event_source
*timeout_resend
;
76 sd_event_source
*timeout_resend_expire
;
77 sd_dhcp6_client_callback_t callback
;
81 usec_t information_request_time_usec
;
82 usec_t information_refresh_time_usec
;
83 OrderedHashmap
*extra_options
;
86 static const uint16_t default_req_opts
[] = {
87 SD_DHCP6_OPTION_DNS_SERVERS
,
88 SD_DHCP6_OPTION_DOMAIN_LIST
,
89 SD_DHCP6_OPTION_NTP_SERVER
,
90 SD_DHCP6_OPTION_SNTP_SERVERS
,
93 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
94 [DHCP6_SOLICIT
] = "SOLICIT",
95 [DHCP6_ADVERTISE
] = "ADVERTISE",
96 [DHCP6_REQUEST
] = "REQUEST",
97 [DHCP6_CONFIRM
] = "CONFIRM",
98 [DHCP6_RENEW
] = "RENEW",
99 [DHCP6_REBIND
] = "REBIND",
100 [DHCP6_REPLY
] = "REPLY",
101 [DHCP6_RELEASE
] = "RELEASE",
102 [DHCP6_DECLINE
] = "DECLINE",
103 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
104 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
105 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
106 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
109 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
111 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
112 [DHCP6_STATUS_SUCCESS
] = "Success",
113 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
114 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
115 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
116 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
117 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
118 [DHCP6_STATUS_NO_PREFIX_AVAIL
] = "No prefix available",
119 [DHCP6_STATUS_UNKNOWN_QUERY_TYPE
] = "Unknown query type",
120 [DHCP6_STATUS_MALFORMED_QUERY
] = "Malformed query",
121 [DHCP6_STATUS_NOT_CONFIGURED
] = "Not configured",
122 [DHCP6_STATUS_NOT_ALLOWED
] = "Not allowed",
123 [DHCP6_STATUS_QUERY_TERMINATED
] = "Query terminated",
124 [DHCP6_STATUS_DATA_MISSING
] = "Data missing",
125 [DHCP6_STATUS_CATCHUP_COMPLETE
] = "Catch up complete",
126 [DHCP6_STATUS_NOT_SUPPORTED
] = "Not supported",
127 [DHCP6_STATUS_TLS_CONNECTION_REFUSED
] = "TLS connection refused",
128 [DHCP6_STATUS_ADDRESS_IN_USE
] = "Address in use",
129 [DHCP6_STATUS_CONFIGURATION_CONFLICT
] = "Configuration conflict",
130 [DHCP6_STATUS_MISSING_BINDING_INFORMATION
] = "Missing binding information",
131 [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION
] = "Outdated binding information",
132 [DHCP6_STATUS_SERVER_SHUTTING_DOWN
] = "Server shutting down",
133 [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED
] = "DNS update not supported",
134 [DHCP6_STATUS_EXCESSIVE_TIME_SKEW
] = "Excessive time skew",
137 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
139 #define DHCP6_CLIENT_DONT_DESTROY(client) \
140 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
142 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
144 int sd_dhcp6_client_set_callback(
145 sd_dhcp6_client
*client
,
146 sd_dhcp6_client_callback_t cb
,
149 assert_return(client
, -EINVAL
);
151 client
->callback
= cb
;
152 client
->userdata
= userdata
;
157 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
159 assert_return(client
, -EINVAL
);
160 assert_return(ifindex
>= -1, -EINVAL
);
161 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
163 client
->ifindex
= ifindex
;
167 int sd_dhcp6_client_set_local_address(
168 sd_dhcp6_client
*client
,
169 const struct in6_addr
*local_address
) {
171 assert_return(client
, -EINVAL
);
172 assert_return(local_address
, -EINVAL
);
173 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
175 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
177 client
->local_address
= *local_address
;
182 int sd_dhcp6_client_set_mac(
183 sd_dhcp6_client
*client
,
184 const uint8_t *addr
, size_t addr_len
,
187 assert_return(client
, -EINVAL
);
188 assert_return(addr
, -EINVAL
);
189 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
190 assert_return(arp_type
> 0, -EINVAL
);
192 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
194 if (arp_type
== ARPHRD_ETHER
)
195 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
196 else if (arp_type
== ARPHRD_INFINIBAND
)
197 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
201 if (client
->mac_addr_len
== addr_len
&&
202 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
205 memcpy(&client
->mac_addr
, addr
, addr_len
);
206 client
->mac_addr_len
= addr_len
;
207 client
->arp_type
= arp_type
;
212 int sd_dhcp6_client_set_prefix_delegation_hint(
213 sd_dhcp6_client
*client
,
215 const struct in6_addr
*pd_address
) {
217 assert_return(client
, -EINVAL
);
218 assert_return(pd_address
, -EINVAL
);
220 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
222 client
->hint_pd_prefix
.iapdprefix
.address
= *pd_address
;
223 client
->hint_pd_prefix
.iapdprefix
.prefixlen
= prefixlen
;
228 static int client_ensure_duid(sd_dhcp6_client
*client
) {
229 if (client
->duid_len
!= 0)
232 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
236 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
237 * without further modification. Otherwise, if duid_type is supported, DUID
238 * is set based on that type. Otherwise, an error is returned.
240 static int dhcp6_client_set_duid_internal(
241 sd_dhcp6_client
*client
,
248 assert_return(client
, -EINVAL
);
249 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
250 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
253 r
= dhcp_validate_duid_len(duid_type
, duid_len
, true);
255 r
= dhcp_validate_duid_len(duid_type
, duid_len
, false);
257 return log_dhcp6_client_errno(client
, r
, "Failed to validate length of DUID: %m");
259 log_dhcp6_client(client
, "Using DUID of type %u of incorrect length, proceeding.", duid_type
);
262 client
->duid
.type
= htobe16(duid_type
);
263 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
264 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
268 if (client
->mac_addr_len
== 0)
269 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EOPNOTSUPP
), "Failed to set DUID-LLT, MAC address is not set.");
271 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
273 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LLT: %m");
276 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
278 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-EN: %m");
281 if (client
->mac_addr_len
== 0)
282 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EOPNOTSUPP
), "Failed to set DUID-LL, MAC address is not set.");
284 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
286 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-LL: %m");
289 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
291 return log_dhcp6_client_errno(client
, r
, "Failed to set DUID-UUID: %m");
294 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
), "Invalid DUID type");
300 int sd_dhcp6_client_set_duid(
301 sd_dhcp6_client
*client
,
305 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
308 int sd_dhcp6_client_set_duid_llt(
309 sd_dhcp6_client
*client
,
311 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
314 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
315 assert_return(client
, -EINVAL
);
316 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
318 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
319 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
320 client
->iaid_set
= true;
325 int sd_dhcp6_client_set_fqdn(
326 sd_dhcp6_client
*client
,
329 assert_return(client
, -EINVAL
);
331 /* Make sure FQDN qualifies as DNS and as Linux hostname */
333 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
336 return free_and_strdup(&client
->fqdn
, fqdn
);
339 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
340 assert_return(client
, -EINVAL
);
341 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
343 client
->information_request
= enabled
;
348 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
349 assert_return(client
, -EINVAL
);
350 assert_return(enabled
, -EINVAL
);
352 *enabled
= client
->information_request
;
357 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
360 assert_return(client
, -EINVAL
);
361 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
363 if (option
<= 0 || option
>= 255)
366 for (t
= 0; t
< client
->req_opts_len
; t
++)
367 if (client
->req_opts
[t
] == htobe16(option
))
370 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
371 client
->req_opts_len
+ 1))
374 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
379 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client
*client
, const char *mudurl
) {
381 assert_return(client
, -EINVAL
);
382 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
383 assert_return(mudurl
, -EINVAL
);
384 assert_return(strlen(mudurl
) <= 255, -EINVAL
);
385 assert_return(http_url_is_valid(mudurl
), -EINVAL
);
387 return free_and_strdup(&client
->mudurl
, mudurl
);
390 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client
*client
, char **user_class
) {
391 _cleanup_strv_free_
char **s
= NULL
;
394 assert_return(client
, -EINVAL
);
395 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
397 assert_return(user_class
, -EINVAL
);
399 STRV_FOREACH(p
, user_class
)
400 if (strlen(*p
) > UINT16_MAX
)
401 return -ENAMETOOLONG
;
403 s
= strv_copy((char **) user_class
);
407 client
->user_class
= TAKE_PTR(s
);
412 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client
*client
, char **vendor_class
) {
413 _cleanup_strv_free_
char **s
= NULL
;
416 assert_return(client
, -EINVAL
);
417 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
418 assert_return(vendor_class
, -EINVAL
);
420 STRV_FOREACH(p
, vendor_class
)
421 if (strlen(*p
) > UINT8_MAX
)
422 return -ENAMETOOLONG
;
424 s
= strv_copy(vendor_class
);
428 client
->vendor_class
= TAKE_PTR(s
);
433 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
434 assert_return(client
, -EINVAL
);
435 assert_return(delegation
, -EINVAL
);
437 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
442 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
443 assert_return(client
, -EINVAL
);
445 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
450 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
451 assert_return(client
, -EINVAL
);
452 assert_return(request
, -EINVAL
);
454 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
459 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
460 assert_return(client
, -EINVAL
);
462 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
467 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
468 assert_return(client
, -EINVAL
);
470 client
->transaction_id
= transaction_id
;
475 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
476 assert_return(client
, -EINVAL
);
482 *ret
= client
->lease
;
487 int sd_dhcp6_client_add_option(sd_dhcp6_client
*client
, sd_dhcp6_option
*v
) {
490 assert_return(client
, -EINVAL
);
491 assert_return(v
, -EINVAL
);
493 r
= ordered_hashmap_ensure_allocated(&client
->extra_options
, &dhcp6_option_hash_ops
);
497 r
= ordered_hashmap_put(client
->extra_options
, UINT_TO_PTR(v
->option
), v
);
501 sd_dhcp6_option_ref(v
);
505 static void client_notify(sd_dhcp6_client
*client
, int event
) {
508 if (client
->callback
)
509 client
->callback(client
, event
, client
->userdata
);
512 static int client_reset(sd_dhcp6_client
*client
) {
515 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
517 client
->receive_message
=
518 sd_event_source_unref(client
->receive_message
);
520 client
->transaction_id
= 0;
521 client
->transaction_start
= 0;
523 client
->retransmit_time
= 0;
524 client
->retransmit_count
= 0;
526 (void) event_source_disable(client
->timeout_resend
);
527 (void) event_source_disable(client
->timeout_resend_expire
);
528 (void) event_source_disable(client
->timeout_t1
);
529 (void) event_source_disable(client
->timeout_t2
);
531 client
->state
= DHCP6_STATE_STOPPED
;
536 static void client_stop(sd_dhcp6_client
*client
, int error
) {
537 DHCP6_CLIENT_DONT_DESTROY(client
);
541 client_notify(client
, error
);
543 client_reset(client
);
546 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
547 _cleanup_free_ DHCP6Message
*message
= NULL
;
548 struct in6_addr all_servers
=
549 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
550 struct sd_dhcp6_option
*j
;
551 size_t len
, optlen
= 512;
560 len
= sizeof(DHCP6Message
) + optlen
;
562 message
= malloc0(len
);
566 opt
= (uint8_t *)(message
+ 1);
568 message
->transaction_id
= client
->transaction_id
;
570 switch(client
->state
) {
571 case DHCP6_STATE_INFORMATION_REQUEST
:
572 message
->type
= DHCP6_INFORMATION_REQUEST
;
574 if (client
->mudurl
) {
575 r
= dhcp6_option_append(&opt
, &optlen
,
576 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
584 case DHCP6_STATE_SOLICITATION
:
585 message
->type
= DHCP6_SOLICIT
;
587 r
= dhcp6_option_append(&opt
, &optlen
,
588 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
592 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
593 r
= dhcp6_option_append_ia(&opt
, &optlen
,
600 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
605 if (client
->mudurl
) {
606 r
= dhcp6_option_append(&opt
, &optlen
,
607 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
613 if (client
->user_class
) {
614 r
= dhcp6_option_append_user_class(&opt
, &optlen
, client
->user_class
);
619 if (client
->vendor_class
) {
620 r
= dhcp6_option_append_vendor_class(&opt
, &optlen
, client
->vendor_class
);
625 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
626 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
, &client
->hint_pd_prefix
);
636 case DHCP6_STATE_REQUEST
:
637 case DHCP6_STATE_RENEW
:
639 if (client
->state
== DHCP6_STATE_REQUEST
)
640 message
->type
= DHCP6_REQUEST
;
642 message
->type
= DHCP6_RENEW
;
644 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
645 client
->lease
->serverid_len
,
646 client
->lease
->serverid
);
650 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
651 r
= dhcp6_option_append_ia(&opt
, &optlen
,
658 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
663 if (client
->mudurl
) {
664 r
= dhcp6_option_append(&opt
, &optlen
,
665 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
671 if (client
->user_class
) {
672 r
= dhcp6_option_append_user_class(&opt
, &optlen
, client
->user_class
);
677 if (client
->vendor_class
) {
678 r
= dhcp6_option_append_vendor_class(&opt
, &optlen
, client
->vendor_class
);
683 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
684 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
, NULL
);
694 case DHCP6_STATE_REBIND
:
695 message
->type
= DHCP6_REBIND
;
697 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
698 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
704 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
709 if (client
->mudurl
) {
710 r
= dhcp6_option_append(&opt
, &optlen
,
711 SD_DHCP6_OPTION_MUD_URL
, strlen(client
->mudurl
),
717 if (client
->user_class
) {
718 r
= dhcp6_option_append_user_class(&opt
, &optlen
, client
->user_class
);
723 if (client
->vendor_class
) {
724 r
= dhcp6_option_append_vendor_class(&opt
, &optlen
, client
->vendor_class
);
729 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
730 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
, NULL
);
740 case DHCP6_STATE_STOPPED
:
741 case DHCP6_STATE_BOUND
:
745 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
746 client
->req_opts_len
* sizeof(be16_t
),
751 assert(client
->duid_len
);
752 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
753 client
->duid_len
, &client
->duid
);
757 elapsed_usec
= time_now
- client
->transaction_start
;
758 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
759 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
761 elapsed_time
= 0xffff;
763 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
764 sizeof(elapsed_time
), &elapsed_time
);
768 ORDERED_HASHMAP_FOREACH(j
, client
->extra_options
, i
) {
769 r
= dhcp6_option_append(&opt
, &optlen
, j
->option
, j
->length
, j
->data
);
774 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
779 log_dhcp6_client(client
, "Sent %s",
780 dhcp6_message_type_to_string(message
->type
));
785 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
786 sd_dhcp6_client
*client
= userdata
;
790 assert(client
->lease
);
792 (void) event_source_disable(client
->timeout_t2
);
794 log_dhcp6_client(client
, "Timeout T2");
796 client_start(client
, DHCP6_STATE_REBIND
);
801 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
802 sd_dhcp6_client
*client
= userdata
;
806 assert(client
->lease
);
808 (void) event_source_disable(client
->timeout_t1
);
810 log_dhcp6_client(client
, "Timeout T1");
812 client_start(client
, DHCP6_STATE_RENEW
);
817 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
818 sd_dhcp6_client
*client
= userdata
;
819 DHCP6_CLIENT_DONT_DESTROY(client
);
820 enum DHCP6State state
;
824 assert(client
->event
);
826 state
= client
->state
;
828 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
830 /* RFC 3315, section 18.1.4., says that "...the client may choose to
831 use a Solicit message to locate a new DHCP server..." */
832 if (state
== DHCP6_STATE_REBIND
)
833 client_start(client
, DHCP6_STATE_SOLICITATION
);
838 static usec_t
client_timeout_compute_random(usec_t val
) {
839 return val
- (random_u32() % USEC_PER_SEC
) * val
/ 10 / USEC_PER_SEC
;
842 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
844 sd_dhcp6_client
*client
= userdata
;
845 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
846 usec_t max_retransmit_duration
= 0;
847 uint8_t max_retransmit_count
= 0;
848 char time_string
[FORMAT_TIMESPAN_MAX
];
852 assert(client
->event
);
854 (void) event_source_disable(client
->timeout_resend
);
856 switch (client
->state
) {
857 case DHCP6_STATE_INFORMATION_REQUEST
:
858 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
859 max_retransmit_time
= DHCP6_INF_MAX_RT
;
863 case DHCP6_STATE_SOLICITATION
:
865 if (client
->retransmit_count
&& client
->lease
) {
866 client_start(client
, DHCP6_STATE_REQUEST
);
870 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
871 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
875 case DHCP6_STATE_REQUEST
:
876 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
877 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
878 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
882 case DHCP6_STATE_RENEW
:
883 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
884 max_retransmit_time
= DHCP6_REN_MAX_RT
;
886 /* RFC 3315, section 18.1.3. says max retransmit duration will
887 be the remaining time until T2. Instead of setting MRD,
888 wait for T2 to trigger with the same end result */
892 case DHCP6_STATE_REBIND
:
893 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
894 max_retransmit_time
= DHCP6_REB_MAX_RT
;
896 if (event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
899 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
, &expire
);
901 client_stop(client
, r
);
904 max_retransmit_duration
= expire
* USEC_PER_SEC
;
909 case DHCP6_STATE_STOPPED
:
910 case DHCP6_STATE_BOUND
:
914 if (max_retransmit_count
> 0 &&
915 client
->retransmit_count
>= max_retransmit_count
) {
916 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
920 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
924 r
= client_send_message(client
, time_now
);
926 client
->retransmit_count
++;
928 if (client
->retransmit_time
== 0) {
929 client
->retransmit_time
=
930 client_timeout_compute_random(init_retransmit_time
);
932 if (client
->state
== DHCP6_STATE_SOLICITATION
)
933 client
->retransmit_time
+= init_retransmit_time
/ 10;
936 if (max_retransmit_time
> 0 &&
937 client
->retransmit_time
> max_retransmit_time
/ 2)
938 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
940 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
943 log_dhcp6_client(client
, "Next retransmission in %s",
944 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
946 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
947 clock_boottime_or_monotonic(),
948 time_now
+ client
->retransmit_time
, 10 * USEC_PER_MSEC
,
949 client_timeout_resend
, client
,
950 client
->event_priority
, "dhcp6-resend-timer", true);
954 if (max_retransmit_duration
> 0 && event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
956 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
957 max_retransmit_duration
/ USEC_PER_SEC
);
959 r
= event_reset_time(client
->event
, &client
->timeout_resend_expire
,
960 clock_boottime_or_monotonic(),
961 time_now
+ max_retransmit_duration
, USEC_PER_SEC
,
962 client_timeout_resend_expire
, client
,
963 client
->event_priority
, "dhcp6-resend-expire-timer", true);
970 client_stop(client
, r
);
975 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
981 if (client
->iaid_set
)
984 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, true, &iaid
);
988 client
->ia_na
.ia_na
.id
= iaid
;
989 client
->ia_pd
.ia_pd
.id
= iaid
;
990 client
->iaid_set
= true;
995 static int client_parse_message(
996 sd_dhcp6_client
*client
,
997 DHCP6Message
*message
,
999 sd_dhcp6_lease
*lease
) {
1001 uint32_t lt_t1
= ~0, lt_t2
= ~0;
1002 bool clientid
= false;
1004 usec_t irt
= IRT_DEFAULT
;
1009 assert(len
>= sizeof(DHCP6Message
));
1012 len
-= sizeof(DHCP6Message
);
1015 DHCP6Option
*option
= (DHCP6Option
*) &message
->options
[pos
];
1016 uint16_t optcode
, optlen
;
1021 if (len
< pos
+ offsetof(DHCP6Option
, data
))
1024 optcode
= be16toh(option
->code
);
1025 optlen
= be16toh(option
->len
);
1026 optval
= option
->data
;
1028 if (len
< pos
+ offsetof(DHCP6Option
, data
) + optlen
)
1032 case SD_DHCP6_OPTION_CLIENTID
:
1034 log_dhcp6_client(client
, "%s contains multiple clientids",
1035 dhcp6_message_type_to_string(message
->type
));
1039 if (optlen
!= client
->duid_len
||
1040 memcmp(&client
->duid
, optval
, optlen
) != 0) {
1041 log_dhcp6_client(client
, "%s DUID does not match",
1042 dhcp6_message_type_to_string(message
->type
));
1050 case SD_DHCP6_OPTION_SERVERID
:
1051 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1053 log_dhcp6_client(client
, "%s contains multiple serverids",
1054 dhcp6_message_type_to_string(message
->type
));
1058 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
1064 case SD_DHCP6_OPTION_PREFERENCE
:
1068 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
1074 case SD_DHCP6_OPTION_STATUS_CODE
:
1075 status
= dhcp6_option_parse_status(option
, optlen
+ sizeof(DHCP6Option
));
1080 log_dhcp6_client(client
, "%s Status %s",
1081 dhcp6_message_type_to_string(message
->type
),
1082 dhcp6_message_status_to_string(status
));
1089 case SD_DHCP6_OPTION_IA_NA
:
1090 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1091 log_dhcp6_client(client
, "Information request ignoring IA NA option");
1096 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
1097 if (r
< 0 && r
!= -ENOMSG
)
1100 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
1104 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
1105 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
1106 dhcp6_message_type_to_string(message
->type
));
1110 if (lease
->ia
.addresses
) {
1111 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
1112 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
1117 case SD_DHCP6_OPTION_IA_PD
:
1118 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1119 log_dhcp6_client(client
, "Information request ignoring IA PD option");
1124 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
1125 if (r
< 0 && r
!= -ENOMSG
)
1128 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
1132 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
1133 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
1134 dhcp6_message_type_to_string(message
->type
));
1138 if (lease
->pd
.addresses
) {
1139 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
1140 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
1145 case SD_DHCP6_OPTION_RAPID_COMMIT
:
1146 r
= dhcp6_lease_set_rapid_commit(lease
);
1152 case SD_DHCP6_OPTION_DNS_SERVERS
:
1153 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
1159 case SD_DHCP6_OPTION_DOMAIN_LIST
:
1160 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
1166 case SD_DHCP6_OPTION_NTP_SERVER
:
1167 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
1173 case SD_DHCP6_OPTION_SNTP_SERVERS
:
1174 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
1180 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
1184 irt
= unaligned_read_be32((be32_t
*) optval
) * USEC_PER_SEC
;
1188 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1192 log_dhcp6_client(client
, "%s has incomplete options",
1193 dhcp6_message_type_to_string(message
->type
));
1197 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1198 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1200 log_dhcp6_client(client
, "%s has no server id",
1201 dhcp6_message_type_to_string(message
->type
));
1206 if (lease
->ia
.addresses
) {
1207 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1208 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1211 if (lease
->pd
.addresses
) {
1212 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1213 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1217 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
1222 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1223 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1230 if (reply
->type
!= DHCP6_REPLY
)
1233 r
= dhcp6_lease_new(&lease
);
1237 r
= client_parse_message(client
, reply
, len
, lease
);
1241 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1242 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1250 sd_dhcp6_lease_unref(client
->lease
);
1251 client
->lease
= TAKE_PTR(lease
);
1253 return DHCP6_STATE_BOUND
;
1256 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1257 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1258 uint8_t pref_advertise
= 0, pref_lease
= 0;
1261 if (advertise
->type
!= DHCP6_ADVERTISE
)
1264 r
= dhcp6_lease_new(&lease
);
1268 r
= client_parse_message(client
, advertise
, len
, lease
);
1272 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1276 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1278 if (r
< 0 || pref_advertise
> pref_lease
) {
1279 sd_dhcp6_lease_unref(client
->lease
);
1280 client
->lease
= TAKE_PTR(lease
);
1284 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1285 r
= DHCP6_STATE_REQUEST
;
1290 static int client_receive_message(
1296 sd_dhcp6_client
*client
= userdata
;
1297 DHCP6_CLIENT_DONT_DESTROY(client
);
1298 _cleanup_free_ DHCP6Message
*message
= NULL
;
1299 ssize_t buflen
, len
;
1304 assert(client
->event
);
1306 buflen
= next_datagram_size_fd(fd
);
1307 if (buflen
== -ENETDOWN
) {
1308 /* the link is down. Don't return an error or the I/O event
1309 source will be disconnected and we won't be able to receive
1310 packets again when the link comes back. */
1316 message
= malloc(buflen
);
1320 len
= recv(fd
, message
, buflen
, 0);
1322 /* see comment above for why we shouldn't error out on ENETDOWN. */
1323 if (IN_SET(errno
, EAGAIN
, EINTR
, ENETDOWN
))
1326 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1329 if ((size_t) len
< sizeof(DHCP6Message
)) {
1330 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1334 switch(message
->type
) {
1342 case DHCP6_INFORMATION_REQUEST
:
1343 case DHCP6_RELAY_FORW
:
1344 case DHCP6_RELAY_REPL
:
1347 case DHCP6_ADVERTISE
:
1349 case DHCP6_RECONFIGURE
:
1353 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1357 if (client
->transaction_id
!= (message
->transaction_id
&
1358 htobe32(0x00ffffff)))
1361 switch (client
->state
) {
1362 case DHCP6_STATE_INFORMATION_REQUEST
:
1363 r
= client_receive_reply(client
, message
, len
);
1367 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1369 client_start(client
, DHCP6_STATE_STOPPED
);
1373 case DHCP6_STATE_SOLICITATION
:
1374 r
= client_receive_advertise(client
, message
, len
);
1376 if (r
== DHCP6_STATE_REQUEST
) {
1377 client_start(client
, r
);
1382 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1383 case DHCP6_STATE_REQUEST
:
1384 case DHCP6_STATE_RENEW
:
1385 case DHCP6_STATE_REBIND
:
1387 r
= client_receive_reply(client
, message
, len
);
1391 if (r
== DHCP6_STATE_BOUND
) {
1393 r
= client_start(client
, DHCP6_STATE_BOUND
);
1395 client_stop(client
, r
);
1399 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1404 case DHCP6_STATE_BOUND
:
1408 case DHCP6_STATE_STOPPED
:
1412 log_dhcp6_client(client
, "Recv %s",
1413 dhcp6_message_type_to_string(message
->type
));
1418 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1419 uint32_t *lifetime_t2
) {
1420 assert_return(client
, -EINVAL
);
1421 assert_return(client
->lease
, -EINVAL
);
1423 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1424 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1425 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1430 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1431 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1432 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1440 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1442 usec_t timeout
, time_now
;
1443 char time_string
[FORMAT_TIMESPAN_MAX
];
1444 uint32_t lifetime_t1
, lifetime_t2
;
1446 assert_return(client
, -EINVAL
);
1447 assert_return(client
->event
, -EINVAL
);
1448 assert_return(client
->ifindex
> 0, -EINVAL
);
1449 assert_return(client
->state
!= state
, -EINVAL
);
1451 (void) event_source_disable(client
->timeout_resend_expire
);
1452 (void) event_source_disable(client
->timeout_resend
);
1453 client
->retransmit_time
= 0;
1454 client
->retransmit_count
= 0;
1456 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1460 if (!client
->receive_message
) {
1461 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1462 client
->fd
, EPOLLIN
, client_receive_message
,
1467 r
= sd_event_source_set_priority(client
->receive_message
,
1468 client
->event_priority
);
1472 r
= sd_event_source_set_description(client
->receive_message
,
1473 "dhcp6-receive-message");
1479 case DHCP6_STATE_STOPPED
:
1480 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1481 client
->state
= DHCP6_STATE_STOPPED
;
1487 case DHCP6_STATE_SOLICITATION
:
1488 client
->state
= DHCP6_STATE_SOLICITATION
;
1492 case DHCP6_STATE_INFORMATION_REQUEST
:
1493 case DHCP6_STATE_REQUEST
:
1494 case DHCP6_STATE_RENEW
:
1495 case DHCP6_STATE_REBIND
:
1497 client
->state
= state
;
1501 case DHCP6_STATE_BOUND
:
1503 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1507 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1508 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1509 lifetime_t1
, lifetime_t2
);
1514 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1516 log_dhcp6_client(client
, "T1 expires in %s",
1517 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1519 r
= event_reset_time(client
->event
, &client
->timeout_t1
,
1520 clock_boottime_or_monotonic(),
1521 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1522 client_timeout_t1
, client
,
1523 client
->event_priority
, "dhcp6-t1-timeout", true);
1527 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1529 log_dhcp6_client(client
, "T2 expires in %s",
1530 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1532 r
= event_reset_time(client
->event
, &client
->timeout_t2
,
1533 clock_boottime_or_monotonic(),
1534 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1535 client_timeout_t2
, client
,
1536 client
->event_priority
, "dhcp6-t2-timeout", true);
1540 client
->state
= state
;
1545 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1546 client
->transaction_start
= time_now
;
1548 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
1549 clock_boottime_or_monotonic(),
1551 client_timeout_resend
, client
,
1552 client
->event_priority
, "dhcp6-resend-timeout", true);
1559 client_reset(client
);
1563 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1564 assert_return(client
, -EINVAL
);
1566 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1568 client
->fd
= safe_close(client
->fd
);
1573 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1574 assert_return(client
, -EINVAL
);
1576 return client
->state
!= DHCP6_STATE_STOPPED
;
1579 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1580 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1583 assert_return(client
, -EINVAL
);
1584 assert_return(client
->event
, -EINVAL
);
1585 assert_return(client
->ifindex
> 0, -EINVAL
);
1586 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1588 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1591 if (!client
->information_request
&& !client
->request
)
1594 r
= client_reset(client
);
1598 r
= client_ensure_iaid(client
);
1602 r
= client_ensure_duid(client
);
1606 if (client
->fd
< 0) {
1607 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1609 _cleanup_free_
char *p
= NULL
;
1611 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1612 return log_dhcp6_client_errno(client
, r
,
1613 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1619 if (client
->information_request
) {
1620 usec_t t
= now(CLOCK_MONOTONIC
);
1622 if (t
< usec_add(client
->information_request_time_usec
, client
->information_refresh_time_usec
))
1625 client
->information_request_time_usec
= t
;
1626 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1629 log_dhcp6_client(client
, "Started in %s mode",
1630 client
->information_request
? "Information request":
1633 return client_start(client
, state
);
1636 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1639 assert_return(client
, -EINVAL
);
1640 assert_return(!client
->event
, -EBUSY
);
1643 client
->event
= sd_event_ref(event
);
1645 r
= sd_event_default(&client
->event
);
1650 client
->event_priority
= priority
;
1655 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1656 assert_return(client
, -EINVAL
);
1658 client
->event
= sd_event_unref(client
->event
);
1663 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1664 assert_return(client
, NULL
);
1666 return client
->event
;
1669 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1672 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1673 client
->timeout_resend_expire
= sd_event_source_unref(client
->timeout_resend_expire
);
1674 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
1675 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
1677 client_reset(client
);
1679 client
->fd
= safe_close(client
->fd
);
1681 sd_dhcp6_client_detach_event(client
);
1683 free(client
->req_opts
);
1685 free(client
->mudurl
);
1687 ordered_hashmap_free(client
->extra_options
);
1688 strv_free(client
->user_class
);
1689 strv_free(client
->vendor_class
);
1691 return mfree(client
);
1694 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1696 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1697 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1698 _cleanup_free_ be16_t
*req_opts
= NULL
;
1701 assert_return(ret
, -EINVAL
);
1703 req_opts
= new(be16_t
, ELEMENTSOF(default_req_opts
));
1707 for (t
= 0; t
< ELEMENTSOF(default_req_opts
); t
++)
1708 req_opts
[t
] = htobe16(default_req_opts
[t
]);
1710 client
= new(sd_dhcp6_client
, 1);
1714 *client
= (sd_dhcp6_client
) {
1716 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1717 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1719 .request
= DHCP6_REQUEST_IA_NA
,
1721 .req_opts_len
= ELEMENTSOF(default_req_opts
),
1722 .hint_pd_prefix
.iapdprefix
.lifetime_preferred
= (be32_t
) -1,
1723 .hint_pd_prefix
.iapdprefix
.lifetime_valid
= (be32_t
) -1,
1724 .req_opts
= TAKE_PTR(req_opts
),
1727 *ret
= TAKE_PTR(client
);