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
) {
833 uint32_t lt_t1
= ~0, lt_t2
= ~0;
834 bool clientid
= false;
840 assert(len
>= sizeof(DHCP6Message
));
843 len
-= sizeof(DHCP6Message
);
846 DHCP6Option
*option
= (DHCP6Option
*) &message
->options
[pos
];
847 uint16_t optcode
, optlen
;
852 if (len
< pos
+ offsetof(DHCP6Option
, data
))
855 optcode
= be16toh(option
->code
);
856 optlen
= be16toh(option
->len
);
857 optval
= option
->data
;
859 if (len
< pos
+ offsetof(DHCP6Option
, data
) + optlen
)
863 case SD_DHCP6_OPTION_CLIENTID
:
865 log_dhcp6_client(client
, "%s contains multiple clientids",
866 dhcp6_message_type_to_string(message
->type
));
870 if (optlen
!= client
->duid_len
||
871 memcmp(&client
->duid
, optval
, optlen
) != 0) {
872 log_dhcp6_client(client
, "%s DUID does not match",
873 dhcp6_message_type_to_string(message
->type
));
881 case SD_DHCP6_OPTION_SERVERID
:
882 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
884 log_dhcp6_client(client
, "%s contains multiple serverids",
885 dhcp6_message_type_to_string(message
->type
));
889 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
895 case SD_DHCP6_OPTION_PREFERENCE
:
899 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
905 case SD_DHCP6_OPTION_STATUS_CODE
:
906 status
= dhcp6_option_parse_status(option
, optlen
+ sizeof(DHCP6Option
));
911 log_dhcp6_client(client
, "%s Status %s",
912 dhcp6_message_type_to_string(message
->type
),
913 dhcp6_message_status_to_string(status
));
920 case SD_DHCP6_OPTION_IA_NA
:
921 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
922 log_dhcp6_client(client
, "Information request ignoring IA NA option");
927 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
928 if (r
< 0 && r
!= -ENOMSG
)
931 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
935 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
936 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
937 dhcp6_message_type_to_string(message
->type
));
941 if (lease
->ia
.addresses
) {
942 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
943 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
948 case SD_DHCP6_OPTION_IA_PD
:
949 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
950 log_dhcp6_client(client
, "Information request ignoring IA PD option");
955 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
956 if (r
< 0 && r
!= -ENOMSG
)
959 r
= dhcp6_lease_get_pd_iaid(lease
, &iaid_lease
);
963 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
964 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
965 dhcp6_message_type_to_string(message
->type
));
969 if (lease
->pd
.addresses
) {
970 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
971 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
976 case SD_DHCP6_OPTION_RAPID_COMMIT
:
977 r
= dhcp6_lease_set_rapid_commit(lease
);
983 case SD_DHCP6_OPTION_DNS_SERVERS
:
984 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
990 case SD_DHCP6_OPTION_DOMAIN_LIST
:
991 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
997 case SD_DHCP6_OPTION_NTP_SERVER
:
998 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
1004 case SD_DHCP6_OPTION_SNTP_SERVERS
:
1005 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
1012 pos
+= offsetof(DHCP6Option
, data
) + optlen
;
1016 log_dhcp6_client(client
, "%s has incomplete options",
1017 dhcp6_message_type_to_string(message
->type
));
1021 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
1022 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
1024 log_dhcp6_client(client
, "%s has no server id",
1025 dhcp6_message_type_to_string(message
->type
));
1030 if (lease
->ia
.addresses
) {
1031 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
1032 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
1035 if (lease
->pd
.addresses
) {
1036 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
1037 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
1044 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
1045 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1052 if (reply
->type
!= DHCP6_REPLY
)
1055 r
= dhcp6_lease_new(&lease
);
1059 r
= client_parse_message(client
, reply
, len
, lease
);
1063 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
1064 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1072 sd_dhcp6_lease_unref(client
->lease
);
1073 client
->lease
= TAKE_PTR(lease
);
1075 return DHCP6_STATE_BOUND
;
1078 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1079 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1080 uint8_t pref_advertise
= 0, pref_lease
= 0;
1083 if (advertise
->type
!= DHCP6_ADVERTISE
)
1086 r
= dhcp6_lease_new(&lease
);
1090 r
= client_parse_message(client
, advertise
, len
, lease
);
1094 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1098 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1100 if (r
< 0 || pref_advertise
> pref_lease
) {
1101 sd_dhcp6_lease_unref(client
->lease
);
1102 client
->lease
= TAKE_PTR(lease
);
1106 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1107 r
= DHCP6_STATE_REQUEST
;
1112 static int client_receive_message(
1118 sd_dhcp6_client
*client
= userdata
;
1119 DHCP6_CLIENT_DONT_DESTROY(client
);
1120 _cleanup_free_ DHCP6Message
*message
= NULL
;
1121 ssize_t buflen
, len
;
1126 assert(client
->event
);
1128 buflen
= next_datagram_size_fd(fd
);
1132 message
= malloc(buflen
);
1136 len
= recv(fd
, message
, buflen
, 0);
1138 if (IN_SET(errno
, EAGAIN
, EINTR
))
1141 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1144 if ((size_t) len
< sizeof(DHCP6Message
)) {
1145 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1149 switch(message
->type
) {
1157 case DHCP6_INFORMATION_REQUEST
:
1158 case DHCP6_RELAY_FORW
:
1159 case DHCP6_RELAY_REPL
:
1162 case DHCP6_ADVERTISE
:
1164 case DHCP6_RECONFIGURE
:
1168 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1172 if (client
->transaction_id
!= (message
->transaction_id
&
1173 htobe32(0x00ffffff)))
1176 switch (client
->state
) {
1177 case DHCP6_STATE_INFORMATION_REQUEST
:
1178 r
= client_receive_reply(client
, message
, len
);
1182 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1184 client_start(client
, DHCP6_STATE_STOPPED
);
1188 case DHCP6_STATE_SOLICITATION
:
1189 r
= client_receive_advertise(client
, message
, len
);
1191 if (r
== DHCP6_STATE_REQUEST
) {
1192 client_start(client
, r
);
1197 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1198 case DHCP6_STATE_REQUEST
:
1199 case DHCP6_STATE_RENEW
:
1200 case DHCP6_STATE_REBIND
:
1202 r
= client_receive_reply(client
, message
, len
);
1206 if (r
== DHCP6_STATE_BOUND
) {
1208 r
= client_start(client
, DHCP6_STATE_BOUND
);
1210 client_stop(client
, r
);
1214 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1219 case DHCP6_STATE_BOUND
:
1223 case DHCP6_STATE_STOPPED
:
1227 log_dhcp6_client(client
, "Recv %s",
1228 dhcp6_message_type_to_string(message
->type
));
1233 static int client_get_lifetime(sd_dhcp6_client
*client
, uint32_t *lifetime_t1
,
1234 uint32_t *lifetime_t2
) {
1235 assert_return(client
, -EINVAL
);
1236 assert_return(client
->lease
, -EINVAL
);
1238 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_NA
) && client
->lease
->ia
.addresses
) {
1239 *lifetime_t1
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
);
1240 *lifetime_t2
= be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
);
1245 if (FLAGS_SET(client
->request
, DHCP6_REQUEST_IA_PD
) && client
->lease
->pd
.addresses
) {
1246 *lifetime_t1
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t1
);
1247 *lifetime_t2
= be32toh(client
->lease
->pd
.ia_pd
.lifetime_t2
);
1255 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1257 usec_t timeout
, time_now
;
1258 char time_string
[FORMAT_TIMESPAN_MAX
];
1259 uint32_t lifetime_t1
, lifetime_t2
;
1261 assert_return(client
, -EINVAL
);
1262 assert_return(client
->event
, -EINVAL
);
1263 assert_return(client
->ifindex
> 0, -EINVAL
);
1264 assert_return(client
->state
!= state
, -EINVAL
);
1266 client
->timeout_resend_expire
=
1267 sd_event_source_unref(client
->timeout_resend_expire
);
1268 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1269 client
->retransmit_time
= 0;
1270 client
->retransmit_count
= 0;
1272 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1276 if (!client
->receive_message
) {
1277 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1278 client
->fd
, EPOLLIN
, client_receive_message
,
1283 r
= sd_event_source_set_priority(client
->receive_message
,
1284 client
->event_priority
);
1288 r
= sd_event_source_set_description(client
->receive_message
,
1289 "dhcp6-receive-message");
1295 case DHCP6_STATE_STOPPED
:
1296 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1297 client
->state
= DHCP6_STATE_STOPPED
;
1303 case DHCP6_STATE_SOLICITATION
:
1304 client
->state
= DHCP6_STATE_SOLICITATION
;
1308 case DHCP6_STATE_INFORMATION_REQUEST
:
1309 case DHCP6_STATE_REQUEST
:
1310 case DHCP6_STATE_RENEW
:
1311 case DHCP6_STATE_REBIND
:
1313 client
->state
= state
;
1317 case DHCP6_STATE_BOUND
:
1319 r
= client_get_lifetime(client
, &lifetime_t1
, &lifetime_t2
);
1323 if (lifetime_t1
== 0xffffffff || lifetime_t2
== 0xffffffff) {
1324 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1325 lifetime_t1
, lifetime_t2
);
1330 timeout
= client_timeout_compute_random(lifetime_t1
* USEC_PER_SEC
);
1332 log_dhcp6_client(client
, "T1 expires in %s",
1333 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1335 r
= sd_event_add_time(client
->event
,
1336 &client
->timeout_t1
,
1337 clock_boottime_or_monotonic(), time_now
+ timeout
,
1338 10 * USEC_PER_SEC
, client_timeout_t1
,
1343 r
= sd_event_source_set_priority(client
->timeout_t1
,
1344 client
->event_priority
);
1348 r
= sd_event_source_set_description(client
->timeout_t1
, "dhcp6-t1-timeout");
1352 timeout
= client_timeout_compute_random(lifetime_t2
* USEC_PER_SEC
);
1354 log_dhcp6_client(client
, "T2 expires in %s",
1355 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1357 r
= sd_event_add_time(client
->event
,
1358 &client
->timeout_t2
,
1359 clock_boottime_or_monotonic(), time_now
+ timeout
,
1360 10 * USEC_PER_SEC
, client_timeout_t2
,
1365 r
= sd_event_source_set_priority(client
->timeout_t2
,
1366 client
->event_priority
);
1370 r
= sd_event_source_set_description(client
->timeout_t2
, "dhcp6-t2-timeout");
1374 client
->state
= state
;
1379 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1380 client
->transaction_start
= time_now
;
1382 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1383 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1388 r
= sd_event_source_set_priority(client
->timeout_resend
,
1389 client
->event_priority
);
1393 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1400 client_reset(client
);
1404 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1405 assert_return(client
, -EINVAL
);
1407 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1409 client
->fd
= safe_close(client
->fd
);
1414 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1415 assert_return(client
, -EINVAL
);
1417 return client
->state
!= DHCP6_STATE_STOPPED
;
1420 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1421 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1424 assert_return(client
, -EINVAL
);
1425 assert_return(client
->event
, -EINVAL
);
1426 assert_return(client
->ifindex
> 0, -EINVAL
);
1427 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1429 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1432 if (!client
->information_request
&& !client
->request
)
1435 r
= client_reset(client
);
1439 r
= client_ensure_iaid(client
);
1443 r
= client_ensure_duid(client
);
1447 if (client
->fd
< 0) {
1448 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1450 _cleanup_free_
char *p
= NULL
;
1452 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1453 return log_dhcp6_client_errno(client
, r
,
1454 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1460 if (client
->information_request
)
1461 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1463 log_dhcp6_client(client
, "Started in %s mode",
1464 client
->information_request
? "Information request":
1467 return client_start(client
, state
);
1470 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1473 assert_return(client
, -EINVAL
);
1474 assert_return(!client
->event
, -EBUSY
);
1477 client
->event
= sd_event_ref(event
);
1479 r
= sd_event_default(&client
->event
);
1484 client
->event_priority
= priority
;
1489 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1490 assert_return(client
, -EINVAL
);
1492 client
->event
= sd_event_unref(client
->event
);
1497 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1498 assert_return(client
, NULL
);
1500 return client
->event
;
1503 static sd_dhcp6_client
*dhcp6_client_free(sd_dhcp6_client
*client
) {
1506 client_reset(client
);
1508 client
->fd
= safe_close(client
->fd
);
1510 sd_dhcp6_client_detach_event(client
);
1512 free(client
->req_opts
);
1514 return mfree(client
);
1517 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client
, sd_dhcp6_client
, dhcp6_client_free
);
1519 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1520 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1523 assert_return(ret
, -EINVAL
);
1525 client
= new0(sd_dhcp6_client
, 1);
1530 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1531 client
->ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
;
1532 client
->ifindex
= -1;
1533 client
->request
= DHCP6_REQUEST_IA_NA
;
1536 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1537 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1538 if (!client
->req_opts
)
1541 for (t
= 0; t
< client
->req_opts_len
; t
++)
1542 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);
1544 *ret
= TAKE_PTR(client
);