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"
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"
29 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
31 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
33 DHCP6_REQUEST_IA_NA
= 1,
34 DHCP6_REQUEST_IA_TA
= 2, /* currently not used */
35 DHCP6_REQUEST_IA_PD
= 4,
38 struct sd_dhcp6_client
{
41 enum DHCP6State state
;
45 struct in6_addr local_address
;
46 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
51 sd_event_source
*timeout_t1
;
52 sd_event_source
*timeout_t2
;
54 be32_t transaction_id
;
55 usec_t transaction_start
;
56 struct sd_dhcp6_lease
*lease
;
58 bool information_request
;
61 size_t req_opts_allocated
;
64 sd_event_source
*receive_message
;
65 usec_t retransmit_time
;
66 uint8_t retransmit_count
;
67 sd_event_source
*timeout_resend
;
68 sd_event_source
*timeout_resend_expire
;
69 sd_dhcp6_client_callback_t callback
;
75 static const uint16_t default_req_opts
[] = {
76 SD_DHCP6_OPTION_DNS_SERVERS
,
77 SD_DHCP6_OPTION_DOMAIN_LIST
,
78 SD_DHCP6_OPTION_NTP_SERVER
,
79 SD_DHCP6_OPTION_SNTP_SERVERS
,
82 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
83 [DHCP6_SOLICIT
] = "SOLICIT",
84 [DHCP6_ADVERTISE
] = "ADVERTISE",
85 [DHCP6_REQUEST
] = "REQUEST",
86 [DHCP6_CONFIRM
] = "CONFIRM",
87 [DHCP6_RENEW
] = "RENEW",
88 [DHCP6_REBIND
] = "REBIND",
89 [DHCP6_REPLY
] = "REPLY",
90 [DHCP6_RELEASE
] = "RELEASE",
91 [DHCP6_DECLINE
] = "DECLINE",
92 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
93 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
94 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
95 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
98 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
100 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
101 [DHCP6_STATUS_SUCCESS
] = "Success",
102 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
103 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
104 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
105 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
106 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
109 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
111 #define DHCP6_CLIENT_DONT_DESTROY(client) \
112 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
114 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
116 int sd_dhcp6_client_set_callback(
117 sd_dhcp6_client
*client
,
118 sd_dhcp6_client_callback_t cb
,
121 assert_return(client
, -EINVAL
);
123 client
->callback
= cb
;
124 client
->userdata
= userdata
;
129 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
131 assert_return(client
, -EINVAL
);
132 assert_return(ifindex
>= -1, -EINVAL
);
133 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
135 client
->ifindex
= ifindex
;
139 int sd_dhcp6_client_set_local_address(
140 sd_dhcp6_client
*client
,
141 const struct in6_addr
*local_address
) {
143 assert_return(client
, -EINVAL
);
144 assert_return(local_address
, -EINVAL
);
145 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
147 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
149 client
->local_address
= *local_address
;
154 int sd_dhcp6_client_set_mac(
155 sd_dhcp6_client
*client
,
156 const uint8_t *addr
, size_t addr_len
,
159 assert_return(client
, -EINVAL
);
160 assert_return(addr
, -EINVAL
);
161 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
162 assert_return(arp_type
> 0, -EINVAL
);
164 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
166 if (arp_type
== ARPHRD_ETHER
)
167 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
168 else if (arp_type
== ARPHRD_INFINIBAND
)
169 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
173 if (client
->mac_addr_len
== addr_len
&&
174 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
177 memcpy(&client
->mac_addr
, addr
, addr_len
);
178 client
->mac_addr_len
= addr_len
;
179 client
->arp_type
= arp_type
;
184 static int client_ensure_duid(sd_dhcp6_client
*client
) {
185 if (client
->duid_len
!= 0)
188 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
192 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
193 * without further modification. Otherwise, if duid_type is supported, DUID
194 * is set based on that type. Otherwise, an error is returned.
196 static int dhcp6_client_set_duid_internal(
197 sd_dhcp6_client
*client
,
204 assert_return(client
, -EINVAL
);
205 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
206 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
209 r
= dhcp_validate_duid_len(duid_type
, duid_len
, true);
211 r
= dhcp_validate_duid_len(duid_type
, duid_len
, false);
214 log_dhcp6_client(client
, "Setting DUID of type %u with unexpected content", duid_type
);
217 client
->duid
.type
= htobe16(duid_type
);
218 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
219 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
223 if (client
->mac_addr_len
== 0)
226 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
231 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
236 if (client
->mac_addr_len
== 0)
239 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
244 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
255 int sd_dhcp6_client_set_duid(
256 sd_dhcp6_client
*client
,
260 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
263 int sd_dhcp6_client_set_duid_llt(
264 sd_dhcp6_client
*client
,
266 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
269 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
270 assert_return(client
, -EINVAL
);
271 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
273 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
274 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
275 client
->iaid_set
= true;
280 int sd_dhcp6_client_set_fqdn(
281 sd_dhcp6_client
*client
,
284 assert_return(client
, -EINVAL
);
286 /* Make sure FQDN qualifies as DNS and as Linux hostname */
288 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
291 return free_and_strdup(&client
->fqdn
, fqdn
);
294 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
295 assert_return(client
, -EINVAL
);
296 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
298 client
->information_request
= enabled
;
303 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
304 assert_return(client
, -EINVAL
);
305 assert_return(enabled
, -EINVAL
);
307 *enabled
= client
->information_request
;
312 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
315 assert_return(client
, -EINVAL
);
316 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
320 case SD_DHCP6_OPTION_DNS_SERVERS
:
321 case SD_DHCP6_OPTION_DOMAIN_LIST
:
322 case SD_DHCP6_OPTION_SNTP_SERVERS
:
323 case SD_DHCP6_OPTION_NTP_SERVER
:
324 case SD_DHCP6_OPTION_RAPID_COMMIT
:
331 for (t
= 0; t
< client
->req_opts_len
; t
++)
332 if (client
->req_opts
[t
] == htobe16(option
))
335 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
336 client
->req_opts_len
+ 1))
339 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
344 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
345 assert_return(client
, -EINVAL
);
346 assert_return(delegation
, -EINVAL
);
348 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
353 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
354 assert_return(client
, -EINVAL
);
356 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
361 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
362 assert_return(client
, -EINVAL
);
363 assert_return(request
, -EINVAL
);
365 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
370 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
371 assert_return(client
, -EINVAL
);
373 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
378 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
379 assert_return(client
, -EINVAL
);
381 client
->transaction_id
= transaction_id
;
386 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
387 assert_return(client
, -EINVAL
);
393 *ret
= client
->lease
;
398 static void client_notify(sd_dhcp6_client
*client
, int event
) {
401 if (client
->callback
)
402 client
->callback(client
, event
, client
->userdata
);
405 static int client_reset(sd_dhcp6_client
*client
) {
408 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
410 client
->receive_message
=
411 sd_event_source_unref(client
->receive_message
);
413 client
->transaction_id
= 0;
414 client
->transaction_start
= 0;
416 client
->retransmit_time
= 0;
417 client
->retransmit_count
= 0;
419 (void) event_source_disable(client
->timeout_resend
);
420 (void) event_source_disable(client
->timeout_resend_expire
);
421 (void) event_source_disable(client
->timeout_t1
);
422 (void) event_source_disable(client
->timeout_t2
);
424 client
->state
= DHCP6_STATE_STOPPED
;
429 static void client_stop(sd_dhcp6_client
*client
, int error
) {
430 DHCP6_CLIENT_DONT_DESTROY(client
);
434 client_notify(client
, error
);
436 client_reset(client
);
439 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
440 _cleanup_free_ DHCP6Message
*message
= NULL
;
441 struct in6_addr all_servers
=
442 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
443 size_t len
, optlen
= 512;
451 len
= sizeof(DHCP6Message
) + optlen
;
453 message
= malloc0(len
);
457 opt
= (uint8_t *)(message
+ 1);
459 message
->transaction_id
= client
->transaction_id
;
461 switch(client
->state
) {
462 case DHCP6_STATE_INFORMATION_REQUEST
:
463 message
->type
= DHCP6_INFORMATION_REQUEST
;
467 case DHCP6_STATE_SOLICITATION
:
468 message
->type
= DHCP6_SOLICIT
;
470 r
= dhcp6_option_append(&opt
, &optlen
,
471 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
475 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
476 r
= dhcp6_option_append_ia(&opt
, &optlen
,
483 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
488 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
489 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
499 case DHCP6_STATE_REQUEST
:
500 case DHCP6_STATE_RENEW
:
502 if (client
->state
== DHCP6_STATE_REQUEST
)
503 message
->type
= DHCP6_REQUEST
;
505 message
->type
= DHCP6_RENEW
;
507 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
508 client
->lease
->serverid_len
,
509 client
->lease
->serverid
);
513 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
514 r
= dhcp6_option_append_ia(&opt
, &optlen
,
521 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
526 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
527 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
537 case DHCP6_STATE_REBIND
:
538 message
->type
= DHCP6_REBIND
;
540 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
541 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
547 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
552 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
553 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
563 case DHCP6_STATE_STOPPED
:
564 case DHCP6_STATE_BOUND
:
568 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
569 client
->req_opts_len
* sizeof(be16_t
),
574 assert(client
->duid_len
);
575 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
576 client
->duid_len
, &client
->duid
);
580 elapsed_usec
= time_now
- client
->transaction_start
;
581 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
582 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
584 elapsed_time
= 0xffff;
586 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
587 sizeof(elapsed_time
), &elapsed_time
);
591 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
596 log_dhcp6_client(client
, "Sent %s",
597 dhcp6_message_type_to_string(message
->type
));
602 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
603 sd_dhcp6_client
*client
= userdata
;
607 assert(client
->lease
);
609 (void) event_source_disable(client
->timeout_t2
);
611 log_dhcp6_client(client
, "Timeout T2");
613 client_start(client
, DHCP6_STATE_REBIND
);
618 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
619 sd_dhcp6_client
*client
= userdata
;
623 assert(client
->lease
);
625 (void) event_source_disable(client
->timeout_t1
);
627 log_dhcp6_client(client
, "Timeout T1");
629 client_start(client
, DHCP6_STATE_RENEW
);
634 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
635 sd_dhcp6_client
*client
= userdata
;
636 DHCP6_CLIENT_DONT_DESTROY(client
);
637 enum DHCP6State state
;
641 assert(client
->event
);
643 state
= client
->state
;
645 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
647 /* RFC 3315, section 18.1.4., says that "...the client may choose to
648 use a Solicit message to locate a new DHCP server..." */
649 if (state
== DHCP6_STATE_REBIND
)
650 client_start(client
, DHCP6_STATE_SOLICITATION
);
655 static usec_t
client_timeout_compute_random(usec_t val
) {
656 return val
- val
/ 10 +
657 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
660 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
662 sd_dhcp6_client
*client
= userdata
;
663 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
664 usec_t max_retransmit_duration
= 0;
665 uint8_t max_retransmit_count
= 0;
666 char time_string
[FORMAT_TIMESPAN_MAX
];
671 assert(client
->event
);
673 (void) event_source_disable(client
->timeout_resend
);
675 switch (client
->state
) {
676 case DHCP6_STATE_INFORMATION_REQUEST
:
677 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
678 max_retransmit_time
= DHCP6_INF_MAX_RT
;
682 case DHCP6_STATE_SOLICITATION
:
684 if (client
->retransmit_count
&& client
->lease
) {
685 client_start(client
, DHCP6_STATE_REQUEST
);
689 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
690 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
694 case DHCP6_STATE_REQUEST
:
695 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
696 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
697 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
701 case DHCP6_STATE_RENEW
:
702 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
703 max_retransmit_time
= DHCP6_REN_MAX_RT
;
705 /* RFC 3315, section 18.1.3. says max retransmit duration will
706 be the remaining time until T2. Instead of setting MRD,
707 wait for T2 to trigger with the same end result */
711 case DHCP6_STATE_REBIND
:
712 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
713 max_retransmit_time
= DHCP6_REB_MAX_RT
;
715 if (event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
716 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
719 client_stop(client
, r
);
722 max_retransmit_duration
= expire
* USEC_PER_SEC
;
727 case DHCP6_STATE_STOPPED
:
728 case DHCP6_STATE_BOUND
:
732 if (max_retransmit_count
&&
733 client
->retransmit_count
>= max_retransmit_count
) {
734 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
738 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
742 r
= client_send_message(client
, time_now
);
744 client
->retransmit_count
++;
746 if (!client
->retransmit_time
) {
747 client
->retransmit_time
=
748 client_timeout_compute_random(init_retransmit_time
);
750 if (client
->state
== DHCP6_STATE_SOLICITATION
)
751 client
->retransmit_time
+= init_retransmit_time
/ 10;
754 if (max_retransmit_time
&&
755 client
->retransmit_time
> max_retransmit_time
/ 2)
756 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
758 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
761 log_dhcp6_client(client
, "Next retransmission in %s",
762 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
764 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
765 clock_boottime_or_monotonic(),
766 time_now
+ client
->retransmit_time
, 10 * USEC_PER_MSEC
,
767 client_timeout_resend
, client
,
768 client
->event_priority
, "dhcp6-resend-timer", true);
772 if (max_retransmit_duration
&& event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
774 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
775 max_retransmit_duration
/ USEC_PER_SEC
);
777 r
= event_reset_time(client
->event
, &client
->timeout_resend_expire
,
778 clock_boottime_or_monotonic(),
779 time_now
+ max_retransmit_duration
, USEC_PER_SEC
,
780 client_timeout_resend_expire
, client
,
781 client
->event_priority
, "dhcp6-resend-expire-timer", true);
788 client_stop(client
, r
);
793 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
799 if (client
->iaid_set
)
802 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, true, &iaid
);
806 client
->ia_na
.ia_na
.id
= iaid
;
807 client
->ia_pd
.ia_pd
.id
= iaid
;
808 client
->iaid_set
= true;
813 static int client_parse_message(
814 sd_dhcp6_client
*client
,
815 DHCP6Message
*message
,
817 sd_dhcp6_lease
*lease
) {
819 uint32_t lt_t1
= ~0, lt_t2
= ~0;
820 bool clientid
= false;
826 assert(len
>= sizeof(DHCP6Message
));
829 len
-= sizeof(DHCP6Message
);
832 DHCP6Option
*option
= (DHCP6Option
*) &message
->options
[pos
];
833 uint16_t optcode
, optlen
;
838 if (len
< pos
+ offsetof(DHCP6Option
, data
))
841 optcode
= be16toh(option
->code
);
842 optlen
= be16toh(option
->len
);
843 optval
= option
->data
;
845 if (len
< pos
+ offsetof(DHCP6Option
, data
) + optlen
)
849 case SD_DHCP6_OPTION_CLIENTID
:
851 log_dhcp6_client(client
, "%s contains multiple clientids",
852 dhcp6_message_type_to_string(message
->type
));
856 if (optlen
!= client
->duid_len
||
857 memcmp(&client
->duid
, optval
, optlen
) != 0) {
858 log_dhcp6_client(client
, "%s DUID does not match",
859 dhcp6_message_type_to_string(message
->type
));
867 case SD_DHCP6_OPTION_SERVERID
:
868 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
870 log_dhcp6_client(client
, "%s contains multiple serverids",
871 dhcp6_message_type_to_string(message
->type
));
875 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
881 case SD_DHCP6_OPTION_PREFERENCE
:
885 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
891 case SD_DHCP6_OPTION_STATUS_CODE
:
892 status
= dhcp6_option_parse_status(option
, optlen
+ sizeof(DHCP6Option
));
897 log_dhcp6_client(client
, "%s Status %s",
898 dhcp6_message_type_to_string(message
->type
),
899 dhcp6_message_status_to_string(status
));
906 case SD_DHCP6_OPTION_IA_NA
:
907 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
908 log_dhcp6_client(client
, "Information request ignoring IA NA option");
913 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
914 if (r
< 0 && r
!= -ENOMSG
)
917 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
921 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
922 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
923 dhcp6_message_type_to_string(message
->type
));
927 if (lease
->ia
.addresses
) {
928 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
929 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
934 case SD_DHCP6_OPTION_IA_PD
:
935 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
936 log_dhcp6_client(client
, "Information request ignoring IA PD option");
941 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
942 if (r
< 0 && r
!= -ENOMSG
)
945 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
949 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
950 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
951 dhcp6_message_type_to_string(message
->type
));
955 if (lease
->pd
.addresses
) {
956 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
957 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
962 case SD_DHCP6_OPTION_RAPID_COMMIT
:
963 r
= dhcp6_lease_set_rapid_commit(lease
);
969 case SD_DHCP6_OPTION_DNS_SERVERS
:
970 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
976 case SD_DHCP6_OPTION_DOMAIN_LIST
:
977 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
983 case SD_DHCP6_OPTION_NTP_SERVER
:
984 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
990 case SD_DHCP6_OPTION_SNTP_SERVERS
:
991 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
998 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1002 log_dhcp6_client(client
, "%s has incomplete options",
1003 dhcp6_message_type_to_string(message
->type
));
1007 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1008 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1010 log_dhcp6_client(client
, "%s has no server id",
1011 dhcp6_message_type_to_string(message
->type
));
1016 if (lease
->ia
.addresses
) {
1017 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1018 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1021 if (lease
->pd
.addresses
) {
1022 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1023 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1030 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1031 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1038 if (reply
->type
!= DHCP6_REPLY
)
1041 r
= dhcp6_lease_new(&lease
);
1045 r
= client_parse_message(client
, reply
, len
, lease
);
1049 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1050 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1058 sd_dhcp6_lease_unref(client
->lease
);
1059 client
->lease
= TAKE_PTR(lease
);
1061 return DHCP6_STATE_BOUND
;
1064 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1065 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1066 uint8_t pref_advertise
= 0, pref_lease
= 0;
1069 if (advertise
->type
!= DHCP6_ADVERTISE
)
1072 r
= dhcp6_lease_new(&lease
);
1076 r
= client_parse_message(client
, advertise
, len
, lease
);
1080 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1084 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1086 if (r
< 0 || pref_advertise
> pref_lease
) {
1087 sd_dhcp6_lease_unref(client
->lease
);
1088 client
->lease
= TAKE_PTR(lease
);
1092 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1093 r
= DHCP6_STATE_REQUEST
;
1098 static int client_receive_message(
1104 sd_dhcp6_client
*client
= userdata
;
1105 DHCP6_CLIENT_DONT_DESTROY(client
);
1106 _cleanup_free_ DHCP6Message
*message
= NULL
;
1107 ssize_t buflen
, len
;
1112 assert(client
->event
);
1114 buflen
= next_datagram_size_fd(fd
);
1115 if (buflen
== -ENETDOWN
) {
1116 /* the link is down. Don't return an error or the I/O event
1117 source will be disconnected and we won't be able to receive
1118 packets again when the link comes back. */
1124 message
= malloc(buflen
);
1128 len
= recv(fd
, message
, buflen
, 0);
1130 /* see comment above for why we shouldn't error out on ENETDOWN. */
1131 if (IN_SET(errno
, EAGAIN
, EINTR
, ENETDOWN
))
1134 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1137 if ((size_t) len
< sizeof(DHCP6Message
)) {
1138 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1142 switch(message
->type
) {
1150 case DHCP6_INFORMATION_REQUEST
:
1151 case DHCP6_RELAY_FORW
:
1152 case DHCP6_RELAY_REPL
:
1155 case DHCP6_ADVERTISE
:
1157 case DHCP6_RECONFIGURE
:
1161 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1165 if (client
->transaction_id
!= (message
->transaction_id
&
1166 htobe32(0x00ffffff)))
1169 switch (client
->state
) {
1170 case DHCP6_STATE_INFORMATION_REQUEST
:
1171 r
= client_receive_reply(client
, message
, len
);
1175 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1177 client_start(client
, DHCP6_STATE_STOPPED
);
1181 case DHCP6_STATE_SOLICITATION
:
1182 r
= client_receive_advertise(client
, message
, len
);
1184 if (r
== DHCP6_STATE_REQUEST
) {
1185 client_start(client
, r
);
1190 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1191 case DHCP6_STATE_REQUEST
:
1192 case DHCP6_STATE_RENEW
:
1193 case DHCP6_STATE_REBIND
:
1195 r
= client_receive_reply(client
, message
, len
);
1199 if (r
== DHCP6_STATE_BOUND
) {
1201 r
= client_start(client
, DHCP6_STATE_BOUND
);
1203 client_stop(client
, r
);
1207 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1212 case DHCP6_STATE_BOUND
:
1216 case DHCP6_STATE_STOPPED
:
1220 log_dhcp6_client(client
, "Recv %s",
1221 dhcp6_message_type_to_string(message
->type
));
1226 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1227 uint32_t *lifetime_t2
) {
1228 assert_return(client
, -EINVAL
);
1229 assert_return(client
->lease
, -EINVAL
);
1231 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1232 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1233 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1238 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1239 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1240 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1248 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1250 usec_t timeout
, time_now
;
1251 char time_string
[FORMAT_TIMESPAN_MAX
];
1252 uint32_t lifetime_t1
, lifetime_t2
;
1254 assert_return(client
, -EINVAL
);
1255 assert_return(client
->event
, -EINVAL
);
1256 assert_return(client
->ifindex
> 0, -EINVAL
);
1257 assert_return(client
->state
!= state
, -EINVAL
);
1259 (void) event_source_disable(client
->timeout_resend_expire
);
1260 (void) event_source_disable(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
= event_reset_time(client
->event
, &client
->timeout_t1
,
1328 clock_boottime_or_monotonic(),
1329 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1330 client_timeout_t1
, client
,
1331 client
->event_priority
, "dhcp6-t1-timeout", true);
1335 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1337 log_dhcp6_client(client
, "T2 expires in %s",
1338 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1340 r
= event_reset_time(client
->event
, &client
->timeout_t2
,
1341 clock_boottime_or_monotonic(),
1342 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1343 client_timeout_t2
, client
,
1344 client
->event_priority
, "dhcp6-t2-timeout", true);
1348 client
->state
= state
;
1353 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1354 client
->transaction_start
= time_now
;
1356 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
1357 clock_boottime_or_monotonic(),
1359 client_timeout_resend
, client
,
1360 client
->event_priority
, "dhcp6-resend-timeout", true);
1367 client_reset(client
);
1371 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1372 assert_return(client
, -EINVAL
);
1374 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1376 client
->fd
= safe_close(client
->fd
);
1381 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1382 assert_return(client
, -EINVAL
);
1384 return client
->state
!= DHCP6_STATE_STOPPED
;
1387 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1388 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1391 assert_return(client
, -EINVAL
);
1392 assert_return(client
->event
, -EINVAL
);
1393 assert_return(client
->ifindex
> 0, -EINVAL
);
1394 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1396 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1399 if (!client
->information_request
&& !client
->request
)
1402 r
= client_reset(client
);
1406 r
= client_ensure_iaid(client
);
1410 r
= client_ensure_duid(client
);
1414 if (client
->fd
< 0) {
1415 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1417 _cleanup_free_
char *p
= NULL
;
1419 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1420 return log_dhcp6_client_errno(client
, r
,
1421 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1427 if (client
->information_request
)
1428 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1430 log_dhcp6_client(client
, "Started in %s mode",
1431 client
->information_request
? "Information request":
1434 return client_start(client
, state
);
1437 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1440 assert_return(client
, -EINVAL
);
1441 assert_return(!client
->event
, -EBUSY
);
1444 client
->event
= sd_event_ref(event
);
1446 r
= sd_event_default(&client
->event
);
1451 client
->event_priority
= priority
;
1456 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1457 assert_return(client
, -EINVAL
);
1459 client
->event
= sd_event_unref(client
->event
);
1464 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1465 assert_return(client
, NULL
);
1467 return client
->event
;
1470 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1473 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1474 client
->timeout_resend_expire
= sd_event_source_unref(client
->timeout_resend_expire
);
1475 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
1476 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
1478 client_reset(client
);
1480 client
->fd
= safe_close(client
->fd
);
1482 sd_dhcp6_client_detach_event(client
);
1484 free(client
->req_opts
);
1486 return mfree(client
);
1489 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1491 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1492 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1493 _cleanup_free_ be16_t
*req_opts
= NULL
;
1496 assert_return(ret
, -EINVAL
);
1498 req_opts
= new(be16_t
, ELEMENTSOF(default_req_opts
));
1502 for (t
= 0; t
< ELEMENTSOF(default_req_opts
); t
++)
1503 req_opts
[t
] = htobe16(default_req_opts
[t
]);
1505 client
= new(sd_dhcp6_client
, 1);
1509 *client
= (sd_dhcp6_client
) {
1511 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1512 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1514 .request
= DHCP6_REQUEST_IA_NA
,
1516 .req_opts_len
= ELEMENTSOF(default_req_opts
),
1517 .req_opts
= TAKE_PTR(req_opts
),
1520 *ret
= TAKE_PTR(client
);