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
);
213 client
->duid
.type
= htobe16(duid_type
);
214 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
215 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
219 if (client
->mac_addr_len
== 0)
222 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
227 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
232 if (client
->mac_addr_len
== 0)
235 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
240 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
251 int sd_dhcp6_client_set_duid(
252 sd_dhcp6_client
*client
,
256 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
259 int sd_dhcp6_client_set_duid_llt(
260 sd_dhcp6_client
*client
,
262 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
265 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
266 assert_return(client
, -EINVAL
);
267 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
269 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
270 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
271 client
->has_iaid
= true;
276 int sd_dhcp6_client_set_fqdn(
277 sd_dhcp6_client
*client
,
280 assert_return(client
, -EINVAL
);
282 /* Make sure FQDN qualifies as DNS and as Linux hostname */
284 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
287 return free_and_strdup(&client
->fqdn
, fqdn
);
290 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
291 assert_return(client
, -EINVAL
);
292 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
294 client
->information_request
= enabled
;
299 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
300 assert_return(client
, -EINVAL
);
301 assert_return(enabled
, -EINVAL
);
303 *enabled
= client
->information_request
;
308 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
311 assert_return(client
, -EINVAL
);
312 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
316 case SD_DHCP6_OPTION_DNS_SERVERS
:
317 case SD_DHCP6_OPTION_DOMAIN_LIST
:
318 case SD_DHCP6_OPTION_SNTP_SERVERS
:
319 case SD_DHCP6_OPTION_NTP_SERVER
:
320 case SD_DHCP6_OPTION_RAPID_COMMIT
:
327 for (t
= 0; t
< client
->req_opts_len
; t
++)
328 if (client
->req_opts
[t
] == htobe16(option
))
331 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
332 client
->req_opts_len
+ 1))
335 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
340 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
341 assert_return(client
, -EINVAL
);
342 assert_return(delegation
, -EINVAL
);
344 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
349 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
350 assert_return(client
, -EINVAL
);
352 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
357 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
358 assert_return(client
, -EINVAL
);
359 assert_return(request
, -EINVAL
);
361 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
366 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
367 assert_return(client
, -EINVAL
);
369 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
374 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
375 assert_return(client
, -EINVAL
);
377 client
->transaction_id
= transaction_id
;
382 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
383 assert_return(client
, -EINVAL
);
389 *ret
= client
->lease
;
394 static void client_notify(sd_dhcp6_client
*client
, int event
) {
397 if (client
->callback
)
398 client
->callback(client
, event
, client
->userdata
);
401 static int client_reset(sd_dhcp6_client
*client
) {
404 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
406 client
->receive_message
=
407 sd_event_source_unref(client
->receive_message
);
409 client
->transaction_id
= 0;
410 client
->transaction_start
= 0;
412 client
->retransmit_time
= 0;
413 client
->retransmit_count
= 0;
415 (void) event_source_disable(client
->timeout_resend
);
416 (void) event_source_disable(client
->timeout_resend_expire
);
417 (void) event_source_disable(client
->timeout_t1
);
418 (void) event_source_disable(client
->timeout_t2
);
420 client
->state
= DHCP6_STATE_STOPPED
;
425 static void client_stop(sd_dhcp6_client
*client
, int error
) {
426 DHCP6_CLIENT_DONT_DESTROY(client
);
430 client_notify(client
, error
);
432 client_reset(client
);
435 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
436 _cleanup_free_ DHCP6Message
*message
= NULL
;
437 struct in6_addr all_servers
=
438 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
439 size_t len
, optlen
= 512;
447 len
= sizeof(DHCP6Message
) + optlen
;
449 message
= malloc0(len
);
453 opt
= (uint8_t *)(message
+ 1);
455 message
->transaction_id
= client
->transaction_id
;
457 switch(client
->state
) {
458 case DHCP6_STATE_INFORMATION_REQUEST
:
459 message
->type
= DHCP6_INFORMATION_REQUEST
;
463 case DHCP6_STATE_SOLICITATION
:
464 message
->type
= DHCP6_SOLICIT
;
466 r
= dhcp6_option_append(&opt
, &optlen
,
467 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
471 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
472 r
= dhcp6_option_append_ia(&opt
, &optlen
,
479 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
484 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
485 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
495 case DHCP6_STATE_REQUEST
:
496 case DHCP6_STATE_RENEW
:
498 if (client
->state
== DHCP6_STATE_REQUEST
)
499 message
->type
= DHCP6_REQUEST
;
501 message
->type
= DHCP6_RENEW
;
503 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
504 client
->lease
->serverid_len
,
505 client
->lease
->serverid
);
509 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
510 r
= dhcp6_option_append_ia(&opt
, &optlen
,
517 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
522 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
523 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
533 case DHCP6_STATE_REBIND
:
534 message
->type
= DHCP6_REBIND
;
536 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
537 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
543 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
548 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
549 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
559 case DHCP6_STATE_STOPPED
:
560 case DHCP6_STATE_BOUND
:
564 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
565 client
->req_opts_len
* sizeof(be16_t
),
570 assert(client
->duid_len
);
571 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
572 client
->duid_len
, &client
->duid
);
576 elapsed_usec
= time_now
- client
->transaction_start
;
577 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
578 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
580 elapsed_time
= 0xffff;
582 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
583 sizeof(elapsed_time
), &elapsed_time
);
587 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
592 log_dhcp6_client(client
, "Sent %s",
593 dhcp6_message_type_to_string(message
->type
));
598 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
599 sd_dhcp6_client
*client
= userdata
;
603 assert(client
->lease
);
605 (void) event_source_disable(client
->timeout_t2
);
607 log_dhcp6_client(client
, "Timeout T2");
609 client_start(client
, DHCP6_STATE_REBIND
);
614 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
615 sd_dhcp6_client
*client
= userdata
;
619 assert(client
->lease
);
621 (void) event_source_disable(client
->timeout_t1
);
623 log_dhcp6_client(client
, "Timeout T1");
625 client_start(client
, DHCP6_STATE_RENEW
);
630 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
631 sd_dhcp6_client
*client
= userdata
;
632 DHCP6_CLIENT_DONT_DESTROY(client
);
633 enum DHCP6State state
;
637 assert(client
->event
);
639 state
= client
->state
;
641 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
643 /* RFC 3315, section 18.1.4., says that "...the client may choose to
644 use a Solicit message to locate a new DHCP server..." */
645 if (state
== DHCP6_STATE_REBIND
)
646 client_start(client
, DHCP6_STATE_SOLICITATION
);
651 static usec_t
client_timeout_compute_random(usec_t val
) {
652 return val
- val
/ 10 +
653 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
656 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
658 sd_dhcp6_client
*client
= userdata
;
659 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
660 usec_t max_retransmit_duration
= 0;
661 uint8_t max_retransmit_count
= 0;
662 char time_string
[FORMAT_TIMESPAN_MAX
];
667 assert(client
->event
);
669 (void) event_source_disable(client
->timeout_resend
);
671 switch (client
->state
) {
672 case DHCP6_STATE_INFORMATION_REQUEST
:
673 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
674 max_retransmit_time
= DHCP6_INF_MAX_RT
;
678 case DHCP6_STATE_SOLICITATION
:
680 if (client
->retransmit_count
&& client
->lease
) {
681 client_start(client
, DHCP6_STATE_REQUEST
);
685 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
686 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
690 case DHCP6_STATE_REQUEST
:
691 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
692 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
693 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
697 case DHCP6_STATE_RENEW
:
698 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
699 max_retransmit_time
= DHCP6_REN_MAX_RT
;
701 /* RFC 3315, section 18.1.3. says max retransmit duration will
702 be the remaining time until T2. Instead of setting MRD,
703 wait for T2 to trigger with the same end result */
707 case DHCP6_STATE_REBIND
:
708 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
709 max_retransmit_time
= DHCP6_REB_MAX_RT
;
711 if (event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
712 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
715 client_stop(client
, r
);
718 max_retransmit_duration
= expire
* USEC_PER_SEC
;
723 case DHCP6_STATE_STOPPED
:
724 case DHCP6_STATE_BOUND
:
728 if (max_retransmit_count
&&
729 client
->retransmit_count
>= max_retransmit_count
) {
730 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
734 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
738 r
= client_send_message(client
, time_now
);
740 client
->retransmit_count
++;
742 if (!client
->retransmit_time
) {
743 client
->retransmit_time
=
744 client_timeout_compute_random(init_retransmit_time
);
746 if (client
->state
== DHCP6_STATE_SOLICITATION
)
747 client
->retransmit_time
+= init_retransmit_time
/ 10;
750 if (max_retransmit_time
&&
751 client
->retransmit_time
> max_retransmit_time
/ 2)
752 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
754 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
757 log_dhcp6_client(client
, "Next retransmission in %s",
758 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
760 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
761 clock_boottime_or_monotonic(),
762 time_now
+ client
->retransmit_time
, 10 * USEC_PER_MSEC
,
763 client_timeout_resend
, client
,
764 client
->event_priority
, "dhcp6-resend-timer", true);
768 if (max_retransmit_duration
&& event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
770 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
771 max_retransmit_duration
/ USEC_PER_SEC
);
773 r
= event_reset_time(client
->event
, &client
->timeout_resend_expire
,
774 clock_boottime_or_monotonic(),
775 time_now
+ max_retransmit_duration
, USEC_PER_SEC
,
776 client_timeout_resend_expire
, client
,
777 client
->event_priority
, "dhcp6-resend-expire-timer", true);
784 client_stop(client
, r
);
789 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
795 if (client
->has_iaid
)
798 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, true, &iaid
);
802 client
->ia_na
.ia_na
.id
= iaid
;
803 client
->ia_pd
.ia_pd
.id
= iaid
;
804 client
->has_iaid
= true;
809 static int client_parse_message(
810 sd_dhcp6_client
*client
,
811 DHCP6Message
*message
,
813 sd_dhcp6_lease
*lease
) {
815 uint32_t lt_t1
= ~0, lt_t2
= ~0;
816 bool clientid
= false;
822 assert(len
>= sizeof(DHCP6Message
));
825 len
-= sizeof(DHCP6Message
);
828 DHCP6Option
*option
= (DHCP6Option
*) &message
->options
[pos
];
829 uint16_t optcode
, optlen
;
834 if (len
< pos
+ offsetof(DHCP6Option
, data
))
837 optcode
= be16toh(option
->code
);
838 optlen
= be16toh(option
->len
);
839 optval
= option
->data
;
841 if (len
< pos
+ offsetof(DHCP6Option
, data
) + optlen
)
845 case SD_DHCP6_OPTION_CLIENTID
:
847 log_dhcp6_client(client
, "%s contains multiple clientids",
848 dhcp6_message_type_to_string(message
->type
));
852 if (optlen
!= client
->duid_len
||
853 memcmp(&client
->duid
, optval
, optlen
) != 0) {
854 log_dhcp6_client(client
, "%s DUID does not match",
855 dhcp6_message_type_to_string(message
->type
));
863 case SD_DHCP6_OPTION_SERVERID
:
864 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
866 log_dhcp6_client(client
, "%s contains multiple serverids",
867 dhcp6_message_type_to_string(message
->type
));
871 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
877 case SD_DHCP6_OPTION_PREFERENCE
:
881 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
887 case SD_DHCP6_OPTION_STATUS_CODE
:
888 status
= dhcp6_option_parse_status(option
, optlen
+ sizeof(DHCP6Option
));
893 log_dhcp6_client(client
, "%s Status %s",
894 dhcp6_message_type_to_string(message
->type
),
895 dhcp6_message_status_to_string(status
));
902 case SD_DHCP6_OPTION_IA_NA
:
903 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
904 log_dhcp6_client(client
, "Information request ignoring IA NA option");
909 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
910 if (r
< 0 && r
!= -ENOMSG
)
913 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
917 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
918 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
919 dhcp6_message_type_to_string(message
->type
));
923 if (lease
->ia
.addresses
) {
924 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
925 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
930 case SD_DHCP6_OPTION_IA_PD
:
931 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
932 log_dhcp6_client(client
, "Information request ignoring IA PD option");
937 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
938 if (r
< 0 && r
!= -ENOMSG
)
941 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
945 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
946 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
947 dhcp6_message_type_to_string(message
->type
));
951 if (lease
->pd
.addresses
) {
952 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
953 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
958 case SD_DHCP6_OPTION_RAPID_COMMIT
:
959 r
= dhcp6_lease_set_rapid_commit(lease
);
965 case SD_DHCP6_OPTION_DNS_SERVERS
:
966 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
972 case SD_DHCP6_OPTION_DOMAIN_LIST
:
973 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
979 case SD_DHCP6_OPTION_NTP_SERVER
:
980 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
986 case SD_DHCP6_OPTION_SNTP_SERVERS
:
987 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
994 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
998 log_dhcp6_client(client
, "%s has incomplete options",
999 dhcp6_message_type_to_string(message
->type
));
1003 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1004 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1006 log_dhcp6_client(client
, "%s has no server id",
1007 dhcp6_message_type_to_string(message
->type
));
1012 if (lease
->ia
.addresses
) {
1013 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1014 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1017 if (lease
->pd
.addresses
) {
1018 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1019 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1026 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1027 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1034 if (reply
->type
!= DHCP6_REPLY
)
1037 r
= dhcp6_lease_new(&lease
);
1041 r
= client_parse_message(client
, reply
, len
, lease
);
1045 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1046 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1054 sd_dhcp6_lease_unref(client
->lease
);
1055 client
->lease
= TAKE_PTR(lease
);
1057 return DHCP6_STATE_BOUND
;
1060 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1061 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1062 uint8_t pref_advertise
= 0, pref_lease
= 0;
1065 if (advertise
->type
!= DHCP6_ADVERTISE
)
1068 r
= dhcp6_lease_new(&lease
);
1072 r
= client_parse_message(client
, advertise
, len
, lease
);
1076 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1080 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1082 if (r
< 0 || pref_advertise
> pref_lease
) {
1083 sd_dhcp6_lease_unref(client
->lease
);
1084 client
->lease
= TAKE_PTR(lease
);
1088 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1089 r
= DHCP6_STATE_REQUEST
;
1094 static int client_receive_message(
1100 sd_dhcp6_client
*client
= userdata
;
1101 DHCP6_CLIENT_DONT_DESTROY(client
);
1102 _cleanup_free_ DHCP6Message
*message
= NULL
;
1103 ssize_t buflen
, len
;
1108 assert(client
->event
);
1110 buflen
= next_datagram_size_fd(fd
);
1114 message
= malloc(buflen
);
1118 len
= recv(fd
, message
, buflen
, 0);
1120 if (IN_SET(errno
, EAGAIN
, EINTR
))
1123 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1126 if ((size_t) len
< sizeof(DHCP6Message
)) {
1127 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1131 switch(message
->type
) {
1139 case DHCP6_INFORMATION_REQUEST
:
1140 case DHCP6_RELAY_FORW
:
1141 case DHCP6_RELAY_REPL
:
1144 case DHCP6_ADVERTISE
:
1146 case DHCP6_RECONFIGURE
:
1150 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1154 if (client
->transaction_id
!= (message
->transaction_id
&
1155 htobe32(0x00ffffff)))
1158 switch (client
->state
) {
1159 case DHCP6_STATE_INFORMATION_REQUEST
:
1160 r
= client_receive_reply(client
, message
, len
);
1164 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1166 client_start(client
, DHCP6_STATE_STOPPED
);
1170 case DHCP6_STATE_SOLICITATION
:
1171 r
= client_receive_advertise(client
, message
, len
);
1173 if (r
== DHCP6_STATE_REQUEST
) {
1174 client_start(client
, r
);
1179 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1180 case DHCP6_STATE_REQUEST
:
1181 case DHCP6_STATE_RENEW
:
1182 case DHCP6_STATE_REBIND
:
1184 r
= client_receive_reply(client
, message
, len
);
1188 if (r
== DHCP6_STATE_BOUND
) {
1190 r
= client_start(client
, DHCP6_STATE_BOUND
);
1192 client_stop(client
, r
);
1196 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1201 case DHCP6_STATE_BOUND
:
1205 case DHCP6_STATE_STOPPED
:
1209 log_dhcp6_client(client
, "Recv %s",
1210 dhcp6_message_type_to_string(message
->type
));
1215 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1216 uint32_t *lifetime_t2
) {
1217 assert_return(client
, -EINVAL
);
1218 assert_return(client
->lease
, -EINVAL
);
1220 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1221 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1222 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1227 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1228 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1229 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1237 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1239 usec_t timeout
, time_now
;
1240 char time_string
[FORMAT_TIMESPAN_MAX
];
1241 uint32_t lifetime_t1
, lifetime_t2
;
1243 assert_return(client
, -EINVAL
);
1244 assert_return(client
->event
, -EINVAL
);
1245 assert_return(client
->ifindex
> 0, -EINVAL
);
1246 assert_return(client
->state
!= state
, -EINVAL
);
1248 (void) event_source_disable(client
->timeout_resend_expire
);
1249 (void) event_source_disable(client
->timeout_resend
);
1250 client
->retransmit_time
= 0;
1251 client
->retransmit_count
= 0;
1253 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1257 if (!client
->receive_message
) {
1258 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1259 client
->fd
, EPOLLIN
, client_receive_message
,
1264 r
= sd_event_source_set_priority(client
->receive_message
,
1265 client
->event_priority
);
1269 r
= sd_event_source_set_description(client
->receive_message
,
1270 "dhcp6-receive-message");
1276 case DHCP6_STATE_STOPPED
:
1277 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1278 client
->state
= DHCP6_STATE_STOPPED
;
1284 case DHCP6_STATE_SOLICITATION
:
1285 client
->state
= DHCP6_STATE_SOLICITATION
;
1289 case DHCP6_STATE_INFORMATION_REQUEST
:
1290 case DHCP6_STATE_REQUEST
:
1291 case DHCP6_STATE_RENEW
:
1292 case DHCP6_STATE_REBIND
:
1294 client
->state
= state
;
1298 case DHCP6_STATE_BOUND
:
1300 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1304 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1305 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1306 lifetime_t1
, lifetime_t2
);
1311 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1313 log_dhcp6_client(client
, "T1 expires in %s",
1314 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1316 r
= event_reset_time(client
->event
, &client
->timeout_t1
,
1317 clock_boottime_or_monotonic(),
1318 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1319 client_timeout_t1
, client
,
1320 client
->event_priority
, "dhcp6-t1-timeout", true);
1324 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1326 log_dhcp6_client(client
, "T2 expires in %s",
1327 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1329 r
= event_reset_time(client
->event
, &client
->timeout_t2
,
1330 clock_boottime_or_monotonic(),
1331 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1332 client_timeout_t2
, client
,
1333 client
->event_priority
, "dhcp6-t2-timeout", true);
1337 client
->state
= state
;
1342 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1343 client
->transaction_start
= time_now
;
1345 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
1346 clock_boottime_or_monotonic(),
1348 client_timeout_resend
, client
,
1349 client
->event_priority
, "dhcp6-resend-timeout", true);
1356 client_reset(client
);
1360 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1361 assert_return(client
, -EINVAL
);
1363 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1365 client
->fd
= safe_close(client
->fd
);
1370 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1371 assert_return(client
, -EINVAL
);
1373 return client
->state
!= DHCP6_STATE_STOPPED
;
1376 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1377 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1380 assert_return(client
, -EINVAL
);
1381 assert_return(client
->event
, -EINVAL
);
1382 assert_return(client
->ifindex
> 0, -EINVAL
);
1383 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1385 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1388 if (!client
->information_request
&& !client
->request
)
1391 r
= client_reset(client
);
1395 r
= client_ensure_iaid(client
);
1399 r
= client_ensure_duid(client
);
1403 if (client
->fd
< 0) {
1404 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1406 _cleanup_free_
char *p
= NULL
;
1408 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1409 return log_dhcp6_client_errno(client
, r
,
1410 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1416 if (client
->information_request
)
1417 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1419 log_dhcp6_client(client
, "Started in %s mode",
1420 client
->information_request
? "Information request":
1423 return client_start(client
, state
);
1426 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1429 assert_return(client
, -EINVAL
);
1430 assert_return(!client
->event
, -EBUSY
);
1433 client
->event
= sd_event_ref(event
);
1435 r
= sd_event_default(&client
->event
);
1440 client
->event_priority
= priority
;
1445 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1446 assert_return(client
, -EINVAL
);
1448 client
->event
= sd_event_unref(client
->event
);
1453 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1454 assert_return(client
, NULL
);
1456 return client
->event
;
1459 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1462 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1463 client
->timeout_resend_expire
= sd_event_source_unref(client
->timeout_resend_expire
);
1464 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
1465 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
1467 client_reset(client
);
1469 client
->fd
= safe_close(client
->fd
);
1471 sd_dhcp6_client_detach_event(client
);
1473 free(client
->req_opts
);
1475 return mfree(client
);
1478 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1480 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1481 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1482 _cleanup_free_ be16_t
*req_opts
= NULL
;
1485 assert_return(ret
, -EINVAL
);
1487 req_opts
= new(be16_t
, ELEMENTSOF(default_req_opts
));
1491 for (t
= 0; t
< ELEMENTSOF(default_req_opts
); t
++)
1492 req_opts
[t
] = htobe16(default_req_opts
[t
]);
1494 client
= new(sd_dhcp6_client
, 1);
1498 *client
= (sd_dhcp6_client
) {
1500 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1501 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1503 .request
= DHCP6_REQUEST_IA_NA
,
1505 .req_opts_len
= ELEMENTSOF(default_req_opts
),
1506 .req_opts
= TAKE_PTR(req_opts
),
1509 *ret
= TAKE_PTR(client
);