1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright (C) 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 struct sd_dhcp6_client
{
33 enum DHCP6State state
;
37 struct in6_addr local_address
;
38 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
43 bool prefix_delegation
;
44 be32_t transaction_id
;
45 usec_t transaction_start
;
46 struct sd_dhcp6_lease
*lease
;
48 bool information_request
;
50 size_t req_opts_allocated
;
53 sd_event_source
*receive_message
;
54 usec_t retransmit_time
;
55 uint8_t retransmit_count
;
56 sd_event_source
*timeout_resend
;
57 sd_event_source
*timeout_resend_expire
;
58 sd_dhcp6_client_callback_t callback
;
64 static const uint16_t default_req_opts
[] = {
65 SD_DHCP6_OPTION_DNS_SERVERS
,
66 SD_DHCP6_OPTION_DOMAIN_LIST
,
67 SD_DHCP6_OPTION_NTP_SERVER
,
68 SD_DHCP6_OPTION_SNTP_SERVERS
,
71 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
72 [DHCP6_SOLICIT
] = "SOLICIT",
73 [DHCP6_ADVERTISE
] = "ADVERTISE",
74 [DHCP6_REQUEST
] = "REQUEST",
75 [DHCP6_CONFIRM
] = "CONFIRM",
76 [DHCP6_RENEW
] = "RENEW",
77 [DHCP6_REBIND
] = "REBIND",
78 [DHCP6_REPLY
] = "REPLY",
79 [DHCP6_RELEASE
] = "RELEASE",
80 [DHCP6_DECLINE
] = "DECLINE",
81 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
82 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
83 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
84 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
87 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
89 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
90 [DHCP6_STATUS_SUCCESS
] = "Success",
91 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
92 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
93 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
94 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
95 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
98 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
100 #define DHCP6_CLIENT_DONT_DESTROY(client) \
101 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
103 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
105 int sd_dhcp6_client_set_callback(
106 sd_dhcp6_client
*client
,
107 sd_dhcp6_client_callback_t cb
,
110 assert_return(client
, -EINVAL
);
112 client
->callback
= cb
;
113 client
->userdata
= userdata
;
118 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
120 assert_return(client
, -EINVAL
);
121 assert_return(ifindex
>= -1, -EINVAL
);
122 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
124 client
->ifindex
= ifindex
;
128 int sd_dhcp6_client_set_local_address(
129 sd_dhcp6_client
*client
,
130 const struct in6_addr
*local_address
) {
132 assert_return(client
, -EINVAL
);
133 assert_return(local_address
, -EINVAL
);
134 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
136 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
138 client
->local_address
= *local_address
;
143 int sd_dhcp6_client_set_mac(
144 sd_dhcp6_client
*client
,
145 const uint8_t *addr
, size_t addr_len
,
148 assert_return(client
, -EINVAL
);
149 assert_return(addr
, -EINVAL
);
150 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
151 assert_return(arp_type
> 0, -EINVAL
);
153 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
155 if (arp_type
== ARPHRD_ETHER
)
156 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
157 else if (arp_type
== ARPHRD_INFINIBAND
)
158 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
162 if (client
->mac_addr_len
== addr_len
&&
163 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
166 memcpy(&client
->mac_addr
, addr
, addr_len
);
167 client
->mac_addr_len
= addr_len
;
168 client
->arp_type
= arp_type
;
173 static int client_ensure_duid(sd_dhcp6_client
*client
) {
174 if (client
->duid_len
!= 0)
177 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
181 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
182 * without further modification. Otherwise, if duid_type is supported, DUID
183 * is set based on that type. Otherwise, an error is returned.
185 int sd_dhcp6_client_set_duid(
186 sd_dhcp6_client
*client
,
192 assert_return(client
, -EINVAL
);
193 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
194 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
197 r
= dhcp_validate_duid_len(duid_type
, duid_len
);
203 client
->duid
.type
= htobe16(duid_type
);
204 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
205 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
206 } else if (duid_type
== DUID_TYPE_EN
) {
207 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
216 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
217 assert_return(client
, -EINVAL
);
218 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
220 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
221 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
226 int sd_dhcp6_client_set_fqdn(
227 sd_dhcp6_client
*client
,
230 assert_return(client
, -EINVAL
);
232 /* Make sure FQDN qualifies as DNS and as Linux hostname */
234 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
237 return free_and_strdup(&client
->fqdn
, fqdn
);
240 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
241 assert_return(client
, -EINVAL
);
242 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
244 client
->information_request
= enabled
;
249 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
250 assert_return(client
, -EINVAL
);
251 assert_return(enabled
, -EINVAL
);
253 *enabled
= client
->information_request
;
258 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
261 assert_return(client
, -EINVAL
);
262 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
266 case SD_DHCP6_OPTION_DNS_SERVERS
:
267 case SD_DHCP6_OPTION_DOMAIN_LIST
:
268 case SD_DHCP6_OPTION_SNTP_SERVERS
:
269 case SD_DHCP6_OPTION_NTP_SERVER
:
270 case SD_DHCP6_OPTION_RAPID_COMMIT
:
277 for (t
= 0; t
< client
->req_opts_len
; t
++)
278 if (client
->req_opts
[t
] == htobe16(option
))
281 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
282 client
->req_opts_len
+ 1))
285 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
290 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, bool delegation
) {
291 assert_return(client
, -EINVAL
);
293 client
->prefix_delegation
= delegation
;
298 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
299 assert_return(client
, -EINVAL
);
305 *ret
= client
->lease
;
310 static void client_notify(sd_dhcp6_client
*client
, int event
) {
313 if (client
->callback
)
314 client
->callback(client
, event
, client
->userdata
);
317 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
321 dhcp6_lease_clear_timers(&client
->lease
->ia
);
322 sd_dhcp6_lease_unref(client
->lease
);
325 client
->lease
= lease
;
328 static int client_reset(sd_dhcp6_client
*client
) {
331 client_set_lease(client
, NULL
);
333 client
->receive_message
=
334 sd_event_source_unref(client
->receive_message
);
336 client
->transaction_id
= 0;
337 client
->transaction_start
= 0;
339 client
->ia_na
.timeout_t1
=
340 sd_event_source_unref(client
->ia_na
.timeout_t1
);
341 client
->ia_na
.timeout_t2
=
342 sd_event_source_unref(client
->ia_na
.timeout_t2
);
344 client
->retransmit_time
= 0;
345 client
->retransmit_count
= 0;
346 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
347 client
->timeout_resend_expire
=
348 sd_event_source_unref(client
->timeout_resend_expire
);
350 client
->state
= DHCP6_STATE_STOPPED
;
355 static void client_stop(sd_dhcp6_client
*client
, int error
) {
356 DHCP6_CLIENT_DONT_DESTROY(client
);
360 client_notify(client
, error
);
362 client_reset(client
);
365 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
366 _cleanup_free_ DHCP6Message
*message
= NULL
;
367 struct in6_addr all_servers
=
368 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
369 size_t len
, optlen
= 512;
377 len
= sizeof(DHCP6Message
) + optlen
;
379 message
= malloc0(len
);
383 opt
= (uint8_t *)(message
+ 1);
385 message
->transaction_id
= client
->transaction_id
;
387 switch(client
->state
) {
388 case DHCP6_STATE_INFORMATION_REQUEST
:
389 message
->type
= DHCP6_INFORMATION_REQUEST
;
393 case DHCP6_STATE_SOLICITATION
:
394 message
->type
= DHCP6_SOLICIT
;
396 r
= dhcp6_option_append(&opt
, &optlen
,
397 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
401 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
406 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
411 if (client
->prefix_delegation
) {
412 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
422 case DHCP6_STATE_REQUEST
:
423 case DHCP6_STATE_RENEW
:
425 if (client
->state
== DHCP6_STATE_REQUEST
)
426 message
->type
= DHCP6_REQUEST
;
428 message
->type
= DHCP6_RENEW
;
430 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
431 client
->lease
->serverid_len
,
432 client
->lease
->serverid
);
436 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
441 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
446 if (client
->prefix_delegation
) {
447 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
457 case DHCP6_STATE_REBIND
:
458 message
->type
= DHCP6_REBIND
;
460 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
465 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
470 if (client
->prefix_delegation
) {
471 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
481 case DHCP6_STATE_STOPPED
:
482 case DHCP6_STATE_BOUND
:
486 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
487 client
->req_opts_len
* sizeof(be16_t
),
492 assert(client
->duid_len
);
493 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
494 client
->duid_len
, &client
->duid
);
498 elapsed_usec
= time_now
- client
->transaction_start
;
499 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
500 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
502 elapsed_time
= 0xffff;
504 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
505 sizeof(elapsed_time
), &elapsed_time
);
509 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
514 log_dhcp6_client(client
, "Sent %s",
515 dhcp6_message_type_to_string(message
->type
));
520 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
521 sd_dhcp6_client
*client
= userdata
;
525 assert(client
->lease
);
527 client
->lease
->ia
.timeout_t2
=
528 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
530 log_dhcp6_client(client
, "Timeout T2");
532 client_start(client
, DHCP6_STATE_REBIND
);
537 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
538 sd_dhcp6_client
*client
= userdata
;
542 assert(client
->lease
);
544 client
->lease
->ia
.timeout_t1
=
545 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
547 log_dhcp6_client(client
, "Timeout T1");
549 client_start(client
, DHCP6_STATE_RENEW
);
554 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
555 sd_dhcp6_client
*client
= userdata
;
556 DHCP6_CLIENT_DONT_DESTROY(client
);
557 enum DHCP6State state
;
561 assert(client
->event
);
563 state
= client
->state
;
565 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
567 /* RFC 3315, section 18.1.4., says that "...the client may choose to
568 use a Solicit message to locate a new DHCP server..." */
569 if (state
== DHCP6_STATE_REBIND
)
570 client_start(client
, DHCP6_STATE_SOLICITATION
);
575 static usec_t
client_timeout_compute_random(usec_t val
) {
576 return val
- val
/ 10 +
577 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
580 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
582 sd_dhcp6_client
*client
= userdata
;
583 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
584 usec_t max_retransmit_duration
= 0;
585 uint8_t max_retransmit_count
= 0;
586 char time_string
[FORMAT_TIMESPAN_MAX
];
591 assert(client
->event
);
593 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
595 switch (client
->state
) {
596 case DHCP6_STATE_INFORMATION_REQUEST
:
597 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
598 max_retransmit_time
= DHCP6_INF_MAX_RT
;
602 case DHCP6_STATE_SOLICITATION
:
604 if (client
->retransmit_count
&& client
->lease
) {
605 client_start(client
, DHCP6_STATE_REQUEST
);
609 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
610 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
614 case DHCP6_STATE_REQUEST
:
615 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
616 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
617 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
621 case DHCP6_STATE_RENEW
:
622 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
623 max_retransmit_time
= DHCP6_REN_MAX_RT
;
625 /* RFC 3315, section 18.1.3. says max retransmit duration will
626 be the remaining time until T2. Instead of setting MRD,
627 wait for T2 to trigger with the same end result */
631 case DHCP6_STATE_REBIND
:
632 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
633 max_retransmit_time
= DHCP6_REB_MAX_RT
;
635 if (!client
->timeout_resend_expire
) {
636 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
639 client_stop(client
, r
);
642 max_retransmit_duration
= expire
* USEC_PER_SEC
;
647 case DHCP6_STATE_STOPPED
:
648 case DHCP6_STATE_BOUND
:
652 if (max_retransmit_count
&&
653 client
->retransmit_count
>= max_retransmit_count
) {
654 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
658 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
662 r
= client_send_message(client
, time_now
);
664 client
->retransmit_count
++;
666 if (!client
->retransmit_time
) {
667 client
->retransmit_time
=
668 client_timeout_compute_random(init_retransmit_time
);
670 if (client
->state
== DHCP6_STATE_SOLICITATION
)
671 client
->retransmit_time
+= init_retransmit_time
/ 10;
674 if (max_retransmit_time
&&
675 client
->retransmit_time
> max_retransmit_time
/ 2)
676 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
678 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
681 log_dhcp6_client(client
, "Next retransmission in %s",
682 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
684 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
685 clock_boottime_or_monotonic(),
686 time_now
+ client
->retransmit_time
,
687 10 * USEC_PER_MSEC
, client_timeout_resend
,
692 r
= sd_event_source_set_priority(client
->timeout_resend
,
693 client
->event_priority
);
697 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
701 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
703 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
704 max_retransmit_duration
/ USEC_PER_SEC
);
706 r
= sd_event_add_time(client
->event
,
707 &client
->timeout_resend_expire
,
708 clock_boottime_or_monotonic(),
709 time_now
+ max_retransmit_duration
,
711 client_timeout_resend_expire
, client
);
715 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
716 client
->event_priority
);
720 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
727 client_stop(client
, r
);
732 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
738 if (client
->ia_na
.ia_na
.id
)
741 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, &iaid
);
745 client
->ia_na
.ia_na
.id
= iaid
;
746 client
->ia_pd
.ia_pd
.id
= iaid
;
751 static int client_parse_message(
752 sd_dhcp6_client
*client
,
753 DHCP6Message
*message
,
755 sd_dhcp6_lease
*lease
) {
758 bool clientid
= false;
759 uint32_t lt_t1
= ~0, lt_t2
= ~0;
763 assert(len
>= sizeof(DHCP6Message
));
766 len
-= sizeof(DHCP6Message
);
769 DHCP6Option
*option
= (DHCP6Option
*)&message
->options
[pos
];
770 uint16_t optcode
, optlen
;
775 if (len
< offsetof(DHCP6Option
, data
) ||
776 len
< offsetof(DHCP6Option
, data
) + be16toh(option
->len
))
779 optcode
= be16toh(option
->code
);
780 optlen
= be16toh(option
->len
);
781 optval
= option
->data
;
784 case SD_DHCP6_OPTION_CLIENTID
:
786 log_dhcp6_client(client
, "%s contains multiple clientids",
787 dhcp6_message_type_to_string(message
->type
));
791 if (optlen
!= client
->duid_len
||
792 memcmp(&client
->duid
, optval
, optlen
) != 0) {
793 log_dhcp6_client(client
, "%s DUID does not match",
794 dhcp6_message_type_to_string(message
->type
));
802 case SD_DHCP6_OPTION_SERVERID
:
803 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
805 log_dhcp6_client(client
, "%s contains multiple serverids",
806 dhcp6_message_type_to_string(message
->type
));
810 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
816 case SD_DHCP6_OPTION_PREFERENCE
:
820 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
826 case SD_DHCP6_OPTION_STATUS_CODE
:
827 status
= dhcp6_option_parse_status(option
);
829 log_dhcp6_client(client
, "%s Status %s",
830 dhcp6_message_type_to_string(message
->type
),
831 dhcp6_message_status_to_string(status
));
832 dhcp6_lease_free_ia(&lease
->ia
);
833 dhcp6_lease_free_ia(&lease
->pd
);
840 case SD_DHCP6_OPTION_IA_NA
:
841 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
842 log_dhcp6_client(client
, "Information request ignoring IA NA option");
847 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
848 if (r
< 0 && r
!= -ENOMSG
)
851 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
855 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
856 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
857 dhcp6_message_type_to_string(message
->type
));
861 if (lease
->ia
.addresses
) {
862 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
863 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
868 case SD_DHCP6_OPTION_IA_PD
:
869 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
870 log_dhcp6_client(client
, "Information request ignoring IA PD option");
875 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
876 if (r
< 0 && r
!= -ENOMSG
)
879 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
883 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
884 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
885 dhcp6_message_type_to_string(message
->type
));
889 if (lease
->pd
.addresses
) {
890 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
891 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
896 case SD_DHCP6_OPTION_RAPID_COMMIT
:
897 r
= dhcp6_lease_set_rapid_commit(lease
);
903 case SD_DHCP6_OPTION_DNS_SERVERS
:
904 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
910 case SD_DHCP6_OPTION_DOMAIN_LIST
:
911 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
917 case SD_DHCP6_OPTION_NTP_SERVER
:
918 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
924 case SD_DHCP6_OPTION_SNTP_SERVERS
:
925 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
932 pos
+= sizeof(*option
) + optlen
;
936 log_dhcp6_client(client
, "%s has incomplete options",
937 dhcp6_message_type_to_string(message
->type
));
941 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
942 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
944 log_dhcp6_client(client
, "%s has no server id",
945 dhcp6_message_type_to_string(message
->type
));
950 if (lease
->ia
.addresses
) {
951 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
952 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
955 if (lease
->pd
.addresses
) {
956 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
957 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
964 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
965 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
972 if (reply
->type
!= DHCP6_REPLY
)
975 r
= dhcp6_lease_new(&lease
);
979 r
= client_parse_message(client
, reply
, len
, lease
);
983 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
984 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
992 client_set_lease(client
, lease
);
995 return DHCP6_STATE_BOUND
;
998 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
999 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1000 uint8_t pref_advertise
= 0, pref_lease
= 0;
1003 if (advertise
->type
!= DHCP6_ADVERTISE
)
1006 r
= dhcp6_lease_new(&lease
);
1010 r
= client_parse_message(client
, advertise
, len
, lease
);
1014 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1018 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1020 if (r
< 0 || pref_advertise
> pref_lease
) {
1021 client_set_lease(client
, lease
);
1026 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1027 r
= DHCP6_STATE_REQUEST
;
1032 static int client_receive_message(
1038 sd_dhcp6_client
*client
= userdata
;
1039 DHCP6_CLIENT_DONT_DESTROY(client
);
1040 _cleanup_free_ DHCP6Message
*message
= NULL
;
1041 ssize_t buflen
, len
;
1046 assert(client
->event
);
1048 buflen
= next_datagram_size_fd(fd
);
1052 message
= malloc(buflen
);
1056 len
= recv(fd
, message
, buflen
, 0);
1058 if (IN_SET(errno
, EAGAIN
, EINTR
))
1061 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1064 if ((size_t) len
< sizeof(DHCP6Message
)) {
1065 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1069 switch(message
->type
) {
1077 case DHCP6_INFORMATION_REQUEST
:
1078 case DHCP6_RELAY_FORW
:
1079 case DHCP6_RELAY_REPL
:
1082 case DHCP6_ADVERTISE
:
1084 case DHCP6_RECONFIGURE
:
1088 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1092 if (client
->transaction_id
!= (message
->transaction_id
&
1093 htobe32(0x00ffffff)))
1096 switch (client
->state
) {
1097 case DHCP6_STATE_INFORMATION_REQUEST
:
1098 r
= client_receive_reply(client
, message
, len
);
1102 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1104 client_start(client
, DHCP6_STATE_STOPPED
);
1108 case DHCP6_STATE_SOLICITATION
:
1109 r
= client_receive_advertise(client
, message
, len
);
1111 if (r
== DHCP6_STATE_REQUEST
) {
1112 client_start(client
, r
);
1117 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1118 case DHCP6_STATE_REQUEST
:
1119 case DHCP6_STATE_RENEW
:
1120 case DHCP6_STATE_REBIND
:
1122 r
= client_receive_reply(client
, message
, len
);
1126 if (r
== DHCP6_STATE_BOUND
) {
1128 r
= client_start(client
, DHCP6_STATE_BOUND
);
1130 client_stop(client
, r
);
1134 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1139 case DHCP6_STATE_BOUND
:
1143 case DHCP6_STATE_STOPPED
:
1148 log_dhcp6_client(client
, "Recv %s",
1149 dhcp6_message_type_to_string(message
->type
));
1154 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1156 usec_t timeout
, time_now
;
1157 char time_string
[FORMAT_TIMESPAN_MAX
];
1159 assert_return(client
, -EINVAL
);
1160 assert_return(client
->event
, -EINVAL
);
1161 assert_return(client
->ifindex
> 0, -EINVAL
);
1162 assert_return(client
->state
!= state
, -EINVAL
);
1164 client
->timeout_resend_expire
=
1165 sd_event_source_unref(client
->timeout_resend_expire
);
1166 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1167 client
->retransmit_time
= 0;
1168 client
->retransmit_count
= 0;
1170 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1174 if (!client
->receive_message
) {
1175 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1176 client
->fd
, EPOLLIN
, client_receive_message
,
1181 r
= sd_event_source_set_priority(client
->receive_message
,
1182 client
->event_priority
);
1186 r
= sd_event_source_set_description(client
->receive_message
,
1187 "dhcp6-receive-message");
1193 case DHCP6_STATE_STOPPED
:
1194 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1195 client
->state
= DHCP6_STATE_STOPPED
;
1201 case DHCP6_STATE_SOLICITATION
:
1202 client
->state
= DHCP6_STATE_SOLICITATION
;
1206 case DHCP6_STATE_INFORMATION_REQUEST
:
1207 case DHCP6_STATE_REQUEST
:
1208 case DHCP6_STATE_RENEW
:
1209 case DHCP6_STATE_REBIND
:
1211 client
->state
= state
;
1215 case DHCP6_STATE_BOUND
:
1217 if (client
->lease
->ia
.ia_na
.lifetime_t1
== 0xffffffff ||
1218 client
->lease
->ia
.ia_na
.lifetime_t2
== 0xffffffff) {
1220 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1221 be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
),
1222 be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
));
1227 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
) * USEC_PER_SEC
);
1229 log_dhcp6_client(client
, "T1 expires in %s",
1230 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1232 r
= sd_event_add_time(client
->event
,
1233 &client
->lease
->ia
.timeout_t1
,
1234 clock_boottime_or_monotonic(), time_now
+ timeout
,
1235 10 * USEC_PER_SEC
, client_timeout_t1
,
1240 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1241 client
->event_priority
);
1245 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1249 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
) * USEC_PER_SEC
);
1251 log_dhcp6_client(client
, "T2 expires in %s",
1252 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1254 r
= sd_event_add_time(client
->event
,
1255 &client
->lease
->ia
.timeout_t2
,
1256 clock_boottime_or_monotonic(), time_now
+ timeout
,
1257 10 * USEC_PER_SEC
, client_timeout_t2
,
1262 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1263 client
->event_priority
);
1267 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1271 client
->state
= state
;
1276 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1277 client
->transaction_start
= time_now
;
1279 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1280 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1285 r
= sd_event_source_set_priority(client
->timeout_resend
,
1286 client
->event_priority
);
1290 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1297 client_reset(client
);
1301 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1302 assert_return(client
, -EINVAL
);
1304 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1306 client
->fd
= safe_close(client
->fd
);
1311 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1312 assert_return(client
, -EINVAL
);
1314 return client
->state
!= DHCP6_STATE_STOPPED
;
1317 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1318 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1321 assert_return(client
, -EINVAL
);
1322 assert_return(client
->event
, -EINVAL
);
1323 assert_return(client
->ifindex
> 0, -EINVAL
);
1324 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1326 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1329 r
= client_reset(client
);
1333 r
= client_ensure_iaid(client
);
1337 r
= client_ensure_duid(client
);
1341 if (client
->fd
< 0) {
1342 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1344 _cleanup_free_
char *p
= NULL
;
1346 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1347 return log_dhcp6_client_errno(client
, r
,
1348 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1354 if (client
->information_request
)
1355 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1357 log_dhcp6_client(client
, "Started in %s mode",
1358 client
->information_request
? "Information request":
1361 return client_start(client
, state
);
1364 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1367 assert_return(client
, -EINVAL
);
1368 assert_return(!client
->event
, -EBUSY
);
1371 client
->event
= sd_event_ref(event
);
1373 r
= sd_event_default(&client
->event
);
1378 client
->event_priority
= priority
;
1383 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1384 assert_return(client
, -EINVAL
);
1386 client
->event
= sd_event_unref(client
->event
);
1391 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1392 assert_return(client
, NULL
);
1394 return client
->event
;
1397 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1402 assert(client
->n_ref
>= 1);
1408 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1413 assert(client
->n_ref
>= 1);
1416 if (client
->n_ref
> 0)
1419 client_reset(client
);
1421 client
->fd
= safe_close(client
->fd
);
1423 sd_dhcp6_client_detach_event(client
);
1425 free(client
->req_opts
);
1427 return mfree(client
);
1430 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1431 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1434 assert_return(ret
, -EINVAL
);
1436 client
= new0(sd_dhcp6_client
, 1);
1441 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1442 client
->ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
;
1443 client
->ifindex
= -1;
1446 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1447 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1448 if (!client
->req_opts
)
1451 for (t
= 0; t
< client
->req_opts_len
; t
++)
1452 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);
1454 *ret
= TAKE_PTR(client
);