1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
9 #include <linux/if_infiniband.h>
11 #include "sd-dhcp6-client.h"
13 #include "alloc-util.h"
14 #include "dhcp-identifier.h"
15 #include "dhcp6-internal.h"
16 #include "dhcp6-lease-internal.h"
17 #include "dhcp6-protocol.h"
18 #include "dns-domain.h"
20 #include "hostname-util.h"
21 #include "in-addr-util.h"
22 #include "network-internal.h"
23 #include "random-util.h"
24 #include "socket-util.h"
25 #include "string-table.h"
28 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
30 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
32 DHCP6_REQUEST_IA_NA
= 1,
33 DHCP6_REQUEST_IA_TA
= 2, /* currently not used */
34 DHCP6_REQUEST_IA_PD
= 4,
37 struct sd_dhcp6_client
{
40 enum DHCP6State state
;
44 struct in6_addr local_address
;
45 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
50 sd_event_source
*timeout_t1
;
51 sd_event_source
*timeout_t2
;
53 be32_t transaction_id
;
54 usec_t transaction_start
;
55 struct sd_dhcp6_lease
*lease
;
57 bool information_request
;
59 size_t req_opts_allocated
;
62 sd_event_source
*receive_message
;
63 usec_t retransmit_time
;
64 uint8_t retransmit_count
;
65 sd_event_source
*timeout_resend
;
66 sd_event_source
*timeout_resend_expire
;
67 sd_dhcp6_client_callback_t callback
;
73 static const uint16_t default_req_opts
[] = {
74 SD_DHCP6_OPTION_DNS_SERVERS
,
75 SD_DHCP6_OPTION_DOMAIN_LIST
,
76 SD_DHCP6_OPTION_NTP_SERVER
,
77 SD_DHCP6_OPTION_SNTP_SERVERS
,
80 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
81 [DHCP6_SOLICIT
] = "SOLICIT",
82 [DHCP6_ADVERTISE
] = "ADVERTISE",
83 [DHCP6_REQUEST
] = "REQUEST",
84 [DHCP6_CONFIRM
] = "CONFIRM",
85 [DHCP6_RENEW
] = "RENEW",
86 [DHCP6_REBIND
] = "REBIND",
87 [DHCP6_REPLY
] = "REPLY",
88 [DHCP6_RELEASE
] = "RELEASE",
89 [DHCP6_DECLINE
] = "DECLINE",
90 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
91 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
92 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
93 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
96 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
98 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
99 [DHCP6_STATUS_SUCCESS
] = "Success",
100 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
101 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
102 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
103 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
104 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
107 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
109 #define DHCP6_CLIENT_DONT_DESTROY(client) \
110 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
112 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
114 int sd_dhcp6_client_set_callback(
115 sd_dhcp6_client
*client
,
116 sd_dhcp6_client_callback_t cb
,
119 assert_return(client
, -EINVAL
);
121 client
->callback
= cb
;
122 client
->userdata
= userdata
;
127 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
129 assert_return(client
, -EINVAL
);
130 assert_return(ifindex
>= -1, -EINVAL
);
131 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
133 client
->ifindex
= ifindex
;
137 int sd_dhcp6_client_set_local_address(
138 sd_dhcp6_client
*client
,
139 const struct in6_addr
*local_address
) {
141 assert_return(client
, -EINVAL
);
142 assert_return(local_address
, -EINVAL
);
143 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
145 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
147 client
->local_address
= *local_address
;
152 int sd_dhcp6_client_set_mac(
153 sd_dhcp6_client
*client
,
154 const uint8_t *addr
, size_t addr_len
,
157 assert_return(client
, -EINVAL
);
158 assert_return(addr
, -EINVAL
);
159 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
160 assert_return(arp_type
> 0, -EINVAL
);
162 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
164 if (arp_type
== ARPHRD_ETHER
)
165 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
166 else if (arp_type
== ARPHRD_INFINIBAND
)
167 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
171 if (client
->mac_addr_len
== addr_len
&&
172 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
175 memcpy(&client
->mac_addr
, addr
, addr_len
);
176 client
->mac_addr_len
= addr_len
;
177 client
->arp_type
= arp_type
;
182 static int client_ensure_duid(sd_dhcp6_client
*client
) {
183 if (client
->duid_len
!= 0)
186 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
190 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
191 * without further modification. Otherwise, if duid_type is supported, DUID
192 * is set based on that type. Otherwise, an error is returned.
194 static int dhcp6_client_set_duid_internal(
195 sd_dhcp6_client
*client
,
202 assert_return(client
, -EINVAL
);
203 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
204 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
207 r
= dhcp_validate_duid_len(duid_type
, duid_len
);
211 client
->duid
.type
= htobe16(duid_type
);
212 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
213 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
217 if (client
->mac_addr_len
== 0)
220 r
= dhcp_identifier_set_duid_llt(&client
->duid
, llt_time
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
225 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
230 if (client
->mac_addr_len
== 0)
233 r
= dhcp_identifier_set_duid_ll(&client
->duid
, client
->mac_addr
, client
->mac_addr_len
, client
->arp_type
, &client
->duid_len
);
238 r
= dhcp_identifier_set_duid_uuid(&client
->duid
, &client
->duid_len
);
249 int sd_dhcp6_client_set_duid(
250 sd_dhcp6_client
*client
,
254 return dhcp6_client_set_duid_internal(client
, duid_type
, duid
, duid_len
, 0);
257 int sd_dhcp6_client_set_duid_llt(
258 sd_dhcp6_client
*client
,
260 return dhcp6_client_set_duid_internal(client
, DUID_TYPE_LLT
, NULL
, 0, llt_time
);
263 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
264 assert_return(client
, -EINVAL
);
265 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
267 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
268 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
273 int sd_dhcp6_client_set_fqdn(
274 sd_dhcp6_client
*client
,
277 assert_return(client
, -EINVAL
);
279 /* Make sure FQDN qualifies as DNS and as Linux hostname */
281 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
284 return free_and_strdup(&client
->fqdn
, fqdn
);
287 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
288 assert_return(client
, -EINVAL
);
289 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
291 client
->information_request
= enabled
;
296 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
297 assert_return(client
, -EINVAL
);
298 assert_return(enabled
, -EINVAL
);
300 *enabled
= client
->information_request
;
305 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
308 assert_return(client
, -EINVAL
);
309 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
313 case SD_DHCP6_OPTION_DNS_SERVERS
:
314 case SD_DHCP6_OPTION_DOMAIN_LIST
:
315 case SD_DHCP6_OPTION_SNTP_SERVERS
:
316 case SD_DHCP6_OPTION_NTP_SERVER
:
317 case SD_DHCP6_OPTION_RAPID_COMMIT
:
324 for (t
= 0; t
< client
->req_opts_len
; t
++)
325 if (client
->req_opts
[t
] == htobe16(option
))
328 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
329 client
->req_opts_len
+ 1))
332 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
337 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client
*client
, int *delegation
) {
338 assert_return(client
, -EINVAL
);
339 assert_return(delegation
, -EINVAL
);
341 *delegation
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
);
346 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, int delegation
) {
347 assert_return(client
, -EINVAL
);
349 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_PD
, delegation
);
354 int sd_dhcp6_client_get_address_request(sd_dhcp6_client
*client
, int *request
) {
355 assert_return(client
, -EINVAL
);
356 assert_return(request
, -EINVAL
);
358 *request
= FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
);
363 int sd_dhcp6_client_set_address_request(sd_dhcp6_client
*client
, int request
) {
364 assert_return(client
, -EINVAL
);
366 SET_FLAG(client
->request
, DHCP6_REQUEST_IA_NA
, request
);
371 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client
*client
, uint32_t transaction_id
) {
372 assert_return(client
, -EINVAL
);
374 client
->transaction_id
= transaction_id
;
379 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
380 assert_return(client
, -EINVAL
);
386 *ret
= client
->lease
;
391 static void client_notify(sd_dhcp6_client
*client
, int event
) {
394 if (client
->callback
)
395 client
->callback(client
, event
, client
->userdata
);
398 static int client_reset(sd_dhcp6_client
*client
) {
401 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
403 client
->receive_message
=
404 sd_event_source_unref(client
->receive_message
);
406 client
->transaction_id
= 0;
407 client
->transaction_start
= 0;
409 client
->retransmit_time
= 0;
410 client
->retransmit_count
= 0;
411 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
412 client
->timeout_resend_expire
=
413 sd_event_source_unref(client
->timeout_resend_expire
);
415 client
->timeout_t1
= sd_event_source_unref(client
->timeout_t1
);
416 client
->timeout_t2
= sd_event_source_unref(client
->timeout_t2
);
418 client
->state
= DHCP6_STATE_STOPPED
;
423 static void client_stop(sd_dhcp6_client
*client
, int error
) {
424 DHCP6_CLIENT_DONT_DESTROY(client
);
428 client_notify(client
, error
);
430 client_reset(client
);
433 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
434 _cleanup_free_ DHCP6Message
*message
= NULL
;
435 struct in6_addr all_servers
=
436 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
437 size_t len
, optlen
= 512;
445 len
= sizeof(DHCP6Message
) + optlen
;
447 message
= malloc0(len
);
451 opt
= (uint8_t *)(message
+ 1);
453 message
->transaction_id
= client
->transaction_id
;
455 switch(client
->state
) {
456 case DHCP6_STATE_INFORMATION_REQUEST
:
457 message
->type
= DHCP6_INFORMATION_REQUEST
;
461 case DHCP6_STATE_SOLICITATION
:
462 message
->type
= DHCP6_SOLICIT
;
464 r
= dhcp6_option_append(&opt
, &optlen
,
465 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
469 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
470 r
= dhcp6_option_append_ia(&opt
, &optlen
,
477 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
482 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
483 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
493 case DHCP6_STATE_REQUEST
:
494 case DHCP6_STATE_RENEW
:
496 if (client
->state
== DHCP6_STATE_REQUEST
)
497 message
->type
= DHCP6_REQUEST
;
499 message
->type
= DHCP6_RENEW
;
501 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
502 client
->lease
->serverid_len
,
503 client
->lease
->serverid
);
507 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
508 r
= dhcp6_option_append_ia(&opt
, &optlen
,
515 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
520 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
521 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
531 case DHCP6_STATE_REBIND
:
532 message
->type
= DHCP6_REBIND
;
534 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
)) {
535 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
541 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
546 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
)) {
547 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
557 case DHCP6_STATE_STOPPED
:
558 case DHCP6_STATE_BOUND
:
562 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
563 client
->req_opts_len
* sizeof(be16_t
),
568 assert(client
->duid_len
);
569 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
570 client
->duid_len
, &client
->duid
);
574 elapsed_usec
= time_now
- client
->transaction_start
;
575 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
576 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
578 elapsed_time
= 0xffff;
580 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
581 sizeof(elapsed_time
), &elapsed_time
);
585 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
590 log_dhcp6_client(client
, "Sent %s",
591 dhcp6_message_type_to_string(message
->type
));
596 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
597 sd_dhcp6_client
*client
= userdata
;
601 assert(client
->lease
);
604 sd_event_source_unref(client
->timeout_t2
);
606 log_dhcp6_client(client
, "Timeout T2");
608 client_start(client
, DHCP6_STATE_REBIND
);
613 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
614 sd_dhcp6_client
*client
= userdata
;
618 assert(client
->lease
);
621 sd_event_source_unref(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 client
->timeout_resend
= sd_event_source_unref(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 (!client
->timeout_resend_expire
) {
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
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
761 clock_boottime_or_monotonic(),
762 time_now
+ client
->retransmit_time
,
763 10 * USEC_PER_MSEC
, client_timeout_resend
,
768 r
= sd_event_source_set_priority(client
->timeout_resend
,
769 client
->event_priority
);
773 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
777 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
779 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
780 max_retransmit_duration
/ USEC_PER_SEC
);
782 r
= sd_event_add_time(client
->event
,
783 &client
->timeout_resend_expire
,
784 clock_boottime_or_monotonic(),
785 time_now
+ max_retransmit_duration
,
787 client_timeout_resend_expire
, client
);
791 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
792 client
->event_priority
);
796 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
803 client_stop(client
, r
);
808 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
814 if (client
->ia_na
.ia_na
.id
)
817 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, &iaid
);
821 client
->ia_na
.ia_na
.id
= iaid
;
822 client
->ia_pd
.ia_pd
.id
= iaid
;
827 static int client_parse_message(
828 sd_dhcp6_client
*client
,
829 DHCP6Message
*message
,
831 sd_dhcp6_lease
*lease
) {
834 bool clientid
= false;
835 uint32_t lt_t1
= ~0, lt_t2
= ~0;
839 assert(len
>= sizeof(DHCP6Message
));
842 len
-= sizeof(DHCP6Message
);
845 DHCP6Option
*option
= (DHCP6Option
*)&message
->options
[pos
];
846 uint16_t optcode
, optlen
;
851 if (len
< pos
+ offsetof(DHCP6Option
, data
) ||
852 len
< pos
+ offsetof(DHCP6Option
, data
) + be16toh(option
->len
))
855 optcode
= be16toh(option
->code
);
856 optlen
= be16toh(option
->len
);
857 optval
= option
->data
;
860 case SD_DHCP6_OPTION_CLIENTID
:
862 log_dhcp6_client(client
, "%s contains multiple clientids",
863 dhcp6_message_type_to_string(message
->type
));
867 if (optlen
!= client
->duid_len
||
868 memcmp(&client
->duid
, optval
, optlen
) != 0) {
869 log_dhcp6_client(client
, "%s DUID does not match",
870 dhcp6_message_type_to_string(message
->type
));
878 case SD_DHCP6_OPTION_SERVERID
:
879 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
881 log_dhcp6_client(client
, "%s contains multiple serverids",
882 dhcp6_message_type_to_string(message
->type
));
886 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
892 case SD_DHCP6_OPTION_PREFERENCE
:
896 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
902 case SD_DHCP6_OPTION_STATUS_CODE
:
903 status
= dhcp6_option_parse_status(option
, optlen
);
905 log_dhcp6_client(client
, "%s Status %s",
906 dhcp6_message_type_to_string(message
->type
),
907 dhcp6_message_status_to_string(status
));
908 dhcp6_lease_free_ia(&lease
->ia
);
909 dhcp6_lease_free_ia(&lease
->pd
);
916 case SD_DHCP6_OPTION_IA_NA
:
917 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
918 log_dhcp6_client(client
, "Information request ignoring IA NA option");
923 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
924 if (r
< 0 && r
!= -ENOMSG
)
927 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
931 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
932 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
933 dhcp6_message_type_to_string(message
->type
));
937 if (lease
->ia
.addresses
) {
938 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
939 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
944 case SD_DHCP6_OPTION_IA_PD
:
945 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
946 log_dhcp6_client(client
, "Information request ignoring IA PD option");
951 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
952 if (r
< 0 && r
!= -ENOMSG
)
955 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
959 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
960 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
961 dhcp6_message_type_to_string(message
->type
));
965 if (lease
->pd
.addresses
) {
966 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
967 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
972 case SD_DHCP6_OPTION_RAPID_COMMIT
:
973 r
= dhcp6_lease_set_rapid_commit(lease
);
979 case SD_DHCP6_OPTION_DNS_SERVERS
:
980 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
986 case SD_DHCP6_OPTION_DOMAIN_LIST
:
987 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
993 case SD_DHCP6_OPTION_NTP_SERVER
:
994 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
1000 case SD_DHCP6_OPTION_SNTP_SERVERS
:
1001 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
1008 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1012 log_dhcp6_client(client
, "%s has incomplete options",
1013 dhcp6_message_type_to_string(message
->type
));
1017 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1018 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1020 log_dhcp6_client(client
, "%s has no server id",
1021 dhcp6_message_type_to_string(message
->type
));
1026 if (lease
->ia
.addresses
) {
1027 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1028 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1031 if (lease
->pd
.addresses
) {
1032 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1033 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1040 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1041 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1048 if (reply
->type
!= DHCP6_REPLY
)
1051 r
= dhcp6_lease_new(&lease
);
1055 r
= client_parse_message(client
, reply
, len
, lease
);
1059 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1060 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1068 sd_dhcp6_lease_unref(client
->lease
);
1069 client
->lease
= TAKE_PTR(lease
);
1071 return DHCP6_STATE_BOUND
;
1074 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1075 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1076 uint8_t pref_advertise
= 0, pref_lease
= 0;
1079 if (advertise
->type
!= DHCP6_ADVERTISE
)
1082 r
= dhcp6_lease_new(&lease
);
1086 r
= client_parse_message(client
, advertise
, len
, lease
);
1090 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1094 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1096 if (r
< 0 || pref_advertise
> pref_lease
) {
1097 sd_dhcp6_lease_unref(client
->lease
);
1098 client
->lease
= TAKE_PTR(lease
);
1102 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1103 r
= DHCP6_STATE_REQUEST
;
1108 static int client_receive_message(
1114 sd_dhcp6_client
*client
= userdata
;
1115 DHCP6_CLIENT_DONT_DESTROY(client
);
1116 _cleanup_free_ DHCP6Message
*message
= NULL
;
1117 ssize_t buflen
, len
;
1122 assert(client
->event
);
1124 buflen
= next_datagram_size_fd(fd
);
1128 message
= malloc(buflen
);
1132 len
= recv(fd
, message
, buflen
, 0);
1134 if (IN_SET(errno
, EAGAIN
, EINTR
))
1137 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1140 if ((size_t) len
< sizeof(DHCP6Message
)) {
1141 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1145 switch(message
->type
) {
1153 case DHCP6_INFORMATION_REQUEST
:
1154 case DHCP6_RELAY_FORW
:
1155 case DHCP6_RELAY_REPL
:
1158 case DHCP6_ADVERTISE
:
1160 case DHCP6_RECONFIGURE
:
1164 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1168 if (client
->transaction_id
!= (message
->transaction_id
&
1169 htobe32(0x00ffffff)))
1172 switch (client
->state
) {
1173 case DHCP6_STATE_INFORMATION_REQUEST
:
1174 r
= client_receive_reply(client
, message
, len
);
1178 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1180 client_start(client
, DHCP6_STATE_STOPPED
);
1184 case DHCP6_STATE_SOLICITATION
:
1185 r
= client_receive_advertise(client
, message
, len
);
1187 if (r
== DHCP6_STATE_REQUEST
) {
1188 client_start(client
, r
);
1193 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1194 case DHCP6_STATE_REQUEST
:
1195 case DHCP6_STATE_RENEW
:
1196 case DHCP6_STATE_REBIND
:
1198 r
= client_receive_reply(client
, message
, len
);
1202 if (r
== DHCP6_STATE_BOUND
) {
1204 r
= client_start(client
, DHCP6_STATE_BOUND
);
1206 client_stop(client
, r
);
1210 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1215 case DHCP6_STATE_BOUND
:
1219 case DHCP6_STATE_STOPPED
:
1223 log_dhcp6_client(client
, "Recv %s",
1224 dhcp6_message_type_to_string(message
->type
));
1229 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1230 uint32_t *lifetime_t2
) {
1231 assert_return(client
, -EINVAL
);
1232 assert_return(client
->lease
, -EINVAL
);
1234 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1235 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1236 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1241 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1242 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1243 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1251 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1253 usec_t timeout
, time_now
;
1254 char time_string
[FORMAT_TIMESPAN_MAX
];
1255 uint32_t lifetime_t1
, lifetime_t2
;
1257 assert_return(client
, -EINVAL
);
1258 assert_return(client
->event
, -EINVAL
);
1259 assert_return(client
->ifindex
> 0, -EINVAL
);
1260 assert_return(client
->state
!= state
, -EINVAL
);
1262 client
->timeout_resend_expire
=
1263 sd_event_source_unref(client
->timeout_resend_expire
);
1264 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1265 client
->retransmit_time
= 0;
1266 client
->retransmit_count
= 0;
1268 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1272 if (!client
->receive_message
) {
1273 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1274 client
->fd
, EPOLLIN
, client_receive_message
,
1279 r
= sd_event_source_set_priority(client
->receive_message
,
1280 client
->event_priority
);
1284 r
= sd_event_source_set_description(client
->receive_message
,
1285 "dhcp6-receive-message");
1291 case DHCP6_STATE_STOPPED
:
1292 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1293 client
->state
= DHCP6_STATE_STOPPED
;
1299 case DHCP6_STATE_SOLICITATION
:
1300 client
->state
= DHCP6_STATE_SOLICITATION
;
1304 case DHCP6_STATE_INFORMATION_REQUEST
:
1305 case DHCP6_STATE_REQUEST
:
1306 case DHCP6_STATE_RENEW
:
1307 case DHCP6_STATE_REBIND
:
1309 client
->state
= state
;
1313 case DHCP6_STATE_BOUND
:
1315 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1319 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1320 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1321 lifetime_t1
, lifetime_t2
);
1326 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1328 log_dhcp6_client(client
, "T1 expires in %s",
1329 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1331 r
= sd_event_add_time(client
->event
,
1332 &client
->timeout_t1
,
1333 clock_boottime_or_monotonic(), time_now
+ timeout
,
1334 10 * USEC_PER_SEC
, client_timeout_t1
,
1339 r
= sd_event_source_set_priority(client
->timeout_t1
,
1340 client
->event_priority
);
1344 r
= sd_event_source_set_description(client
->timeout_t1
, "dhcp6-t1-timeout");
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
= sd_event_add_time(client
->event
,
1354 &client
->timeout_t2
,
1355 clock_boottime_or_monotonic(), time_now
+ timeout
,
1356 10 * USEC_PER_SEC
, client_timeout_t2
,
1361 r
= sd_event_source_set_priority(client
->timeout_t2
,
1362 client
->event_priority
);
1366 r
= sd_event_source_set_description(client
->timeout_t2
, "dhcp6-t2-timeout");
1370 client
->state
= state
;
1375 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1376 client
->transaction_start
= time_now
;
1378 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1379 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1384 r
= sd_event_source_set_priority(client
->timeout_resend
,
1385 client
->event_priority
);
1389 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1396 client_reset(client
);
1400 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1401 assert_return(client
, -EINVAL
);
1403 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1405 client
->fd
= safe_close(client
->fd
);
1410 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1411 assert_return(client
, -EINVAL
);
1413 return client
->state
!= DHCP6_STATE_STOPPED
;
1416 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1417 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1420 assert_return(client
, -EINVAL
);
1421 assert_return(client
->event
, -EINVAL
);
1422 assert_return(client
->ifindex
> 0, -EINVAL
);
1423 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1425 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1428 if (!client
->information_request
&& !client
->request
)
1431 r
= client_reset(client
);
1435 r
= client_ensure_iaid(client
);
1439 r
= client_ensure_duid(client
);
1443 if (client
->fd
< 0) {
1444 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1446 _cleanup_free_
char *p
= NULL
;
1448 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1449 return log_dhcp6_client_errno(client
, r
,
1450 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1456 if (client
->information_request
)
1457 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1459 log_dhcp6_client(client
, "Started in %s mode",
1460 client
->information_request
? "Information request":
1463 return client_start(client
, state
);
1466 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1469 assert_return(client
, -EINVAL
);
1470 assert_return(!client
->event
, -EBUSY
);
1473 client
->event
= sd_event_ref(event
);
1475 r
= sd_event_default(&client
->event
);
1480 client
->event_priority
= priority
;
1485 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1486 assert_return(client
, -EINVAL
);
1488 client
->event
= sd_event_unref(client
->event
);
1493 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1494 assert_return(client
, NULL
);
1496 return client
->event
;
1499 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1502 client_reset(client
);
1504 client
->fd
= safe_close(client
->fd
);
1506 sd_dhcp6_client_detach_event(client
);
1508 free(client
->req_opts
);
1510 return mfree(client
);
1513 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1515 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1516 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1519 assert_return(ret
, -EINVAL
);
1521 client
= new0(sd_dhcp6_client
, 1);
1526 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1527 client
->ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
;
1528 client
->ifindex
= -1;
1529 client
->request
= DHCP6_REQUEST_IA_NA
;
1532 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1533 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1534 if (!client
->req_opts
)
1537 for (t
= 0; t
< client
->req_opts_len
; t
++)
1538 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);
1540 *ret
= TAKE_PTR(client
);