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 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
34 DHCP6_REQUEST_IA_NA
= 1,
35 DHCP6_REQUEST_IA_TA
= 2, /* currently not used */
36 DHCP6_REQUEST_IA_PD
= 4,
39 struct sd_dhcp6_client
{
42 enum DHCP6State state
;
46 struct in6_addr local_address
;
47 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
52 sd_event_source
*timeout_t1
;
53 sd_event_source
*timeout_t2
;
55 be32_t transaction_id
;
56 usec_t transaction_start
;
57 struct sd_dhcp6_lease
*lease
;
59 bool information_request
;
62 size_t req_opts_allocated
;
65 sd_event_source
*receive_message
;
66 usec_t retransmit_time
;
67 uint8_t retransmit_count
;
68 sd_event_source
*timeout_resend
;
69 sd_event_source
*timeout_resend_expire
;
70 sd_dhcp6_client_callback_t callback
;
76 static const uint16_t default_req_opts
[] = {
77 SD_DHCP6_OPTION_DNS_SERVERS
,
78 SD_DHCP6_OPTION_DOMAIN_LIST
,
79 SD_DHCP6_OPTION_NTP_SERVER
,
80 SD_DHCP6_OPTION_SNTP_SERVERS
,
83 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
84 [DHCP6_SOLICIT
] = "SOLICIT",
85 [DHCP6_ADVERTISE
] = "ADVERTISE",
86 [DHCP6_REQUEST
] = "REQUEST",
87 [DHCP6_CONFIRM
] = "CONFIRM",
88 [DHCP6_RENEW
] = "RENEW",
89 [DHCP6_REBIND
] = "REBIND",
90 [DHCP6_REPLY
] = "REPLY",
91 [DHCP6_RELEASE
] = "RELEASE",
92 [DHCP6_DECLINE
] = "DECLINE",
93 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
94 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
95 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
96 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
99 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
101 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
102 [DHCP6_STATUS_SUCCESS
] = "Success",
103 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
104 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
105 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
106 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
107 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
110 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
112 #define DHCP6_CLIENT_DONT_DESTROY(client) \
113 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
115 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
117 int sd_dhcp6_client_set_callback(
118 sd_dhcp6_client
*client
,
119 sd_dhcp6_client_callback_t cb
,
122 assert_return(client
, -EINVAL
);
124 client
->callback
= cb
;
125 client
->userdata
= userdata
;
130 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
132 assert_return(client
, -EINVAL
);
133 assert_return(ifindex
>= -1, -EINVAL
);
134 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
136 client
->ifindex
= ifindex
;
140 int sd_dhcp6_client_set_local_address(
141 sd_dhcp6_client
*client
,
142 const struct in6_addr
*local_address
) {
144 assert_return(client
, -EINVAL
);
145 assert_return(local_address
, -EINVAL
);
146 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
148 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
150 client
->local_address
= *local_address
;
155 int sd_dhcp6_client_set_mac(
156 sd_dhcp6_client
*client
,
157 const uint8_t *addr
, size_t addr_len
,
160 assert_return(client
, -EINVAL
);
161 assert_return(addr
, -EINVAL
);
162 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
163 assert_return(arp_type
> 0, -EINVAL
);
165 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
167 if (arp_type
== ARPHRD_ETHER
)
168 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
169 else if (arp_type
== ARPHRD_INFINIBAND
)
170 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
174 if (client
->mac_addr_len
== addr_len
&&
175 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
178 memcpy(&client
->mac_addr
, addr
, addr_len
);
179 client
->mac_addr_len
= addr_len
;
180 client
->arp_type
= arp_type
;
185 static int client_ensure_duid(sd_dhcp6_client
*client
) {
186 if (client
->duid_len
!= 0)
189 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
193 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
194 * without further modification. Otherwise, if duid_type is supported, DUID
195 * is set based on that type. Otherwise, an error is returned.
197 static int dhcp6_client_set_duid_internal(
198 sd_dhcp6_client
*client
,
205 assert_return(client
, -EINVAL
);
206 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
207 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
210 r
= dhcp_validate_duid_len(duid_type
, duid_len
, true);
212 r
= dhcp_validate_duid_len(duid_type
, duid_len
, false);
215 log_dhcp6_client(client
, "Setting DUID of type %u with unexpected content", duid_type
);
218 client
->duid
.type
= htobe16(duid_type
);
219 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
220 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
224 if (client
->mac_addr_len
== 0)
227 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
232 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
237 if (client
->mac_addr_len
== 0)
240 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
245 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
256 int sd_dhcp6_client_set_duid(
257 sd_dhcp6_client
*client
,
261 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
264 int sd_dhcp6_client_set_duid_llt(
265 sd_dhcp6_client
*client
,
267 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
270 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
271 assert_return(client
, -EINVAL
);
272 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
274 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
275 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
276 client
->iaid_set
= true;
281 int sd_dhcp6_client_set_fqdn(
282 sd_dhcp6_client
*client
,
285 assert_return(client
, -EINVAL
);
287 /* Make sure FQDN qualifies as DNS and as Linux hostname */
289 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
292 return free_and_strdup(&client
->fqdn
, fqdn
);
295 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
296 assert_return(client
, -EINVAL
);
297 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
299 client
->information_request
= enabled
;
304 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
305 assert_return(client
, -EINVAL
);
306 assert_return(enabled
, -EINVAL
);
308 *enabled
= client
->information_request
;
313 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
316 assert_return(client
, -EINVAL
);
317 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
321 case SD_DHCP6_OPTION_DNS_SERVERS
:
322 case SD_DHCP6_OPTION_DOMAIN_LIST
:
323 case SD_DHCP6_OPTION_SNTP_SERVERS
:
324 case SD_DHCP6_OPTION_NTP_SERVER
:
325 case SD_DHCP6_OPTION_RAPID_COMMIT
:
332 for (t
= 0; t
< client
->req_opts_len
; t
++)
333 if (client
->req_opts
[t
] == htobe16(option
))
336 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
337 client
->req_opts_len
+ 1))
340 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
345 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
346 assert_return(client
, -EINVAL
);
347 assert_return(delegation
, -EINVAL
);
349 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
354 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
355 assert_return(client
, -EINVAL
);
357 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
362 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
363 assert_return(client
, -EINVAL
);
364 assert_return(request
, -EINVAL
);
366 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
371 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
372 assert_return(client
, -EINVAL
);
374 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
379 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
380 assert_return(client
, -EINVAL
);
382 client
->transaction_id
= transaction_id
;
387 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
388 assert_return(client
, -EINVAL
);
394 *ret
= client
->lease
;
399 static void client_notify(sd_dhcp6_client
*client
, int event
) {
402 if (client
->callback
)
403 client
->callback(client
, event
, client
->userdata
);
406 static int client_reset(sd_dhcp6_client
*client
) {
409 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
411 client
->receive_message
=
412 sd_event_source_unref(client
->receive_message
);
414 client
->transaction_id
= 0;
415 client
->transaction_start
= 0;
417 client
->retransmit_time
= 0;
418 client
->retransmit_count
= 0;
420 (void) event_source_disable(client
->timeout_resend
);
421 (void) event_source_disable(client
->timeout_resend_expire
);
422 (void) event_source_disable(client
->timeout_t1
);
423 (void) event_source_disable(client
->timeout_t2
);
425 client
->state
= DHCP6_STATE_STOPPED
;
430 static void client_stop(sd_dhcp6_client
*client
, int error
) {
431 DHCP6_CLIENT_DONT_DESTROY(client
);
435 client_notify(client
, error
);
437 client_reset(client
);
440 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
441 _cleanup_free_ DHCP6Message
*message
= NULL
;
442 struct in6_addr all_servers
=
443 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
444 size_t len
, optlen
= 512;
452 len
= sizeof(DHCP6Message
) + optlen
;
454 message
= malloc0(len
);
458 opt
= (uint8_t *)(message
+ 1);
460 message
->transaction_id
= client
->transaction_id
;
462 switch(client
->state
) {
463 case DHCP6_STATE_INFORMATION_REQUEST
:
464 message
->type
= DHCP6_INFORMATION_REQUEST
;
468 case DHCP6_STATE_SOLICITATION
:
469 message
->type
= DHCP6_SOLICIT
;
471 r
= dhcp6_option_append(&opt
, &optlen
,
472 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
476 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
477 r
= dhcp6_option_append_ia(&opt
, &optlen
,
484 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
489 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
490 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
500 case DHCP6_STATE_REQUEST
:
501 case DHCP6_STATE_RENEW
:
503 if (client
->state
== DHCP6_STATE_REQUEST
)
504 message
->type
= DHCP6_REQUEST
;
506 message
->type
= DHCP6_RENEW
;
508 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
509 client
->lease
->serverid_len
,
510 client
->lease
->serverid
);
514 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
515 r
= dhcp6_option_append_ia(&opt
, &optlen
,
522 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
527 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
528 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
538 case DHCP6_STATE_REBIND
:
539 message
->type
= DHCP6_REBIND
;
541 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
542 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
548 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
553 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
554 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
564 case DHCP6_STATE_STOPPED
:
565 case DHCP6_STATE_BOUND
:
569 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
570 client
->req_opts_len
* sizeof(be16_t
),
575 assert(client
->duid_len
);
576 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
577 client
->duid_len
, &client
->duid
);
581 elapsed_usec
= time_now
- client
->transaction_start
;
582 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
583 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
585 elapsed_time
= 0xffff;
587 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
588 sizeof(elapsed_time
), &elapsed_time
);
592 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
597 log_dhcp6_client(client
, "Sent %s",
598 dhcp6_message_type_to_string(message
->type
));
603 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
604 sd_dhcp6_client
*client
= userdata
;
608 assert(client
->lease
);
610 (void) event_source_disable(client
->timeout_t2
);
612 log_dhcp6_client(client
, "Timeout T2");
614 client_start(client
, DHCP6_STATE_REBIND
);
619 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
620 sd_dhcp6_client
*client
= userdata
;
624 assert(client
->lease
);
626 (void) event_source_disable(client
->timeout_t1
);
628 log_dhcp6_client(client
, "Timeout T1");
630 client_start(client
, DHCP6_STATE_RENEW
);
635 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
636 sd_dhcp6_client
*client
= userdata
;
637 DHCP6_CLIENT_DONT_DESTROY(client
);
638 enum DHCP6State state
;
642 assert(client
->event
);
644 state
= client
->state
;
646 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
648 /* RFC 3315, section 18.1.4., says that "...the client may choose to
649 use a Solicit message to locate a new DHCP server..." */
650 if (state
== DHCP6_STATE_REBIND
)
651 client_start(client
, DHCP6_STATE_SOLICITATION
);
656 static usec_t
client_timeout_compute_random(usec_t val
) {
657 return val
- val
/ 10 +
658 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
661 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
663 sd_dhcp6_client
*client
= userdata
;
664 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
665 usec_t max_retransmit_duration
= 0;
666 uint8_t max_retransmit_count
= 0;
667 char time_string
[FORMAT_TIMESPAN_MAX
];
672 assert(client
->event
);
674 (void) event_source_disable(client
->timeout_resend
);
676 switch (client
->state
) {
677 case DHCP6_STATE_INFORMATION_REQUEST
:
678 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
679 max_retransmit_time
= DHCP6_INF_MAX_RT
;
683 case DHCP6_STATE_SOLICITATION
:
685 if (client
->retransmit_count
&& client
->lease
) {
686 client_start(client
, DHCP6_STATE_REQUEST
);
690 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
691 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
695 case DHCP6_STATE_REQUEST
:
696 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
697 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
698 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
702 case DHCP6_STATE_RENEW
:
703 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
704 max_retransmit_time
= DHCP6_REN_MAX_RT
;
706 /* RFC 3315, section 18.1.3. says max retransmit duration will
707 be the remaining time until T2. Instead of setting MRD,
708 wait for T2 to trigger with the same end result */
712 case DHCP6_STATE_REBIND
:
713 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
714 max_retransmit_time
= DHCP6_REB_MAX_RT
;
716 if (event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
717 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
720 client_stop(client
, r
);
723 max_retransmit_duration
= expire
* USEC_PER_SEC
;
728 case DHCP6_STATE_STOPPED
:
729 case DHCP6_STATE_BOUND
:
733 if (max_retransmit_count
&&
734 client
->retransmit_count
>= max_retransmit_count
) {
735 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
739 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
743 r
= client_send_message(client
, time_now
);
745 client
->retransmit_count
++;
747 if (!client
->retransmit_time
) {
748 client
->retransmit_time
=
749 client_timeout_compute_random(init_retransmit_time
);
751 if (client
->state
== DHCP6_STATE_SOLICITATION
)
752 client
->retransmit_time
+= init_retransmit_time
/ 10;
755 if (max_retransmit_time
&&
756 client
->retransmit_time
> max_retransmit_time
/ 2)
757 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
759 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
762 log_dhcp6_client(client
, "Next retransmission in %s",
763 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
765 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
766 clock_boottime_or_monotonic(),
767 time_now
+ client
->retransmit_time
, 10 * USEC_PER_MSEC
,
768 client_timeout_resend
, client
,
769 client
->event_priority
, "dhcp6-resend-timer", true);
773 if (max_retransmit_duration
&& event_source_is_enabled(client
->timeout_resend_expire
) <= 0) {
775 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
776 max_retransmit_duration
/ USEC_PER_SEC
);
778 r
= event_reset_time(client
->event
, &client
->timeout_resend_expire
,
779 clock_boottime_or_monotonic(),
780 time_now
+ max_retransmit_duration
, USEC_PER_SEC
,
781 client_timeout_resend_expire
, client
,
782 client
->event_priority
, "dhcp6-resend-expire-timer", true);
789 client_stop(client
, r
);
794 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
800 if (client
->iaid_set
)
803 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, true, &iaid
);
807 client
->ia_na
.ia_na
.id
= iaid
;
808 client
->ia_pd
.ia_pd
.id
= iaid
;
809 client
->iaid_set
= true;
814 static int client_parse_message(
815 sd_dhcp6_client
*client
,
816 DHCP6Message
*message
,
818 sd_dhcp6_lease
*lease
) {
820 uint32_t lt_t1
= ~0, lt_t2
= ~0;
821 bool clientid
= false;
827 assert(len
>= sizeof(DHCP6Message
));
830 len
-= sizeof(DHCP6Message
);
833 DHCP6Option
*option
= (DHCP6Option
*) &message
->options
[pos
];
834 uint16_t optcode
, optlen
;
839 if (len
< pos
+ offsetof(DHCP6Option
, data
))
842 optcode
= be16toh(option
->code
);
843 optlen
= be16toh(option
->len
);
844 optval
= option
->data
;
846 if (len
< pos
+ offsetof(DHCP6Option
, data
) + optlen
)
850 case SD_DHCP6_OPTION_CLIENTID
:
852 log_dhcp6_client(client
, "%s contains multiple clientids",
853 dhcp6_message_type_to_string(message
->type
));
857 if (optlen
!= client
->duid_len
||
858 memcmp(&client
->duid
, optval
, optlen
) != 0) {
859 log_dhcp6_client(client
, "%s DUID does not match",
860 dhcp6_message_type_to_string(message
->type
));
868 case SD_DHCP6_OPTION_SERVERID
:
869 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
871 log_dhcp6_client(client
, "%s contains multiple serverids",
872 dhcp6_message_type_to_string(message
->type
));
876 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
882 case SD_DHCP6_OPTION_PREFERENCE
:
886 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
892 case SD_DHCP6_OPTION_STATUS_CODE
:
893 status
= dhcp6_option_parse_status(option
, optlen
+ sizeof(DHCP6Option
));
898 log_dhcp6_client(client
, "%s Status %s",
899 dhcp6_message_type_to_string(message
->type
),
900 dhcp6_message_status_to_string(status
));
907 case SD_DHCP6_OPTION_IA_NA
:
908 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
909 log_dhcp6_client(client
, "Information request ignoring IA NA option");
914 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
915 if (r
< 0 && r
!= -ENOMSG
)
918 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
922 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
923 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
924 dhcp6_message_type_to_string(message
->type
));
928 if (lease
->ia
.addresses
) {
929 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
930 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
935 case SD_DHCP6_OPTION_IA_PD
:
936 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
937 log_dhcp6_client(client
, "Information request ignoring IA PD option");
942 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
943 if (r
< 0 && r
!= -ENOMSG
)
946 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
950 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
951 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
952 dhcp6_message_type_to_string(message
->type
));
956 if (lease
->pd
.addresses
) {
957 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
958 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
963 case SD_DHCP6_OPTION_RAPID_COMMIT
:
964 r
= dhcp6_lease_set_rapid_commit(lease
);
970 case SD_DHCP6_OPTION_DNS_SERVERS
:
971 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
977 case SD_DHCP6_OPTION_DOMAIN_LIST
:
978 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
984 case SD_DHCP6_OPTION_NTP_SERVER
:
985 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
991 case SD_DHCP6_OPTION_SNTP_SERVERS
:
992 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
999 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1003 log_dhcp6_client(client
, "%s has incomplete options",
1004 dhcp6_message_type_to_string(message
->type
));
1008 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1009 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1011 log_dhcp6_client(client
, "%s has no server id",
1012 dhcp6_message_type_to_string(message
->type
));
1017 if (lease
->ia
.addresses
) {
1018 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1019 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1022 if (lease
->pd
.addresses
) {
1023 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1024 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1031 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1032 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1039 if (reply
->type
!= DHCP6_REPLY
)
1042 r
= dhcp6_lease_new(&lease
);
1046 r
= client_parse_message(client
, reply
, len
, lease
);
1050 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1051 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1059 sd_dhcp6_lease_unref(client
->lease
);
1060 client
->lease
= TAKE_PTR(lease
);
1062 return DHCP6_STATE_BOUND
;
1065 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1066 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1067 uint8_t pref_advertise
= 0, pref_lease
= 0;
1070 if (advertise
->type
!= DHCP6_ADVERTISE
)
1073 r
= dhcp6_lease_new(&lease
);
1077 r
= client_parse_message(client
, advertise
, len
, lease
);
1081 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1085 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1087 if (r
< 0 || pref_advertise
> pref_lease
) {
1088 sd_dhcp6_lease_unref(client
->lease
);
1089 client
->lease
= TAKE_PTR(lease
);
1093 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1094 r
= DHCP6_STATE_REQUEST
;
1099 static int client_receive_message(
1105 sd_dhcp6_client
*client
= userdata
;
1106 DHCP6_CLIENT_DONT_DESTROY(client
);
1107 _cleanup_free_ DHCP6Message
*message
= NULL
;
1108 ssize_t buflen
, len
;
1113 assert(client
->event
);
1115 buflen
= next_datagram_size_fd(fd
);
1116 if (buflen
== -ENETDOWN
) {
1117 /* the link is down. Don't return an error or the I/O event
1118 source will be disconnected and we won't be able to receive
1119 packets again when the link comes back. */
1125 message
= malloc(buflen
);
1129 len
= recv(fd
, message
, buflen
, 0);
1131 /* see comment above for why we shouldn't error out on ENETDOWN. */
1132 if (IN_SET(errno
, EAGAIN
, EINTR
, ENETDOWN
))
1135 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1138 if ((size_t) len
< sizeof(DHCP6Message
)) {
1139 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1143 switch(message
->type
) {
1151 case DHCP6_INFORMATION_REQUEST
:
1152 case DHCP6_RELAY_FORW
:
1153 case DHCP6_RELAY_REPL
:
1156 case DHCP6_ADVERTISE
:
1158 case DHCP6_RECONFIGURE
:
1162 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1166 if (client
->transaction_id
!= (message
->transaction_id
&
1167 htobe32(0x00ffffff)))
1170 switch (client
->state
) {
1171 case DHCP6_STATE_INFORMATION_REQUEST
:
1172 r
= client_receive_reply(client
, message
, len
);
1176 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1178 client_start(client
, DHCP6_STATE_STOPPED
);
1182 case DHCP6_STATE_SOLICITATION
:
1183 r
= client_receive_advertise(client
, message
, len
);
1185 if (r
== DHCP6_STATE_REQUEST
) {
1186 client_start(client
, r
);
1191 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1192 case DHCP6_STATE_REQUEST
:
1193 case DHCP6_STATE_RENEW
:
1194 case DHCP6_STATE_REBIND
:
1196 r
= client_receive_reply(client
, message
, len
);
1200 if (r
== DHCP6_STATE_BOUND
) {
1202 r
= client_start(client
, DHCP6_STATE_BOUND
);
1204 client_stop(client
, r
);
1208 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1213 case DHCP6_STATE_BOUND
:
1217 case DHCP6_STATE_STOPPED
:
1221 log_dhcp6_client(client
, "Recv %s",
1222 dhcp6_message_type_to_string(message
->type
));
1227 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1228 uint32_t *lifetime_t2
) {
1229 assert_return(client
, -EINVAL
);
1230 assert_return(client
->lease
, -EINVAL
);
1232 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1233 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1234 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1239 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1240 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1241 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1249 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1251 usec_t timeout
, time_now
;
1252 char time_string
[FORMAT_TIMESPAN_MAX
];
1253 uint32_t lifetime_t1
, lifetime_t2
;
1255 assert_return(client
, -EINVAL
);
1256 assert_return(client
->event
, -EINVAL
);
1257 assert_return(client
->ifindex
> 0, -EINVAL
);
1258 assert_return(client
->state
!= state
, -EINVAL
);
1260 (void) event_source_disable(client
->timeout_resend_expire
);
1261 (void) event_source_disable(client
->timeout_resend
);
1262 client
->retransmit_time
= 0;
1263 client
->retransmit_count
= 0;
1265 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1269 if (!client
->receive_message
) {
1270 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1271 client
->fd
, EPOLLIN
, client_receive_message
,
1276 r
= sd_event_source_set_priority(client
->receive_message
,
1277 client
->event_priority
);
1281 r
= sd_event_source_set_description(client
->receive_message
,
1282 "dhcp6-receive-message");
1288 case DHCP6_STATE_STOPPED
:
1289 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1290 client
->state
= DHCP6_STATE_STOPPED
;
1296 case DHCP6_STATE_SOLICITATION
:
1297 client
->state
= DHCP6_STATE_SOLICITATION
;
1301 case DHCP6_STATE_INFORMATION_REQUEST
:
1302 case DHCP6_STATE_REQUEST
:
1303 case DHCP6_STATE_RENEW
:
1304 case DHCP6_STATE_REBIND
:
1306 client
->state
= state
;
1310 case DHCP6_STATE_BOUND
:
1312 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1316 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1317 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1318 lifetime_t1
, lifetime_t2
);
1323 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1325 log_dhcp6_client(client
, "T1 expires in %s",
1326 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1328 r
= event_reset_time(client
->event
, &client
->timeout_t1
,
1329 clock_boottime_or_monotonic(),
1330 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1331 client_timeout_t1
, client
,
1332 client
->event_priority
, "dhcp6-t1-timeout", true);
1336 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1338 log_dhcp6_client(client
, "T2 expires in %s",
1339 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1341 r
= event_reset_time(client
->event
, &client
->timeout_t2
,
1342 clock_boottime_or_monotonic(),
1343 time_now
+ timeout
, 10 * USEC_PER_SEC
,
1344 client_timeout_t2
, client
,
1345 client
->event_priority
, "dhcp6-t2-timeout", true);
1349 client
->state
= state
;
1354 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1355 client
->transaction_start
= time_now
;
1357 r
= event_reset_time(client
->event
, &client
->timeout_resend
,
1358 clock_boottime_or_monotonic(),
1360 client_timeout_resend
, client
,
1361 client
->event_priority
, "dhcp6-resend-timeout", true);
1368 client_reset(client
);
1372 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1373 assert_return(client
, -EINVAL
);
1375 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1377 client
->fd
= safe_close(client
->fd
);
1382 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1383 assert_return(client
, -EINVAL
);
1385 return client
->state
!= DHCP6_STATE_STOPPED
;
1388 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1389 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1392 assert_return(client
, -EINVAL
);
1393 assert_return(client
->event
, -EINVAL
);
1394 assert_return(client
->ifindex
> 0, -EINVAL
);
1395 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1397 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1400 if (!client
->information_request
&& !client
->request
)
1403 r
= client_reset(client
);
1407 r
= client_ensure_iaid(client
);
1411 r
= client_ensure_duid(client
);
1415 if (client
->fd
< 0) {
1416 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1418 _cleanup_free_
char *p
= NULL
;
1420 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1421 return log_dhcp6_client_errno(client
, r
,
1422 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1428 if (client
->information_request
)
1429 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1431 log_dhcp6_client(client
, "Started in %s mode",
1432 client
->information_request
? "Information request":
1435 return client_start(client
, state
);
1438 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1441 assert_return(client
, -EINVAL
);
1442 assert_return(!client
->event
, -EBUSY
);
1445 client
->event
= sd_event_ref(event
);
1447 r
= sd_event_default(&client
->event
);
1452 client
->event_priority
= priority
;
1457 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1458 assert_return(client
, -EINVAL
);
1460 client
->event
= sd_event_unref(client
->event
);
1465 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1466 assert_return(client
, NULL
);
1468 return client
->event
;
1471 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1474 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1475 client
->timeout_resend_expire
= sd_event_source_unref(client
->timeout_resend_expire
);
1476 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
1477 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
1479 client_reset(client
);
1481 client
->fd
= safe_close(client
->fd
);
1483 sd_dhcp6_client_detach_event(client
);
1485 free(client
->req_opts
);
1487 return mfree(client
);
1490 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1492 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1493 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1494 _cleanup_free_ be16_t
*req_opts
= NULL
;
1497 assert_return(ret
, -EINVAL
);
1499 req_opts
= new(be16_t
, ELEMENTSOF(default_req_opts
));
1503 for (t
= 0; t
< ELEMENTSOF(default_req_opts
); t
++)
1504 req_opts
[t
] = htobe16(default_req_opts
[t
]);
1506 client
= new(sd_dhcp6_client
, 1);
1510 *client
= (sd_dhcp6_client
) {
1512 .ia_na
.type
= SD_DHCP6_OPTION_IA_NA
,
1513 .ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
,
1515 .request
= DHCP6_REQUEST_IA_NA
,
1517 .req_opts_len
= ELEMENTSOF(default_req_opts
),
1518 .req_opts
= TAKE_PTR(req_opts
),
1521 *ret
= TAKE_PTR(client
);