1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/ioctl.h>
24 #include <linux/if_infiniband.h>
26 #include "sd-dhcp6-client.h"
28 #include "alloc-util.h"
29 #include "dhcp-identifier.h"
30 #include "dhcp6-internal.h"
31 #include "dhcp6-lease-internal.h"
32 #include "dhcp6-protocol.h"
33 #include "dns-domain.h"
35 #include "hostname-util.h"
36 #include "in-addr-util.h"
37 #include "network-internal.h"
38 #include "random-util.h"
39 #include "socket-util.h"
40 #include "string-table.h"
43 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
45 struct sd_dhcp6_client
{
48 enum DHCP6State state
;
52 struct in6_addr local_address
;
53 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
57 be32_t transaction_id
;
58 usec_t transaction_start
;
59 struct sd_dhcp6_lease
*lease
;
61 bool information_request
;
63 size_t req_opts_allocated
;
66 sd_event_source
*receive_message
;
67 usec_t retransmit_time
;
68 uint8_t retransmit_count
;
69 sd_event_source
*timeout_resend
;
70 sd_event_source
*timeout_resend_expire
;
71 sd_dhcp6_client_callback_t callback
;
77 static const uint16_t default_req_opts
[] = {
78 SD_DHCP6_OPTION_DNS_SERVERS
,
79 SD_DHCP6_OPTION_DOMAIN_LIST
,
80 SD_DHCP6_OPTION_NTP_SERVER
,
81 SD_DHCP6_OPTION_SNTP_SERVERS
,
84 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
85 [DHCP6_SOLICIT
] = "SOLICIT",
86 [DHCP6_ADVERTISE
] = "ADVERTISE",
87 [DHCP6_REQUEST
] = "REQUEST",
88 [DHCP6_CONFIRM
] = "CONFIRM",
89 [DHCP6_RENEW
] = "RENEW",
90 [DHCP6_REBIND
] = "REBIND",
91 [DHCP6_REPLY
] = "REPLY",
92 [DHCP6_RELEASE
] = "RELEASE",
93 [DHCP6_DECLINE
] = "DECLINE",
94 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
95 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
96 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
97 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
100 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
102 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
103 [DHCP6_STATUS_SUCCESS
] = "Success",
104 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
105 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
106 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
107 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
108 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
111 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
113 #define DHCP6_CLIENT_DONT_DESTROY(client) \
114 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
116 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
118 int sd_dhcp6_client_set_callback(
119 sd_dhcp6_client
*client
,
120 sd_dhcp6_client_callback_t cb
,
123 assert_return(client
, -EINVAL
);
125 client
->callback
= cb
;
126 client
->userdata
= userdata
;
131 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
133 assert_return(client
, -EINVAL
);
134 assert_return(ifindex
>= -1, -EINVAL
);
135 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
137 client
->ifindex
= ifindex
;
141 int sd_dhcp6_client_set_local_address(
142 sd_dhcp6_client
*client
,
143 const struct in6_addr
*local_address
) {
145 assert_return(client
, -EINVAL
);
146 assert_return(local_address
, -EINVAL
);
147 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
149 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
151 client
->local_address
= *local_address
;
156 int sd_dhcp6_client_set_mac(
157 sd_dhcp6_client
*client
,
158 const uint8_t *addr
, size_t addr_len
,
161 assert_return(client
, -EINVAL
);
162 assert_return(addr
, -EINVAL
);
163 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
164 assert_return(arp_type
> 0, -EINVAL
);
166 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
168 if (arp_type
== ARPHRD_ETHER
)
169 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
170 else if (arp_type
== ARPHRD_INFINIBAND
)
171 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
175 if (client
->mac_addr_len
== addr_len
&&
176 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
179 memcpy(&client
->mac_addr
, addr
, addr_len
);
180 client
->mac_addr_len
= addr_len
;
181 client
->arp_type
= arp_type
;
186 static int client_ensure_duid(sd_dhcp6_client
*client
) {
187 if (client
->duid_len
!= 0)
190 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
194 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
195 * without further modification. Otherwise, if duid_type is supported, DUID
196 * is set based on that type. Otherwise, an error is returned.
198 int sd_dhcp6_client_set_duid(
199 sd_dhcp6_client
*client
,
205 assert_return(client
, -EINVAL
);
206 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
207 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
210 r
= dhcp_validate_duid_len(duid_type
, duid_len
);
216 client
->duid
.type
= htobe16(duid_type
);
217 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
218 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
219 } else if (duid_type
== DUID_TYPE_EN
) {
220 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
229 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
230 assert_return(client
, -EINVAL
);
231 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
233 client
->ia_na
.id
= htobe32(iaid
);
238 int sd_dhcp6_client_set_fqdn(
239 sd_dhcp6_client
*client
,
242 assert_return(client
, -EINVAL
);
244 /* Make sure FQDN qualifies as DNS and as Linux hostname */
246 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
249 return free_and_strdup(&client
->fqdn
, fqdn
);
252 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
253 assert_return(client
, -EINVAL
);
254 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
256 client
->information_request
= enabled
;
261 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
262 assert_return(client
, -EINVAL
);
263 assert_return(enabled
, -EINVAL
);
265 *enabled
= client
->information_request
;
270 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
273 assert_return(client
, -EINVAL
);
274 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
278 case SD_DHCP6_OPTION_DNS_SERVERS
:
279 case SD_DHCP6_OPTION_DOMAIN_LIST
:
280 case SD_DHCP6_OPTION_SNTP_SERVERS
:
281 case SD_DHCP6_OPTION_NTP_SERVER
:
288 for (t
= 0; t
< client
->req_opts_len
; t
++)
289 if (client
->req_opts
[t
] == htobe16(option
))
292 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
293 client
->req_opts_len
+ 1))
296 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
301 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
302 assert_return(client
, -EINVAL
);
308 *ret
= client
->lease
;
313 static void client_notify(sd_dhcp6_client
*client
, int event
) {
316 if (client
->callback
)
317 client
->callback(client
, event
, client
->userdata
);
320 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
324 dhcp6_lease_clear_timers(&client
->lease
->ia
);
325 sd_dhcp6_lease_unref(client
->lease
);
328 client
->lease
= lease
;
331 static int client_reset(sd_dhcp6_client
*client
) {
334 client_set_lease(client
, NULL
);
336 client
->receive_message
=
337 sd_event_source_unref(client
->receive_message
);
339 client
->fd
= safe_close(client
->fd
);
341 client
->transaction_id
= 0;
342 client
->transaction_start
= 0;
344 client
->ia_na
.timeout_t1
=
345 sd_event_source_unref(client
->ia_na
.timeout_t1
);
346 client
->ia_na
.timeout_t2
=
347 sd_event_source_unref(client
->ia_na
.timeout_t2
);
349 client
->retransmit_time
= 0;
350 client
->retransmit_count
= 0;
351 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
352 client
->timeout_resend_expire
=
353 sd_event_source_unref(client
->timeout_resend_expire
);
355 client
->state
= DHCP6_STATE_STOPPED
;
360 static void client_stop(sd_dhcp6_client
*client
, int error
) {
361 DHCP6_CLIENT_DONT_DESTROY(client
);
365 client_notify(client
, error
);
367 client_reset(client
);
370 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
371 _cleanup_free_ DHCP6Message
*message
= NULL
;
372 struct in6_addr all_servers
=
373 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
374 size_t len
, optlen
= 512;
382 len
= sizeof(DHCP6Message
) + optlen
;
384 message
= malloc0(len
);
388 opt
= (uint8_t *)(message
+ 1);
390 message
->transaction_id
= client
->transaction_id
;
392 switch(client
->state
) {
393 case DHCP6_STATE_INFORMATION_REQUEST
:
394 message
->type
= DHCP6_INFORMATION_REQUEST
;
398 case DHCP6_STATE_SOLICITATION
:
399 message
->type
= DHCP6_SOLICIT
;
401 r
= dhcp6_option_append(&opt
, &optlen
,
402 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
406 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
411 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
418 case DHCP6_STATE_REQUEST
:
419 case DHCP6_STATE_RENEW
:
421 if (client
->state
== DHCP6_STATE_REQUEST
)
422 message
->type
= DHCP6_REQUEST
;
424 message
->type
= DHCP6_RENEW
;
426 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
427 client
->lease
->serverid_len
,
428 client
->lease
->serverid
);
432 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
437 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
444 case DHCP6_STATE_REBIND
:
445 message
->type
= DHCP6_REBIND
;
447 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
452 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
459 case DHCP6_STATE_STOPPED
:
460 case DHCP6_STATE_BOUND
:
464 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
465 client
->req_opts_len
* sizeof(be16_t
),
470 assert(client
->duid_len
);
471 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
472 client
->duid_len
, &client
->duid
);
476 elapsed_usec
= time_now
- client
->transaction_start
;
477 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
478 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
480 elapsed_time
= 0xffff;
482 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
483 sizeof(elapsed_time
), &elapsed_time
);
487 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
492 log_dhcp6_client(client
, "Sent %s",
493 dhcp6_message_type_to_string(message
->type
));
498 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
499 sd_dhcp6_client
*client
= userdata
;
503 assert(client
->lease
);
505 client
->lease
->ia
.timeout_t2
=
506 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
508 log_dhcp6_client(client
, "Timeout T2");
510 client_start(client
, DHCP6_STATE_REBIND
);
515 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
516 sd_dhcp6_client
*client
= userdata
;
520 assert(client
->lease
);
522 client
->lease
->ia
.timeout_t1
=
523 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
525 log_dhcp6_client(client
, "Timeout T1");
527 client_start(client
, DHCP6_STATE_RENEW
);
532 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
533 sd_dhcp6_client
*client
= userdata
;
534 DHCP6_CLIENT_DONT_DESTROY(client
);
535 enum DHCP6State state
;
539 assert(client
->event
);
541 state
= client
->state
;
543 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
545 /* RFC 3315, section 18.1.4., says that "...the client may choose to
546 use a Solicit message to locate a new DHCP server..." */
547 if (state
== DHCP6_STATE_REBIND
)
548 client_start(client
, DHCP6_STATE_SOLICITATION
);
553 static usec_t
client_timeout_compute_random(usec_t val
) {
554 return val
- val
/ 10 +
555 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
558 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
560 sd_dhcp6_client
*client
= userdata
;
561 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
562 usec_t max_retransmit_duration
= 0;
563 uint8_t max_retransmit_count
= 0;
564 char time_string
[FORMAT_TIMESPAN_MAX
];
569 assert(client
->event
);
571 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
573 switch (client
->state
) {
574 case DHCP6_STATE_INFORMATION_REQUEST
:
575 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
576 max_retransmit_time
= DHCP6_INF_MAX_RT
;
580 case DHCP6_STATE_SOLICITATION
:
582 if (client
->retransmit_count
&& client
->lease
) {
583 client_start(client
, DHCP6_STATE_REQUEST
);
587 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
588 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
592 case DHCP6_STATE_REQUEST
:
593 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
594 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
595 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
599 case DHCP6_STATE_RENEW
:
600 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
601 max_retransmit_time
= DHCP6_REN_MAX_RT
;
603 /* RFC 3315, section 18.1.3. says max retransmit duration will
604 be the remaining time until T2. Instead of setting MRD,
605 wait for T2 to trigger with the same end result */
609 case DHCP6_STATE_REBIND
:
610 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
611 max_retransmit_time
= DHCP6_REB_MAX_RT
;
613 if (!client
->timeout_resend_expire
) {
614 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
617 client_stop(client
, r
);
620 max_retransmit_duration
= expire
* USEC_PER_SEC
;
625 case DHCP6_STATE_STOPPED
:
626 case DHCP6_STATE_BOUND
:
630 if (max_retransmit_count
&&
631 client
->retransmit_count
>= max_retransmit_count
) {
632 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
636 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
640 r
= client_send_message(client
, time_now
);
642 client
->retransmit_count
++;
644 if (!client
->retransmit_time
) {
645 client
->retransmit_time
=
646 client_timeout_compute_random(init_retransmit_time
);
648 if (client
->state
== DHCP6_STATE_SOLICITATION
)
649 client
->retransmit_time
+= init_retransmit_time
/ 10;
652 if (max_retransmit_time
&&
653 client
->retransmit_time
> max_retransmit_time
/ 2)
654 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
656 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
659 log_dhcp6_client(client
, "Next retransmission in %s",
660 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
662 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
663 clock_boottime_or_monotonic(),
664 time_now
+ client
->retransmit_time
,
665 10 * USEC_PER_MSEC
, client_timeout_resend
,
670 r
= sd_event_source_set_priority(client
->timeout_resend
,
671 client
->event_priority
);
675 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
679 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
681 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
682 max_retransmit_duration
/ USEC_PER_SEC
);
684 r
= sd_event_add_time(client
->event
,
685 &client
->timeout_resend_expire
,
686 clock_boottime_or_monotonic(),
687 time_now
+ max_retransmit_duration
,
689 client_timeout_resend_expire
, client
);
693 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
694 client
->event_priority
);
698 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
705 client_stop(client
, r
);
710 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
715 if (client
->ia_na
.id
)
718 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
725 static int client_parse_message(
726 sd_dhcp6_client
*client
,
727 DHCP6Message
*message
,
729 sd_dhcp6_lease
*lease
) {
731 uint8_t *optval
, *option
, *id
= NULL
;
732 uint16_t optcode
, status
;
733 size_t optlen
, id_len
;
734 bool clientid
= false;
739 assert(len
>= sizeof(DHCP6Message
));
742 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
743 len
-= sizeof(DHCP6Message
);
745 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
748 case SD_DHCP6_OPTION_CLIENTID
:
750 log_dhcp6_client(client
, "%s contains multiple clientids",
751 dhcp6_message_type_to_string(message
->type
));
755 if (optlen
!= client
->duid_len
||
756 memcmp(&client
->duid
, optval
, optlen
) != 0) {
757 log_dhcp6_client(client
, "%s DUID does not match",
758 dhcp6_message_type_to_string(message
->type
));
766 case SD_DHCP6_OPTION_SERVERID
:
767 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
769 log_dhcp6_client(client
, "%s contains multiple serverids",
770 dhcp6_message_type_to_string(message
->type
));
774 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
780 case SD_DHCP6_OPTION_PREFERENCE
:
784 r
= dhcp6_lease_set_preference(lease
, *optval
);
790 case SD_DHCP6_OPTION_STATUS_CODE
:
794 status
= optval
[0] << 8 | optval
[1];
796 log_dhcp6_client(client
, "%s Status %s",
797 dhcp6_message_type_to_string(message
->type
),
798 dhcp6_message_status_to_string(status
));
804 case SD_DHCP6_OPTION_IA_NA
:
805 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
806 log_dhcp6_client(client
, "Information request ignoring IA NA option");
811 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
813 if (r
< 0 && r
!= -ENOMSG
)
816 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
820 if (client
->ia_na
.id
!= iaid_lease
) {
821 log_dhcp6_client(client
, "%s has wrong IAID",
822 dhcp6_message_type_to_string(message
->type
));
828 case SD_DHCP6_OPTION_RAPID_COMMIT
:
829 r
= dhcp6_lease_set_rapid_commit(lease
);
835 case SD_DHCP6_OPTION_DNS_SERVERS
:
836 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
842 case SD_DHCP6_OPTION_DOMAIN_LIST
:
843 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
849 case SD_DHCP6_OPTION_NTP_SERVER
:
850 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
856 case SD_DHCP6_OPTION_SNTP_SERVERS
:
857 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
869 if (r
< 0 || !clientid
) {
870 log_dhcp6_client(client
, "%s has incomplete options",
871 dhcp6_message_type_to_string(message
->type
));
875 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
876 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
878 log_dhcp6_client(client
, "%s has no server id",
879 dhcp6_message_type_to_string(message
->type
));
885 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
886 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
893 if (reply
->type
!= DHCP6_REPLY
)
896 r
= dhcp6_lease_new(&lease
);
900 r
= client_parse_message(client
, reply
, len
, lease
);
904 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
905 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
913 client_set_lease(client
, lease
);
916 return DHCP6_STATE_BOUND
;
919 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
920 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
921 uint8_t pref_advertise
= 0, pref_lease
= 0;
924 if (advertise
->type
!= DHCP6_ADVERTISE
)
927 r
= dhcp6_lease_new(&lease
);
931 r
= client_parse_message(client
, advertise
, len
, lease
);
935 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
939 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
941 if (r
< 0 || pref_advertise
> pref_lease
) {
942 client_set_lease(client
, lease
);
947 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
948 r
= DHCP6_STATE_REQUEST
;
953 static int client_receive_message(
959 sd_dhcp6_client
*client
= userdata
;
960 DHCP6_CLIENT_DONT_DESTROY(client
);
961 _cleanup_free_ DHCP6Message
*message
= NULL
;
967 assert(client
->event
);
969 buflen
= next_datagram_size_fd(fd
);
973 message
= malloc(buflen
);
977 len
= recv(fd
, message
, buflen
, 0);
979 if (IN_SET(errno
, EAGAIN
, EINTR
))
982 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
985 if ((size_t) len
< sizeof(DHCP6Message
)) {
986 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
990 switch(message
->type
) {
998 case DHCP6_INFORMATION_REQUEST
:
999 case DHCP6_RELAY_FORW
:
1000 case DHCP6_RELAY_REPL
:
1003 case DHCP6_ADVERTISE
:
1005 case DHCP6_RECONFIGURE
:
1009 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1013 if (client
->transaction_id
!= (message
->transaction_id
&
1014 htobe32(0x00ffffff)))
1017 switch (client
->state
) {
1018 case DHCP6_STATE_INFORMATION_REQUEST
:
1019 r
= client_receive_reply(client
, message
, len
);
1023 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1025 client_start(client
, DHCP6_STATE_STOPPED
);
1029 case DHCP6_STATE_SOLICITATION
:
1030 r
= client_receive_advertise(client
, message
, len
);
1032 if (r
== DHCP6_STATE_REQUEST
) {
1033 client_start(client
, r
);
1038 /* fall through */ /* for Soliciation Rapid Commit option check */
1039 case DHCP6_STATE_REQUEST
:
1040 case DHCP6_STATE_RENEW
:
1041 case DHCP6_STATE_REBIND
:
1043 r
= client_receive_reply(client
, message
, len
);
1047 if (r
== DHCP6_STATE_BOUND
) {
1049 r
= client_start(client
, DHCP6_STATE_BOUND
);
1051 client_stop(client
, r
);
1055 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1060 case DHCP6_STATE_BOUND
:
1064 case DHCP6_STATE_STOPPED
:
1069 log_dhcp6_client(client
, "Recv %s",
1070 dhcp6_message_type_to_string(message
->type
));
1075 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1077 usec_t timeout
, time_now
;
1078 char time_string
[FORMAT_TIMESPAN_MAX
];
1080 assert_return(client
, -EINVAL
);
1081 assert_return(client
->event
, -EINVAL
);
1082 assert_return(client
->ifindex
> 0, -EINVAL
);
1083 assert_return(client
->state
!= state
, -EINVAL
);
1085 client
->timeout_resend_expire
=
1086 sd_event_source_unref(client
->timeout_resend_expire
);
1087 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1088 client
->retransmit_time
= 0;
1089 client
->retransmit_count
= 0;
1091 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1096 case DHCP6_STATE_STOPPED
:
1097 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1098 client
->state
= DHCP6_STATE_STOPPED
;
1104 case DHCP6_STATE_SOLICITATION
:
1105 client
->state
= DHCP6_STATE_SOLICITATION
;
1109 case DHCP6_STATE_INFORMATION_REQUEST
:
1110 case DHCP6_STATE_REQUEST
:
1111 case DHCP6_STATE_RENEW
:
1112 case DHCP6_STATE_REBIND
:
1114 client
->state
= state
;
1118 case DHCP6_STATE_BOUND
:
1120 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1121 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1123 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1124 be32toh(client
->lease
->ia
.lifetime_t1
),
1125 be32toh(client
->lease
->ia
.lifetime_t2
));
1130 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1132 log_dhcp6_client(client
, "T1 expires in %s",
1133 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1135 r
= sd_event_add_time(client
->event
,
1136 &client
->lease
->ia
.timeout_t1
,
1137 clock_boottime_or_monotonic(), time_now
+ timeout
,
1138 10 * USEC_PER_SEC
, client_timeout_t1
,
1143 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1144 client
->event_priority
);
1148 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1152 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1154 log_dhcp6_client(client
, "T2 expires in %s",
1155 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1157 r
= sd_event_add_time(client
->event
,
1158 &client
->lease
->ia
.timeout_t2
,
1159 clock_boottime_or_monotonic(), time_now
+ timeout
,
1160 10 * USEC_PER_SEC
, client_timeout_t2
,
1165 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1166 client
->event_priority
);
1170 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1174 client
->state
= state
;
1179 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1180 client
->transaction_start
= time_now
;
1182 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1183 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1188 r
= sd_event_source_set_priority(client
->timeout_resend
,
1189 client
->event_priority
);
1193 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1200 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1201 assert_return(client
, -EINVAL
);
1203 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1208 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1209 assert_return(client
, -EINVAL
);
1211 return client
->state
!= DHCP6_STATE_STOPPED
;
1214 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1215 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1218 assert_return(client
, -EINVAL
);
1219 assert_return(client
->event
, -EINVAL
);
1220 assert_return(client
->ifindex
> 0, -EINVAL
);
1221 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1223 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1226 r
= client_reset(client
);
1230 r
= client_ensure_iaid(client
);
1234 r
= client_ensure_duid(client
);
1238 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1240 _cleanup_free_
char *p
= NULL
;
1242 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1243 return log_dhcp6_client_errno(client
, r
,
1244 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1249 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1250 client
->fd
, EPOLLIN
, client_receive_message
,
1255 r
= sd_event_source_set_priority(client
->receive_message
,
1256 client
->event_priority
);
1260 r
= sd_event_source_set_description(client
->receive_message
,
1261 "dhcp6-receive-message");
1265 if (client
->information_request
)
1266 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1268 log_dhcp6_client(client
, "Started in %s mode",
1269 client
->information_request
? "Information request":
1272 return client_start(client
, state
);
1275 client_reset(client
);
1279 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1282 assert_return(client
, -EINVAL
);
1283 assert_return(!client
->event
, -EBUSY
);
1286 client
->event
= sd_event_ref(event
);
1288 r
= sd_event_default(&client
->event
);
1293 client
->event_priority
= priority
;
1298 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1299 assert_return(client
, -EINVAL
);
1301 client
->event
= sd_event_unref(client
->event
);
1306 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1307 assert_return(client
, NULL
);
1309 return client
->event
;
1312 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1317 assert(client
->n_ref
>= 1);
1323 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1328 assert(client
->n_ref
>= 1);
1331 if (client
->n_ref
> 0)
1334 client_reset(client
);
1336 sd_dhcp6_client_detach_event(client
);
1338 free(client
->req_opts
);
1340 return mfree(client
);
1343 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1344 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1347 assert_return(ret
, -EINVAL
);
1349 client
= new0(sd_dhcp6_client
, 1);
1354 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1355 client
->ifindex
= -1;
1358 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1359 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1360 if (!client
->req_opts
)
1363 for (t
= 0; t
< client
->req_opts_len
; t
++)
1364 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);