1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
9 #include <linux/if_arp.h>
10 #include <linux/if_infiniband.h>
12 #include "sd-dhcp6-client.h"
14 #include "alloc-util.h"
15 #include "dhcp-identifier.h"
16 #include "dhcp6-internal.h"
17 #include "dhcp6-lease-internal.h"
18 #include "dhcp6-protocol.h"
19 #include "dns-domain.h"
20 #include "event-util.h"
22 #include "hostname-util.h"
23 #include "in-addr-util.h"
24 #include "network-internal.h"
25 #include "random-util.h"
26 #include "socket-util.h"
27 #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 struct in6_addr local_address
;
50 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
55 sd_event_source
*timeout_t1
;
56 sd_event_source
*timeout_t2
;
58 be32_t transaction_id
;
59 usec_t transaction_start
;
60 struct sd_dhcp6_lease
*lease
;
62 bool information_request
;
65 size_t req_opts_allocated
;
68 sd_event_source
*receive_message
;
69 usec_t retransmit_time
;
70 uint8_t retransmit_count
;
71 sd_event_source
*timeout_resend
;
72 sd_event_source
*timeout_resend_expire
;
73 sd_dhcp6_client_callback_t callback
;
77 usec_t information_request_time_usec
;
78 usec_t information_refresh_time_usec
;
81 static const uint16_t default_req_opts
[] = {
82 SD_DHCP6_OPTION_DNS_SERVERS
,
83 SD_DHCP6_OPTION_DOMAIN_LIST
,
84 SD_DHCP6_OPTION_NTP_SERVER
,
85 SD_DHCP6_OPTION_SNTP_SERVERS
,
88 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
89 [DHCP6_SOLICIT
] = "SOLICIT",
90 [DHCP6_ADVERTISE
] = "ADVERTISE",
91 [DHCP6_REQUEST
] = "REQUEST",
92 [DHCP6_CONFIRM
] = "CONFIRM",
93 [DHCP6_RENEW
] = "RENEW",
94 [DHCP6_REBIND
] = "REBIND",
95 [DHCP6_REPLY
] = "REPLY",
96 [DHCP6_RELEASE
] = "RELEASE",
97 [DHCP6_DECLINE
] = "DECLINE",
98 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
99 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
100 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
101 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
104 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
106 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
107 [DHCP6_STATUS_SUCCESS
] = "Success",
108 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
109 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
110 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
111 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
112 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
115 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
117 #define DHCP6_CLIENT_DONT_DESTROY(client) \
118 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
120 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
122 int sd_dhcp6_client_set_callback(
123 sd_dhcp6_client
*client
,
124 sd_dhcp6_client_callback_t cb
,
127 assert_return(client
, -EINVAL
);
129 client
->callback
= cb
;
130 client
->userdata
= userdata
;
135 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
137 assert_return(client
, -EINVAL
);
138 assert_return(ifindex
>= -1, -EINVAL
);
139 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
141 client
->ifindex
= ifindex
;
145 int sd_dhcp6_client_set_local_address(
146 sd_dhcp6_client
*client
,
147 const struct in6_addr
*local_address
) {
149 assert_return(client
, -EINVAL
);
150 assert_return(local_address
, -EINVAL
);
151 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
153 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
155 client
->local_address
= *local_address
;
160 int sd_dhcp6_client_set_mac(
161 sd_dhcp6_client
*client
,
162 const uint8_t *addr
, size_t addr_len
,
165 assert_return(client
, -EINVAL
);
166 assert_return(addr
, -EINVAL
);
167 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
168 assert_return(arp_type
> 0, -EINVAL
);
170 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
172 if (arp_type
== ARPHRD_ETHER
)
173 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
174 else if (arp_type
== ARPHRD_INFINIBAND
)
175 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
179 if (client
->mac_addr_len
== addr_len
&&
180 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
183 memcpy(&client
->mac_addr
, addr
, addr_len
);
184 client
->mac_addr_len
= addr_len
;
185 client
->arp_type
= arp_type
;
190 static int client_ensure_duid(sd_dhcp6_client
*client
) {
191 if (client
->duid_len
!= 0)
194 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
198 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
199 * without further modification. Otherwise, if duid_type is supported, DUID
200 * is set based on that type. Otherwise, an error is returned.
202 static int dhcp6_client_set_duid_internal(
203 sd_dhcp6_client
*client
,
210 assert_return(client
, -EINVAL
);
211 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
212 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
215 r
= dhcp_validate_duid_len(duid_type
, duid_len
, true);
217 r
= dhcp_validate_duid_len(duid_type
, duid_len
, false);
220 log_dhcp6_client(client
, "Setting DUID of type %u with unexpected content", duid_type
);
223 client
->duid
.type
= htobe16(duid_type
);
224 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
225 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
229 if (client
->mac_addr_len
== 0)
232 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
237 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
242 if (client
->mac_addr_len
== 0)
245 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
250 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
261 int sd_dhcp6_client_set_duid(
262 sd_dhcp6_client
*client
,
266 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
269 int sd_dhcp6_client_set_duid_llt(
270 sd_dhcp6_client
*client
,
272 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
275 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
276 assert_return(client
, -EINVAL
);
277 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
279 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
280 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
281 client
->iaid_set
= true;
286 int sd_dhcp6_client_set_fqdn(
287 sd_dhcp6_client
*client
,
290 assert_return(client
, -EINVAL
);
292 /* Make sure FQDN qualifies as DNS and as Linux hostname */
294 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
297 return free_and_strdup(&client
->fqdn
, fqdn
);
300 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
301 assert_return(client
, -EINVAL
);
302 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
304 client
->information_request
= enabled
;
309 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
310 assert_return(client
, -EINVAL
);
311 assert_return(enabled
, -EINVAL
);
313 *enabled
= client
->information_request
;
318 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
321 assert_return(client
, -EINVAL
);
322 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
326 case SD_DHCP6_OPTION_DNS_SERVERS
:
327 case SD_DHCP6_OPTION_DOMAIN_LIST
:
328 case SD_DHCP6_OPTION_SNTP_SERVERS
:
329 case SD_DHCP6_OPTION_NTP_SERVER
:
330 case SD_DHCP6_OPTION_RAPID_COMMIT
:
337 for (t
= 0; t
< client
->req_opts_len
; t
++)
338 if (client
->req_opts
[t
] == htobe16(option
))
341 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
342 client
->req_opts_len
+ 1))
345 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
350 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
351 assert_return(client
, -EINVAL
);
352 assert_return(delegation
, -EINVAL
);
354 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
359 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
360 assert_return(client
, -EINVAL
);
362 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
367 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
368 assert_return(client
, -EINVAL
);
369 assert_return(request
, -EINVAL
);
371 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
376 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
377 assert_return(client
, -EINVAL
);
379 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
384 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
385 assert_return(client
, -EINVAL
);
387 client
->transaction_id
= transaction_id
;
392 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
393 assert_return(client
, -EINVAL
);
399 *ret
= client
->lease
;
404 static void client_notify(sd_dhcp6_client
*client
, int event
) {
407 if (client
->callback
)
408 client
->callback(client
, event
, client
->userdata
);
411 static int client_reset(sd_dhcp6_client
*client
) {
414 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
416 client
->receive_message
=
417 sd_event_source_unref(client
->receive_message
);
419 client
->transaction_id
= 0;
420 client
->transaction_start
= 0;
422 client
->retransmit_time
= 0;
423 client
->retransmit_count
= 0;
425 (void) event_source_disable(client
->timeout_resend
);
426 (void) event_source_disable(client
->timeout_resend_expire
);
427 (void) event_source_disable(client
->timeout_t1
);
428 (void) event_source_disable(client
->timeout_t2
);
430 client
->state
= DHCP6_STATE_STOPPED
;
435 static void client_stop(sd_dhcp6_client
*client
, int error
) {
436 DHCP6_CLIENT_DONT_DESTROY(client
);
440 client_notify(client
, error
);
442 client_reset(client
);
445 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
446 _cleanup_free_ DHCP6Message
*message
= NULL
;
447 struct in6_addr all_servers
=
448 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
449 size_t len
, optlen
= 512;
457 len
= sizeof(DHCP6Message
) + optlen
;
459 message
= malloc0(len
);
463 opt
= (uint8_t *)(message
+ 1);
465 message
->transaction_id
= client
->transaction_id
;
467 switch(client
->state
) {
468 case DHCP6_STATE_INFORMATION_REQUEST
:
469 message
->type
= DHCP6_INFORMATION_REQUEST
;
473 case DHCP6_STATE_SOLICITATION
:
474 message
->type
= DHCP6_SOLICIT
;
476 r
= dhcp6_option_append(&opt
, &optlen
,
477 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
481 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
482 r
= dhcp6_option_append_ia(&opt
, &optlen
,
489 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
494 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
495 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
505 case DHCP6_STATE_REQUEST
:
506 case DHCP6_STATE_RENEW
:
508 if (client
->state
== DHCP6_STATE_REQUEST
)
509 message
->type
= DHCP6_REQUEST
;
511 message
->type
= DHCP6_RENEW
;
513 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
514 client
->lease
->serverid_len
,
515 client
->lease
->serverid
);
519 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
520 r
= dhcp6_option_append_ia(&opt
, &optlen
,
527 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
532 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
533 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
543 case DHCP6_STATE_REBIND
:
544 message
->type
= DHCP6_REBIND
;
546 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
547 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
553 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
558 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
559 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
569 case DHCP6_STATE_STOPPED
:
570 case DHCP6_STATE_BOUND
:
574 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
575 client
->req_opts_len
* sizeof(be16_t
),
580 assert(client
->duid_len
);
581 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
582 client
->duid_len
, &client
->duid
);
586 elapsed_usec
= time_now
- client
->transaction_start
;
587 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
588 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
590 elapsed_time
= 0xffff;
592 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
593 sizeof(elapsed_time
), &elapsed_time
);
597 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
602 log_dhcp6_client(client
, "Sent %s",
603 dhcp6_message_type_to_string(message
->type
));
608 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
609 sd_dhcp6_client
*client
= userdata
;
613 assert(client
->lease
);
615 (void) event_source_disable(client
->timeout_t2
);
617 log_dhcp6_client(client
, "Timeout T2");
619 client_start(client
, DHCP6_STATE_REBIND
);
624 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
625 sd_dhcp6_client
*client
= userdata
;
629 assert(client
->lease
);
631 (void) event_source_disable(client
->timeout_t1
);
633 log_dhcp6_client(client
, "Timeout T1");
635 client_start(client
, DHCP6_STATE_RENEW
);
640 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
641 sd_dhcp6_client
*client
= userdata
;
642 DHCP6_CLIENT_DONT_DESTROY(client
);
643 enum DHCP6State state
;
647 assert(client
->event
);
649 state
= client
->state
;
651 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
653 /* RFC 3315, section 18.1.4., says that "...the client may choose to
654 use a Solicit message to locate a new DHCP server..." */
655 if (state
== DHCP6_STATE_REBIND
)
656 client_start(client
, DHCP6_STATE_SOLICITATION
);
661 static usec_t
client_timeout_compute_random(usec_t val
) {
662 return val
- val
/ 10 +
663 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
666 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
668 sd_dhcp6_client
*client
= userdata
;
669 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
670 usec_t max_retransmit_duration
= 0;
671 uint8_t max_retransmit_count
= 0;
672 char time_string
[FORMAT_TIMESPAN_MAX
];
677 assert(client
->event
);
679 (void) event_source_disable(client
->timeout_resend
);
681 switch (client
->state
) {
682 case DHCP6_STATE_INFORMATION_REQUEST
:
683 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
684 max_retransmit_time
= DHCP6_INF_MAX_RT
;
688 case DHCP6_STATE_SOLICITATION
:
690 if (client
->retransmit_count
&& client
->lease
) {
691 client_start(client
, DHCP6_STATE_REQUEST
);
695 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
696 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
700 case DHCP6_STATE_REQUEST
:
701 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
702 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
703 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
707 case DHCP6_STATE_RENEW
:
708 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
709 max_retransmit_time
= DHCP6_REN_MAX_RT
;
711 /* RFC 3315, section 18.1.3. says max retransmit duration will
712 be the remaining time until T2. Instead of setting MRD,
713 wait for T2 to trigger with the same end result */
717 case DHCP6_STATE_REBIND
:
718 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
719 max_retransmit_time
= DHCP6_REB_MAX_RT
;
721 if (event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
722 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
725 client_stop(client
, r
);
728 max_retransmit_duration
= expire
* USEC_PER_SEC
;
733 case DHCP6_STATE_STOPPED
:
734 case DHCP6_STATE_BOUND
:
738 if (max_retransmit_count
&&
739 client
->retransmit_count
>= max_retransmit_count
) {
740 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
744 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
748 r
= client_send_message(client
, time_now
);
750 client
->retransmit_count
++;
752 if (!client
->retransmit_time
) {
753 client
->retransmit_time
=
754 client_timeout_compute_random(init_retransmit_time
);
756 if (client
->state
== DHCP6_STATE_SOLICITATION
)
757 client
->retransmit_time
+= init_retransmit_time
/ 10;
760 if (max_retransmit_time
&&
761 client
->retransmit_time
> max_retransmit_time
/ 2)
762 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
764 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
767 log_dhcp6_client(client
, "Next retransmission in %s",
768 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
770 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
771 clock_boottime_or_monotonic(),
772 time_now
+ client
->retransmit_time
, 10 * USEC_PER_MSEC
,
773 client_timeout_resend
, client
,
774 client
->event_priority
, "dhcp6-resend-timer", true);
778 if (max_retransmit_duration
&& event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
780 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
781 max_retransmit_duration
/ USEC_PER_SEC
);
783 r
= event_reset_time(client
->event
, &client
->timeout_resend_expire
,
784 clock_boottime_or_monotonic(),
785 time_now
+ max_retransmit_duration
, USEC_PER_SEC
,
786 client_timeout_resend_expire
, client
,
787 client
->event_priority
, "dhcp6-resend-expire-timer", true);
794 client_stop(client
, r
);
799 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
805 if (client
->iaid_set
)
808 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, true, &iaid
);
812 client
->ia_na
.ia_na
.id
= iaid
;
813 client
->ia_pd
.ia_pd
.id
= iaid
;
814 client
->iaid_set
= true;
819 static int client_parse_message(
820 sd_dhcp6_client
*client
,
821 DHCP6Message
*message
,
823 sd_dhcp6_lease
*lease
) {
825 uint32_t lt_t1
= ~0, lt_t2
= ~0;
826 bool clientid
= false;
828 usec_t irt
= IRT_DEFAULT
;
833 assert(len
>= sizeof(DHCP6Message
));
836 len
-= sizeof(DHCP6Message
);
839 DHCP6Option
*option
= (DHCP6Option
*) &message
->options
[pos
];
840 uint16_t optcode
, optlen
;
845 if (len
< pos
+ offsetof(DHCP6Option
, data
))
848 optcode
= be16toh(option
->code
);
849 optlen
= be16toh(option
->len
);
850 optval
= option
->data
;
852 if (len
< pos
+ offsetof(DHCP6Option
, data
) + optlen
)
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
, optlen
+ sizeof(DHCP6Option
));
904 log_dhcp6_client(client
, "%s Status %s",
905 dhcp6_message_type_to_string(message
->type
),
906 dhcp6_message_status_to_string(status
));
913 case SD_DHCP6_OPTION_IA_NA
:
914 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
915 log_dhcp6_client(client
, "Information request ignoring IA NA option");
920 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
921 if (r
< 0 && r
!= -ENOMSG
)
924 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
928 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
929 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
930 dhcp6_message_type_to_string(message
->type
));
934 if (lease
->ia
.addresses
) {
935 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
936 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
941 case SD_DHCP6_OPTION_IA_PD
:
942 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
943 log_dhcp6_client(client
, "Information request ignoring IA PD option");
948 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
949 if (r
< 0 && r
!= -ENOMSG
)
952 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
956 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
957 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
958 dhcp6_message_type_to_string(message
->type
));
962 if (lease
->pd
.addresses
) {
963 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
964 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
969 case SD_DHCP6_OPTION_RAPID_COMMIT
:
970 r
= dhcp6_lease_set_rapid_commit(lease
);
976 case SD_DHCP6_OPTION_DNS_SERVERS
:
977 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
983 case SD_DHCP6_OPTION_DOMAIN_LIST
:
984 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
990 case SD_DHCP6_OPTION_NTP_SERVER
:
991 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
997 case SD_DHCP6_OPTION_SNTP_SERVERS
:
998 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
1004 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
1005 irt
= be32toh(*(be32_t
*) optval
) * USEC_PER_SEC
;
1009 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1013 log_dhcp6_client(client
, "%s has incomplete options",
1014 dhcp6_message_type_to_string(message
->type
));
1018 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1019 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1021 log_dhcp6_client(client
, "%s has no server id",
1022 dhcp6_message_type_to_string(message
->type
));
1027 if (lease
->ia
.addresses
) {
1028 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1029 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1032 if (lease
->pd
.addresses
) {
1033 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1034 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1038 client
->information_refresh_time_usec
= MAX(irt
, IRT_MINIMUM
);
1043 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1044 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1051 if (reply
->type
!= DHCP6_REPLY
)
1054 r
= dhcp6_lease_new(&lease
);
1058 r
= client_parse_message(client
, reply
, len
, lease
);
1062 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1063 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1071 sd_dhcp6_lease_unref(client
->lease
);
1072 client
->lease
= TAKE_PTR(lease
);
1074 return DHCP6_STATE_BOUND
;
1077 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1078 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1079 uint8_t pref_advertise
= 0, pref_lease
= 0;
1082 if (advertise
->type
!= DHCP6_ADVERTISE
)
1085 r
= dhcp6_lease_new(&lease
);
1089 r
= client_parse_message(client
, advertise
, len
, lease
);
1093 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1097 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1099 if (r
< 0 || pref_advertise
> pref_lease
) {
1100 sd_dhcp6_lease_unref(client
->lease
);
1101 client
->lease
= TAKE_PTR(lease
);
1105 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1106 r
= DHCP6_STATE_REQUEST
;
1111 static int client_receive_message(
1117 sd_dhcp6_client
*client
= userdata
;
1118 DHCP6_CLIENT_DONT_DESTROY(client
);
1119 _cleanup_free_ DHCP6Message
*message
= NULL
;
1120 ssize_t buflen
, len
;
1125 assert(client
->event
);
1127 buflen
= next_datagram_size_fd(fd
);
1128 if (buflen
== -ENETDOWN
) {
1129 /* the link is down. Don't return an error or the I/O event
1130 source will be disconnected and we won't be able to receive
1131 packets again when the link comes back. */
1137 message
= malloc(buflen
);
1141 len
= recv(fd
, message
, buflen
, 0);
1143 /* see comment above for why we shouldn't error out on ENETDOWN. */
1144 if (IN_SET(errno
, EAGAIN
, EINTR
, ENETDOWN
))
1147 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1150 if ((size_t) len
< sizeof(DHCP6Message
)) {
1151 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1155 switch(message
->type
) {
1163 case DHCP6_INFORMATION_REQUEST
:
1164 case DHCP6_RELAY_FORW
:
1165 case DHCP6_RELAY_REPL
:
1168 case DHCP6_ADVERTISE
:
1170 case DHCP6_RECONFIGURE
:
1174 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1178 if (client
->transaction_id
!= (message
->transaction_id
&
1179 htobe32(0x00ffffff)))
1182 switch (client
->state
) {
1183 case DHCP6_STATE_INFORMATION_REQUEST
:
1184 r
= client_receive_reply(client
, message
, len
);
1188 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1190 client_start(client
, DHCP6_STATE_STOPPED
);
1194 case DHCP6_STATE_SOLICITATION
:
1195 r
= client_receive_advertise(client
, message
, len
);
1197 if (r
== DHCP6_STATE_REQUEST
) {
1198 client_start(client
, r
);
1203 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1204 case DHCP6_STATE_REQUEST
:
1205 case DHCP6_STATE_RENEW
:
1206 case DHCP6_STATE_REBIND
:
1208 r
= client_receive_reply(client
, message
, len
);
1212 if (r
== DHCP6_STATE_BOUND
) {
1214 r
= client_start(client
, DHCP6_STATE_BOUND
);
1216 client_stop(client
, r
);
1220 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1225 case DHCP6_STATE_BOUND
:
1229 case DHCP6_STATE_STOPPED
:
1233 log_dhcp6_client(client
, "Recv %s",
1234 dhcp6_message_type_to_string(message
->type
));
1239 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1240 uint32_t *lifetime_t2
) {
1241 assert_return(client
, -EINVAL
);
1242 assert_return(client
->lease
, -EINVAL
);
1244 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1245 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1246 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1251 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1252 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1253 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1261 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1263 usec_t timeout
, time_now
;
1264 char time_string
[FORMAT_TIMESPAN_MAX
];
1265 uint32_t lifetime_t1
, lifetime_t2
;
1267 assert_return(client
, -EINVAL
);
1268 assert_return(client
->event
, -EINVAL
);
1269 assert_return(client
->ifindex
> 0, -EINVAL
);
1270 assert_return(client
->state
!= state
, -EINVAL
);
1272 (void) event_source_disable(client
->timeout_resend_expire
);
1273 (void) event_source_disable(client
->timeout_resend
);
1274 client
->retransmit_time
= 0;
1275 client
->retransmit_count
= 0;
1277 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1281 if (!client
->receive_message
) {
1282 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1283 client
->fd
, EPOLLIN
, client_receive_message
,
1288 r
= sd_event_source_set_priority(client
->receive_message
,
1289 client
->event_priority
);
1293 r
= sd_event_source_set_description(client
->receive_message
,
1294 "dhcp6-receive-message");
1300 case DHCP6_STATE_STOPPED
:
1301 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1302 client
->state
= DHCP6_STATE_STOPPED
;
1308 case DHCP6_STATE_SOLICITATION
:
1309 client
->state
= DHCP6_STATE_SOLICITATION
;
1313 case DHCP6_STATE_INFORMATION_REQUEST
:
1314 case DHCP6_STATE_REQUEST
:
1315 case DHCP6_STATE_RENEW
:
1316 case DHCP6_STATE_REBIND
:
1318 client
->state
= state
;
1322 case DHCP6_STATE_BOUND
:
1324 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1328 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1329 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1330 lifetime_t1
, lifetime_t2
);
1335 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1337 log_dhcp6_client(client
, "T1 expires in %s",
1338 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1340 r
= event_reset_time(client
->event
, &client
->timeout_t1
,
1341 clock_boottime_or_monotonic(),
1342 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1343 client_timeout_t1
, client
,
1344 client
->event_priority
, "dhcp6-t1-timeout", true);
1348 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1350 log_dhcp6_client(client
, "T2 expires in %s",
1351 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1353 r
= event_reset_time(client
->event
, &client
->timeout_t2
,
1354 clock_boottime_or_monotonic(),
1355 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1356 client_timeout_t2
, client
,
1357 client
->event_priority
, "dhcp6-t2-timeout", true);
1361 client
->state
= state
;
1366 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1367 client
->transaction_start
= time_now
;
1369 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
1370 clock_boottime_or_monotonic(),
1372 client_timeout_resend
, client
,
1373 client
->event_priority
, "dhcp6-resend-timeout", true);
1380 client_reset(client
);
1384 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1385 assert_return(client
, -EINVAL
);
1387 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1389 client
->fd
= safe_close(client
->fd
);
1394 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1395 assert_return(client
, -EINVAL
);
1397 return client
->state
!= DHCP6_STATE_STOPPED
;
1400 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1401 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1404 assert_return(client
, -EINVAL
);
1405 assert_return(client
->event
, -EINVAL
);
1406 assert_return(client
->ifindex
> 0, -EINVAL
);
1407 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1409 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1412 if (!client
->information_request
&& !client
->request
)
1415 r
= client_reset(client
);
1419 r
= client_ensure_iaid(client
);
1423 r
= client_ensure_duid(client
);
1427 if (client
->fd
< 0) {
1428 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1430 _cleanup_free_
char *p
= NULL
;
1432 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1433 return log_dhcp6_client_errno(client
, r
,
1434 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1440 if (client
->information_request
) {
1441 usec_t t
= now(CLOCK_MONOTONIC
);
1443 if (t
< usec_add(client
->information_request_time_usec
, client
->information_refresh_time_usec
))
1446 client
->information_request_time_usec
= t
;
1447 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1450 log_dhcp6_client(client
, "Started in %s mode",
1451 client
->information_request
? "Information request":
1454 return client_start(client
, state
);
1457 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1460 assert_return(client
, -EINVAL
);
1461 assert_return(!client
->event
, -EBUSY
);
1464 client
->event
= sd_event_ref(event
);
1466 r
= sd_event_default(&client
->event
);
1471 client
->event_priority
= priority
;
1476 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1477 assert_return(client
, -EINVAL
);
1479 client
->event
= sd_event_unref(client
->event
);
1484 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1485 assert_return(client
, NULL
);
1487 return client
->event
;
1490 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1493 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1494 client
->timeout_resend_expire
= sd_event_source_unref(client
->timeout_resend_expire
);
1495 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
1496 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
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
;
1513 _cleanup_free_ be16_t
*req_opts
= NULL
;
1516 assert_return(ret
, -EINVAL
);
1518 req_opts
= new(be16_t
, ELEMENTSOF(default_req_opts
));
1522 for (t
= 0; t
< ELEMENTSOF(default_req_opts
); t
++)
1523 req_opts
[t
] = htobe16(default_req_opts
[t
]);
1525 client
= new(sd_dhcp6_client
, 1);
1529 *client
= (sd_dhcp6_client
) {
1531 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1532 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1534 .request
= DHCP6_REQUEST_IA_NA
,
1536 .req_opts_len
= ELEMENTSOF(default_req_opts
),
1537 .req_opts
= TAKE_PTR(req_opts
),
1540 *ret
= TAKE_PTR(client
);