1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
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"
20 #include "hostname-util.h"
21 #include "in-addr-util.h"
22 #include "network-internal.h"
23 #include "random-util.h"
24 #include "socket-util.h"
25 #include "string-table.h"
28 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
30 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
32 DHCP6_REQUEST_IA_NA
= 1,
33 DHCP6_REQUEST_IA_TA
= 2, /* currently not used */
34 DHCP6_REQUEST_IA_PD
= 4,
37 struct sd_dhcp6_client
{
40 enum DHCP6State state
;
44 struct in6_addr local_address
;
45 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
50 sd_event_source
*timeout_t1
;
51 sd_event_source
*timeout_t2
;
53 be32_t transaction_id
;
54 usec_t transaction_start
;
55 struct sd_dhcp6_lease
*lease
;
57 bool information_request
;
59 size_t req_opts_allocated
;
62 sd_event_source
*receive_message
;
63 usec_t retransmit_time
;
64 uint8_t retransmit_count
;
65 sd_event_source
*timeout_resend
;
66 sd_event_source
*timeout_resend_expire
;
67 sd_dhcp6_client_callback_t callback
;
73 static const uint16_t default_req_opts
[] = {
74 SD_DHCP6_OPTION_DNS_SERVERS
,
75 SD_DHCP6_OPTION_DOMAIN_LIST
,
76 SD_DHCP6_OPTION_NTP_SERVER
,
77 SD_DHCP6_OPTION_SNTP_SERVERS
,
80 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
81 [DHCP6_SOLICIT
] = "SOLICIT",
82 [DHCP6_ADVERTISE
] = "ADVERTISE",
83 [DHCP6_REQUEST
] = "REQUEST",
84 [DHCP6_CONFIRM
] = "CONFIRM",
85 [DHCP6_RENEW
] = "RENEW",
86 [DHCP6_REBIND
] = "REBIND",
87 [DHCP6_REPLY
] = "REPLY",
88 [DHCP6_RELEASE
] = "RELEASE",
89 [DHCP6_DECLINE
] = "DECLINE",
90 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
91 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
92 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
93 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
96 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
98 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
99 [DHCP6_STATUS_SUCCESS
] = "Success",
100 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
101 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
102 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
103 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
104 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
107 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
109 #define DHCP6_CLIENT_DONT_DESTROY(client) \
110 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
112 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
114 int sd_dhcp6_client_set_callback(
115 sd_dhcp6_client
*client
,
116 sd_dhcp6_client_callback_t cb
,
119 assert_return(client
, -EINVAL
);
121 client
->callback
= cb
;
122 client
->userdata
= userdata
;
127 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
129 assert_return(client
, -EINVAL
);
130 assert_return(ifindex
>= -1, -EINVAL
);
131 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
133 client
->ifindex
= ifindex
;
137 int sd_dhcp6_client_set_local_address(
138 sd_dhcp6_client
*client
,
139 const struct in6_addr
*local_address
) {
141 assert_return(client
, -EINVAL
);
142 assert_return(local_address
, -EINVAL
);
143 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
145 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
147 client
->local_address
= *local_address
;
152 int sd_dhcp6_client_set_mac(
153 sd_dhcp6_client
*client
,
154 const uint8_t *addr
, size_t addr_len
,
157 assert_return(client
, -EINVAL
);
158 assert_return(addr
, -EINVAL
);
159 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
160 assert_return(arp_type
> 0, -EINVAL
);
162 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
164 if (arp_type
== ARPHRD_ETHER
)
165 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
166 else if (arp_type
== ARPHRD_INFINIBAND
)
167 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
171 if (client
->mac_addr_len
== addr_len
&&
172 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
175 memcpy(&client
->mac_addr
, addr
, addr_len
);
176 client
->mac_addr_len
= addr_len
;
177 client
->arp_type
= arp_type
;
182 static int client_ensure_duid(sd_dhcp6_client
*client
) {
183 if (client
->duid_len
!= 0)
186 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
190 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
191 * without further modification. Otherwise, if duid_type is supported, DUID
192 * is set based on that type. Otherwise, an error is returned.
194 static int dhcp6_client_set_duid_internal(
195 sd_dhcp6_client
*client
,
202 assert_return(client
, -EINVAL
);
203 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
204 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
207 r
= dhcp_validate_duid_len(duid_type
, duid_len
);
211 client
->duid
.type
= htobe16(duid_type
);
212 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
213 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
217 if (client
->mac_addr_len
== 0)
220 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
225 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
230 if (client
->mac_addr_len
== 0)
233 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
238 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
249 int sd_dhcp6_client_set_duid(
250 sd_dhcp6_client
*client
,
254 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
257 int sd_dhcp6_client_set_duid_llt(
258 sd_dhcp6_client
*client
,
260 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
263 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
264 assert_return(client
, -EINVAL
);
265 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
267 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
268 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
273 int sd_dhcp6_client_set_fqdn(
274 sd_dhcp6_client
*client
,
277 assert_return(client
, -EINVAL
);
279 /* Make sure FQDN qualifies as DNS and as Linux hostname */
281 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
284 return free_and_strdup(&client
->fqdn
, fqdn
);
287 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
288 assert_return(client
, -EINVAL
);
289 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
291 client
->information_request
= enabled
;
296 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
297 assert_return(client
, -EINVAL
);
298 assert_return(enabled
, -EINVAL
);
300 *enabled
= client
->information_request
;
305 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
308 assert_return(client
, -EINVAL
);
309 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
313 case SD_DHCP6_OPTION_DNS_SERVERS
:
314 case SD_DHCP6_OPTION_DOMAIN_LIST
:
315 case SD_DHCP6_OPTION_SNTP_SERVERS
:
316 case SD_DHCP6_OPTION_NTP_SERVER
:
317 case SD_DHCP6_OPTION_RAPID_COMMIT
:
324 for (t
= 0; t
< client
->req_opts_len
; t
++)
325 if (client
->req_opts
[t
] == htobe16(option
))
328 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
329 client
->req_opts_len
+ 1))
332 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
337 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
338 assert_return(client
, -EINVAL
);
339 assert_return(delegation
, -EINVAL
);
341 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
346 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
347 assert_return(client
, -EINVAL
);
349 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
354 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
355 assert_return(client
, -EINVAL
);
356 assert_return(request
, -EINVAL
);
358 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
363 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
364 assert_return(client
, -EINVAL
);
366 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
371 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
372 assert_return(client
, -EINVAL
);
378 *ret
= client
->lease
;
383 static void client_notify(sd_dhcp6_client
*client
, int event
) {
386 if (client
->callback
)
387 client
->callback(client
, event
, client
->userdata
);
390 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
393 (void) sd_dhcp6_lease_unref(client
->lease
);
394 client
->lease
= sd_dhcp6_lease_ref(lease
);
397 static int client_reset(sd_dhcp6_client
*client
) {
400 client_set_lease(client
, NULL
);
402 client
->receive_message
=
403 sd_event_source_unref(client
->receive_message
);
405 client
->transaction_id
= 0;
406 client
->transaction_start
= 0;
408 client
->retransmit_time
= 0;
409 client
->retransmit_count
= 0;
410 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
411 client
->timeout_resend_expire
=
412 sd_event_source_unref(client
->timeout_resend_expire
);
414 client
->state
= DHCP6_STATE_STOPPED
;
419 static void client_stop(sd_dhcp6_client
*client
, int error
) {
420 DHCP6_CLIENT_DONT_DESTROY(client
);
424 client_notify(client
, error
);
426 client_reset(client
);
429 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
430 _cleanup_free_ DHCP6Message
*message
= NULL
;
431 struct in6_addr all_servers
=
432 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
433 size_t len
, optlen
= 512;
441 len
= sizeof(DHCP6Message
) + optlen
;
443 message
= malloc0(len
);
447 opt
= (uint8_t *)(message
+ 1);
449 message
->transaction_id
= client
->transaction_id
;
451 switch(client
->state
) {
452 case DHCP6_STATE_INFORMATION_REQUEST
:
453 message
->type
= DHCP6_INFORMATION_REQUEST
;
457 case DHCP6_STATE_SOLICITATION
:
458 message
->type
= DHCP6_SOLICIT
;
460 r
= dhcp6_option_append(&opt
, &optlen
,
461 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
465 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
466 r
= dhcp6_option_append_ia(&opt
, &optlen
,
473 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
478 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
479 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
489 case DHCP6_STATE_REQUEST
:
490 case DHCP6_STATE_RENEW
:
492 if (client
->state
== DHCP6_STATE_REQUEST
)
493 message
->type
= DHCP6_REQUEST
;
495 message
->type
= DHCP6_RENEW
;
497 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
498 client
->lease
->serverid_len
,
499 client
->lease
->serverid
);
503 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
504 r
= dhcp6_option_append_ia(&opt
, &optlen
,
511 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
516 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
517 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
527 case DHCP6_STATE_REBIND
:
528 message
->type
= DHCP6_REBIND
;
530 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
531 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
537 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
542 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
543 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
553 case DHCP6_STATE_STOPPED
:
554 case DHCP6_STATE_BOUND
:
558 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
559 client
->req_opts_len
* sizeof(be16_t
),
564 assert(client
->duid_len
);
565 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
566 client
->duid_len
, &client
->duid
);
570 elapsed_usec
= time_now
- client
->transaction_start
;
571 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
572 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
574 elapsed_time
= 0xffff;
576 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
577 sizeof(elapsed_time
), &elapsed_time
);
581 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
586 log_dhcp6_client(client
, "Sent %s",
587 dhcp6_message_type_to_string(message
->type
));
592 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
593 sd_dhcp6_client
*client
= userdata
;
597 assert(client
->lease
);
600 sd_event_source_unref(client
->timeout_t2
);
602 log_dhcp6_client(client
, "Timeout T2");
604 client_start(client
, DHCP6_STATE_REBIND
);
609 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
610 sd_dhcp6_client
*client
= userdata
;
614 assert(client
->lease
);
617 sd_event_source_unref(client
->timeout_t1
);
619 log_dhcp6_client(client
, "Timeout T1");
621 client_start(client
, DHCP6_STATE_RENEW
);
626 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
627 sd_dhcp6_client
*client
= userdata
;
628 DHCP6_CLIENT_DONT_DESTROY(client
);
629 enum DHCP6State state
;
633 assert(client
->event
);
635 state
= client
->state
;
637 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
639 /* RFC 3315, section 18.1.4., says that "...the client may choose to
640 use a Solicit message to locate a new DHCP server..." */
641 if (state
== DHCP6_STATE_REBIND
)
642 client_start(client
, DHCP6_STATE_SOLICITATION
);
647 static usec_t
client_timeout_compute_random(usec_t val
) {
648 return val
- val
/ 10 +
649 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
652 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
654 sd_dhcp6_client
*client
= userdata
;
655 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
656 usec_t max_retransmit_duration
= 0;
657 uint8_t max_retransmit_count
= 0;
658 char time_string
[FORMAT_TIMESPAN_MAX
];
663 assert(client
->event
);
665 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
667 switch (client
->state
) {
668 case DHCP6_STATE_INFORMATION_REQUEST
:
669 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
670 max_retransmit_time
= DHCP6_INF_MAX_RT
;
674 case DHCP6_STATE_SOLICITATION
:
676 if (client
->retransmit_count
&& client
->lease
) {
677 client_start(client
, DHCP6_STATE_REQUEST
);
681 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
682 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
686 case DHCP6_STATE_REQUEST
:
687 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
688 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
689 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
693 case DHCP6_STATE_RENEW
:
694 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
695 max_retransmit_time
= DHCP6_REN_MAX_RT
;
697 /* RFC 3315, section 18.1.3. says max retransmit duration will
698 be the remaining time until T2. Instead of setting MRD,
699 wait for T2 to trigger with the same end result */
703 case DHCP6_STATE_REBIND
:
704 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
705 max_retransmit_time
= DHCP6_REB_MAX_RT
;
707 if (!client
->timeout_resend_expire
) {
708 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
711 client_stop(client
, r
);
714 max_retransmit_duration
= expire
* USEC_PER_SEC
;
719 case DHCP6_STATE_STOPPED
:
720 case DHCP6_STATE_BOUND
:
724 if (max_retransmit_count
&&
725 client
->retransmit_count
>= max_retransmit_count
) {
726 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
730 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
734 r
= client_send_message(client
, time_now
);
736 client
->retransmit_count
++;
738 if (!client
->retransmit_time
) {
739 client
->retransmit_time
=
740 client_timeout_compute_random(init_retransmit_time
);
742 if (client
->state
== DHCP6_STATE_SOLICITATION
)
743 client
->retransmit_time
+= init_retransmit_time
/ 10;
746 if (max_retransmit_time
&&
747 client
->retransmit_time
> max_retransmit_time
/ 2)
748 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
750 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
753 log_dhcp6_client(client
, "Next retransmission in %s",
754 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
756 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
757 clock_boottime_or_monotonic(),
758 time_now
+ client
->retransmit_time
,
759 10 * USEC_PER_MSEC
, client_timeout_resend
,
764 r
= sd_event_source_set_priority(client
->timeout_resend
,
765 client
->event_priority
);
769 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
773 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
775 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
776 max_retransmit_duration
/ USEC_PER_SEC
);
778 r
= sd_event_add_time(client
->event
,
779 &client
->timeout_resend_expire
,
780 clock_boottime_or_monotonic(),
781 time_now
+ max_retransmit_duration
,
783 client_timeout_resend_expire
, client
);
787 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
788 client
->event_priority
);
792 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
799 client_stop(client
, r
);
804 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
810 if (client
->ia_na
.ia_na
.id
)
813 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, &iaid
);
817 client
->ia_na
.ia_na
.id
= iaid
;
818 client
->ia_pd
.ia_pd
.id
= iaid
;
823 static int client_parse_message(
824 sd_dhcp6_client
*client
,
825 DHCP6Message
*message
,
827 sd_dhcp6_lease
*lease
) {
830 bool clientid
= false;
831 uint32_t lt_t1
= ~0, lt_t2
= ~0;
835 assert(len
>= sizeof(DHCP6Message
));
838 len
-= sizeof(DHCP6Message
);
841 DHCP6Option
*option
= (DHCP6Option
*)&message
->options
[pos
];
842 uint16_t optcode
, optlen
;
847 if (len
< offsetof(DHCP6Option
, data
) ||
848 len
< offsetof(DHCP6Option
, data
) + be16toh(option
->len
))
851 optcode
= be16toh(option
->code
);
852 optlen
= be16toh(option
->len
);
853 optval
= option
->data
;
856 case SD_DHCP6_OPTION_CLIENTID
:
858 log_dhcp6_client(client
, "%s contains multiple clientids",
859 dhcp6_message_type_to_string(message
->type
));
863 if (optlen
!= client
->duid_len
||
864 memcmp(&client
->duid
, optval
, optlen
) != 0) {
865 log_dhcp6_client(client
, "%s DUID does not match",
866 dhcp6_message_type_to_string(message
->type
));
874 case SD_DHCP6_OPTION_SERVERID
:
875 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
877 log_dhcp6_client(client
, "%s contains multiple serverids",
878 dhcp6_message_type_to_string(message
->type
));
882 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
888 case SD_DHCP6_OPTION_PREFERENCE
:
892 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
898 case SD_DHCP6_OPTION_STATUS_CODE
:
899 status
= dhcp6_option_parse_status(option
);
901 log_dhcp6_client(client
, "%s Status %s",
902 dhcp6_message_type_to_string(message
->type
),
903 dhcp6_message_status_to_string(status
));
904 dhcp6_lease_free_ia(&lease
->ia
);
905 dhcp6_lease_free_ia(&lease
->pd
);
912 case SD_DHCP6_OPTION_IA_NA
:
913 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
914 log_dhcp6_client(client
, "Information request ignoring IA NA option");
919 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
920 if (r
< 0 && r
!= -ENOMSG
)
923 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
927 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
928 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
929 dhcp6_message_type_to_string(message
->type
));
933 if (lease
->ia
.addresses
) {
934 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
935 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
940 case SD_DHCP6_OPTION_IA_PD
:
941 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
942 log_dhcp6_client(client
, "Information request ignoring IA PD option");
947 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
948 if (r
< 0 && r
!= -ENOMSG
)
951 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
955 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
956 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
957 dhcp6_message_type_to_string(message
->type
));
961 if (lease
->pd
.addresses
) {
962 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
963 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
968 case SD_DHCP6_OPTION_RAPID_COMMIT
:
969 r
= dhcp6_lease_set_rapid_commit(lease
);
975 case SD_DHCP6_OPTION_DNS_SERVERS
:
976 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
982 case SD_DHCP6_OPTION_DOMAIN_LIST
:
983 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
989 case SD_DHCP6_OPTION_NTP_SERVER
:
990 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
996 case SD_DHCP6_OPTION_SNTP_SERVERS
:
997 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
1004 pos
+= sizeof(*option
) + optlen
;
1008 log_dhcp6_client(client
, "%s has incomplete options",
1009 dhcp6_message_type_to_string(message
->type
));
1013 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1014 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1016 log_dhcp6_client(client
, "%s has no server id",
1017 dhcp6_message_type_to_string(message
->type
));
1022 if (lease
->ia
.addresses
) {
1023 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1024 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1027 if (lease
->pd
.addresses
) {
1028 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1029 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1036 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1037 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1044 if (reply
->type
!= DHCP6_REPLY
)
1047 r
= dhcp6_lease_new(&lease
);
1051 r
= client_parse_message(client
, reply
, len
, lease
);
1055 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1056 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1064 client_set_lease(client
, lease
);
1067 return DHCP6_STATE_BOUND
;
1070 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1071 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1072 uint8_t pref_advertise
= 0, pref_lease
= 0;
1075 if (advertise
->type
!= DHCP6_ADVERTISE
)
1078 r
= dhcp6_lease_new(&lease
);
1082 r
= client_parse_message(client
, advertise
, len
, lease
);
1086 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1090 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1092 if (r
< 0 || pref_advertise
> pref_lease
) {
1093 client_set_lease(client
, lease
);
1098 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1099 r
= DHCP6_STATE_REQUEST
;
1104 static int client_receive_message(
1110 sd_dhcp6_client
*client
= userdata
;
1111 DHCP6_CLIENT_DONT_DESTROY(client
);
1112 _cleanup_free_ DHCP6Message
*message
= NULL
;
1113 ssize_t buflen
, len
;
1118 assert(client
->event
);
1120 buflen
= next_datagram_size_fd(fd
);
1124 message
= malloc(buflen
);
1128 len
= recv(fd
, message
, buflen
, 0);
1130 if (IN_SET(errno
, EAGAIN
, EINTR
))
1133 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1136 if ((size_t) len
< sizeof(DHCP6Message
)) {
1137 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1141 switch(message
->type
) {
1149 case DHCP6_INFORMATION_REQUEST
:
1150 case DHCP6_RELAY_FORW
:
1151 case DHCP6_RELAY_REPL
:
1154 case DHCP6_ADVERTISE
:
1156 case DHCP6_RECONFIGURE
:
1160 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1164 if (client
->transaction_id
!= (message
->transaction_id
&
1165 htobe32(0x00ffffff)))
1168 switch (client
->state
) {
1169 case DHCP6_STATE_INFORMATION_REQUEST
:
1170 r
= client_receive_reply(client
, message
, len
);
1174 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1176 client_start(client
, DHCP6_STATE_STOPPED
);
1180 case DHCP6_STATE_SOLICITATION
:
1181 r
= client_receive_advertise(client
, message
, len
);
1183 if (r
== DHCP6_STATE_REQUEST
) {
1184 client_start(client
, r
);
1189 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1190 case DHCP6_STATE_REQUEST
:
1191 case DHCP6_STATE_RENEW
:
1192 case DHCP6_STATE_REBIND
:
1194 r
= client_receive_reply(client
, message
, len
);
1198 if (r
== DHCP6_STATE_BOUND
) {
1200 r
= client_start(client
, DHCP6_STATE_BOUND
);
1202 client_stop(client
, r
);
1206 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1211 case DHCP6_STATE_BOUND
:
1215 case DHCP6_STATE_STOPPED
:
1219 log_dhcp6_client(client
, "Recv %s",
1220 dhcp6_message_type_to_string(message
->type
));
1225 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1226 uint32_t *lifetime_t2
) {
1227 assert_return(client
, -EINVAL
);
1228 assert_return(client
->lease
, -EINVAL
);
1230 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1231 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1232 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1237 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1238 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1239 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1247 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1249 usec_t timeout
, time_now
;
1250 char time_string
[FORMAT_TIMESPAN_MAX
];
1251 uint32_t lifetime_t1
, lifetime_t2
;
1253 assert_return(client
, -EINVAL
);
1254 assert_return(client
->event
, -EINVAL
);
1255 assert_return(client
->ifindex
> 0, -EINVAL
);
1256 assert_return(client
->state
!= state
, -EINVAL
);
1258 client
->timeout_resend_expire
=
1259 sd_event_source_unref(client
->timeout_resend_expire
);
1260 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1261 client
->retransmit_time
= 0;
1262 client
->retransmit_count
= 0;
1264 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1268 if (!client
->receive_message
) {
1269 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1270 client
->fd
, EPOLLIN
, client_receive_message
,
1275 r
= sd_event_source_set_priority(client
->receive_message
,
1276 client
->event_priority
);
1280 r
= sd_event_source_set_description(client
->receive_message
,
1281 "dhcp6-receive-message");
1287 case DHCP6_STATE_STOPPED
:
1288 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1289 client
->state
= DHCP6_STATE_STOPPED
;
1295 case DHCP6_STATE_SOLICITATION
:
1296 client
->state
= DHCP6_STATE_SOLICITATION
;
1300 case DHCP6_STATE_INFORMATION_REQUEST
:
1301 case DHCP6_STATE_REQUEST
:
1302 case DHCP6_STATE_RENEW
:
1303 case DHCP6_STATE_REBIND
:
1305 client
->state
= state
;
1309 case DHCP6_STATE_BOUND
:
1311 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1315 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1316 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1317 lifetime_t1
, lifetime_t2
);
1322 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1324 log_dhcp6_client(client
, "T1 expires in %s",
1325 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1327 r
= sd_event_add_time(client
->event
,
1328 &client
->timeout_t1
,
1329 clock_boottime_or_monotonic(), time_now
+ timeout
,
1330 10 * USEC_PER_SEC
, client_timeout_t1
,
1335 r
= sd_event_source_set_priority(client
->timeout_t1
,
1336 client
->event_priority
);
1340 r
= sd_event_source_set_description(client
->timeout_t1
, "dhcp6-t1-timeout");
1344 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1346 log_dhcp6_client(client
, "T2 expires in %s",
1347 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1349 r
= sd_event_add_time(client
->event
,
1350 &client
->timeout_t2
,
1351 clock_boottime_or_monotonic(), time_now
+ timeout
,
1352 10 * USEC_PER_SEC
, client_timeout_t2
,
1357 r
= sd_event_source_set_priority(client
->timeout_t2
,
1358 client
->event_priority
);
1362 r
= sd_event_source_set_description(client
->timeout_t2
, "dhcp6-t2-timeout");
1366 client
->state
= state
;
1371 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1372 client
->transaction_start
= time_now
;
1374 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1375 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1380 r
= sd_event_source_set_priority(client
->timeout_resend
,
1381 client
->event_priority
);
1385 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1392 client_reset(client
);
1396 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1397 assert_return(client
, -EINVAL
);
1399 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1401 client
->fd
= safe_close(client
->fd
);
1406 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1407 assert_return(client
, -EINVAL
);
1409 return client
->state
!= DHCP6_STATE_STOPPED
;
1412 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1413 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1416 assert_return(client
, -EINVAL
);
1417 assert_return(client
->event
, -EINVAL
);
1418 assert_return(client
->ifindex
> 0, -EINVAL
);
1419 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1421 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1424 if (!client
->information_request
&& !client
->request
)
1427 r
= client_reset(client
);
1431 r
= client_ensure_iaid(client
);
1435 r
= client_ensure_duid(client
);
1439 if (client
->fd
< 0) {
1440 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1442 _cleanup_free_
char *p
= NULL
;
1444 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1445 return log_dhcp6_client_errno(client
, r
,
1446 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1452 if (client
->information_request
)
1453 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1455 log_dhcp6_client(client
, "Started in %s mode",
1456 client
->information_request
? "Information request":
1459 return client_start(client
, state
);
1462 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1465 assert_return(client
, -EINVAL
);
1466 assert_return(!client
->event
, -EBUSY
);
1469 client
->event
= sd_event_ref(event
);
1471 r
= sd_event_default(&client
->event
);
1476 client
->event_priority
= priority
;
1481 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1482 assert_return(client
, -EINVAL
);
1484 client
->event
= sd_event_unref(client
->event
);
1489 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1490 assert_return(client
, NULL
);
1492 return client
->event
;
1495 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1498 client_reset(client
);
1500 client
->fd
= safe_close(client
->fd
);
1502 sd_dhcp6_client_detach_event(client
);
1504 free(client
->req_opts
);
1506 return mfree(client
);
1509 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1511 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1512 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1515 assert_return(ret
, -EINVAL
);
1517 client
= new0(sd_dhcp6_client
, 1);
1522 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1523 client
->ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
;
1524 client
->ifindex
= -1;
1525 client
->request
= DHCP6_REQUEST_IA_NA
;
1528 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1529 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1530 if (!client
->req_opts
)
1533 for (t
= 0; t
< client
->req_opts_len
; t
++)
1534 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);
1536 *ret
= TAKE_PTR(client
);