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
];
58 bool prefix_delegation
;
59 be32_t transaction_id
;
60 usec_t transaction_start
;
61 struct sd_dhcp6_lease
*lease
;
63 bool information_request
;
65 size_t req_opts_allocated
;
68 sd_event_source
*receive_message
;
69 usec_t retransmit_time
;
70 uint8_t retransmit_count
;
71 sd_event_source
*timeout_resend
;
72 sd_event_source
*timeout_resend_expire
;
73 sd_dhcp6_client_callback_t callback
;
79 static const uint16_t default_req_opts
[] = {
80 SD_DHCP6_OPTION_DNS_SERVERS
,
81 SD_DHCP6_OPTION_DOMAIN_LIST
,
82 SD_DHCP6_OPTION_NTP_SERVER
,
83 SD_DHCP6_OPTION_SNTP_SERVERS
,
86 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
87 [DHCP6_SOLICIT
] = "SOLICIT",
88 [DHCP6_ADVERTISE
] = "ADVERTISE",
89 [DHCP6_REQUEST
] = "REQUEST",
90 [DHCP6_CONFIRM
] = "CONFIRM",
91 [DHCP6_RENEW
] = "RENEW",
92 [DHCP6_REBIND
] = "REBIND",
93 [DHCP6_REPLY
] = "REPLY",
94 [DHCP6_RELEASE
] = "RELEASE",
95 [DHCP6_DECLINE
] = "DECLINE",
96 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
97 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
98 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
99 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
102 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
104 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
105 [DHCP6_STATUS_SUCCESS
] = "Success",
106 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
107 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
108 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
109 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
110 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
113 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
115 #define DHCP6_CLIENT_DONT_DESTROY(client) \
116 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
118 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
120 int sd_dhcp6_client_set_callback(
121 sd_dhcp6_client
*client
,
122 sd_dhcp6_client_callback_t cb
,
125 assert_return(client
, -EINVAL
);
127 client
->callback
= cb
;
128 client
->userdata
= userdata
;
133 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client
*client
, int ifindex
) {
135 assert_return(client
, -EINVAL
);
136 assert_return(ifindex
>= -1, -EINVAL
);
137 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
139 client
->ifindex
= ifindex
;
143 int sd_dhcp6_client_set_local_address(
144 sd_dhcp6_client
*client
,
145 const struct in6_addr
*local_address
) {
147 assert_return(client
, -EINVAL
);
148 assert_return(local_address
, -EINVAL
);
149 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
151 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
153 client
->local_address
= *local_address
;
158 int sd_dhcp6_client_set_mac(
159 sd_dhcp6_client
*client
,
160 const uint8_t *addr
, size_t addr_len
,
163 assert_return(client
, -EINVAL
);
164 assert_return(addr
, -EINVAL
);
165 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
166 assert_return(arp_type
> 0, -EINVAL
);
168 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
170 if (arp_type
== ARPHRD_ETHER
)
171 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
172 else if (arp_type
== ARPHRD_INFINIBAND
)
173 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
177 if (client
->mac_addr_len
== addr_len
&&
178 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
181 memcpy(&client
->mac_addr
, addr
, addr_len
);
182 client
->mac_addr_len
= addr_len
;
183 client
->arp_type
= arp_type
;
188 static int client_ensure_duid(sd_dhcp6_client
*client
) {
189 if (client
->duid_len
!= 0)
192 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
196 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
197 * without further modification. Otherwise, if duid_type is supported, DUID
198 * is set based on that type. Otherwise, an error is returned.
200 int sd_dhcp6_client_set_duid(
201 sd_dhcp6_client
*client
,
207 assert_return(client
, -EINVAL
);
208 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
209 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
212 r
= dhcp_validate_duid_len(duid_type
, duid_len
);
218 client
->duid
.type
= htobe16(duid_type
);
219 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
220 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
221 } else if (duid_type
== DUID_TYPE_EN
) {
222 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
231 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
232 assert_return(client
, -EINVAL
);
233 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
235 client
->ia_na
.ia_na
.id
= htobe32(iaid
);
236 client
->ia_pd
.ia_pd
.id
= htobe32(iaid
);
241 int sd_dhcp6_client_set_fqdn(
242 sd_dhcp6_client
*client
,
245 assert_return(client
, -EINVAL
);
247 /* Make sure FQDN qualifies as DNS and as Linux hostname */
249 !(hostname_is_valid(fqdn
, false) && dns_name_is_valid(fqdn
) > 0))
252 return free_and_strdup(&client
->fqdn
, fqdn
);
255 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
256 assert_return(client
, -EINVAL
);
257 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
259 client
->information_request
= enabled
;
264 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
265 assert_return(client
, -EINVAL
);
266 assert_return(enabled
, -EINVAL
);
268 *enabled
= client
->information_request
;
273 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
276 assert_return(client
, -EINVAL
);
277 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
281 case SD_DHCP6_OPTION_DNS_SERVERS
:
282 case SD_DHCP6_OPTION_DOMAIN_LIST
:
283 case SD_DHCP6_OPTION_SNTP_SERVERS
:
284 case SD_DHCP6_OPTION_NTP_SERVER
:
285 case SD_DHCP6_OPTION_RAPID_COMMIT
:
292 for (t
= 0; t
< client
->req_opts_len
; t
++)
293 if (client
->req_opts
[t
] == htobe16(option
))
296 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
297 client
->req_opts_len
+ 1))
300 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
305 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client
*client
, bool delegation
) {
306 assert_return(client
, -EINVAL
);
308 client
->prefix_delegation
= delegation
;
313 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
314 assert_return(client
, -EINVAL
);
320 *ret
= client
->lease
;
325 static void client_notify(sd_dhcp6_client
*client
, int event
) {
328 if (client
->callback
)
329 client
->callback(client
, event
, client
->userdata
);
332 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
336 dhcp6_lease_clear_timers(&client
->lease
->ia
);
337 sd_dhcp6_lease_unref(client
->lease
);
340 client
->lease
= lease
;
343 static int client_reset(sd_dhcp6_client
*client
) {
346 client_set_lease(client
, NULL
);
348 client
->receive_message
=
349 sd_event_source_unref(client
->receive_message
);
351 client
->transaction_id
= 0;
352 client
->transaction_start
= 0;
354 client
->ia_na
.timeout_t1
=
355 sd_event_source_unref(client
->ia_na
.timeout_t1
);
356 client
->ia_na
.timeout_t2
=
357 sd_event_source_unref(client
->ia_na
.timeout_t2
);
359 client
->retransmit_time
= 0;
360 client
->retransmit_count
= 0;
361 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
362 client
->timeout_resend_expire
=
363 sd_event_source_unref(client
->timeout_resend_expire
);
365 client
->state
= DHCP6_STATE_STOPPED
;
370 static void client_stop(sd_dhcp6_client
*client
, int error
) {
371 DHCP6_CLIENT_DONT_DESTROY(client
);
375 client_notify(client
, error
);
377 client_reset(client
);
380 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
381 _cleanup_free_ DHCP6Message
*message
= NULL
;
382 struct in6_addr all_servers
=
383 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
384 size_t len
, optlen
= 512;
392 len
= sizeof(DHCP6Message
) + optlen
;
394 message
= malloc0(len
);
398 opt
= (uint8_t *)(message
+ 1);
400 message
->transaction_id
= client
->transaction_id
;
402 switch(client
->state
) {
403 case DHCP6_STATE_INFORMATION_REQUEST
:
404 message
->type
= DHCP6_INFORMATION_REQUEST
;
408 case DHCP6_STATE_SOLICITATION
:
409 message
->type
= DHCP6_SOLICIT
;
411 r
= dhcp6_option_append(&opt
, &optlen
,
412 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
416 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
421 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
426 if (client
->prefix_delegation
) {
427 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->ia_pd
);
437 case DHCP6_STATE_REQUEST
:
438 case DHCP6_STATE_RENEW
:
440 if (client
->state
== DHCP6_STATE_REQUEST
)
441 message
->type
= DHCP6_REQUEST
;
443 message
->type
= DHCP6_RENEW
;
445 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
446 client
->lease
->serverid_len
,
447 client
->lease
->serverid
);
451 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
456 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
461 if (client
->prefix_delegation
) {
462 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
472 case DHCP6_STATE_REBIND
:
473 message
->type
= DHCP6_REBIND
;
475 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
480 r
= dhcp6_option_append_fqdn(&opt
, &optlen
, client
->fqdn
);
485 if (client
->prefix_delegation
) {
486 r
= dhcp6_option_append_pd(opt
, optlen
, &client
->lease
->pd
);
496 case DHCP6_STATE_STOPPED
:
497 case DHCP6_STATE_BOUND
:
501 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
502 client
->req_opts_len
* sizeof(be16_t
),
507 assert(client
->duid_len
);
508 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
509 client
->duid_len
, &client
->duid
);
513 elapsed_usec
= time_now
- client
->transaction_start
;
514 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
515 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
517 elapsed_time
= 0xffff;
519 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
520 sizeof(elapsed_time
), &elapsed_time
);
524 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
529 log_dhcp6_client(client
, "Sent %s",
530 dhcp6_message_type_to_string(message
->type
));
535 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
536 sd_dhcp6_client
*client
= userdata
;
540 assert(client
->lease
);
542 client
->lease
->ia
.timeout_t2
=
543 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
545 log_dhcp6_client(client
, "Timeout T2");
547 client_start(client
, DHCP6_STATE_REBIND
);
552 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
553 sd_dhcp6_client
*client
= userdata
;
557 assert(client
->lease
);
559 client
->lease
->ia
.timeout_t1
=
560 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
562 log_dhcp6_client(client
, "Timeout T1");
564 client_start(client
, DHCP6_STATE_RENEW
);
569 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
570 sd_dhcp6_client
*client
= userdata
;
571 DHCP6_CLIENT_DONT_DESTROY(client
);
572 enum DHCP6State state
;
576 assert(client
->event
);
578 state
= client
->state
;
580 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
582 /* RFC 3315, section 18.1.4., says that "...the client may choose to
583 use a Solicit message to locate a new DHCP server..." */
584 if (state
== DHCP6_STATE_REBIND
)
585 client_start(client
, DHCP6_STATE_SOLICITATION
);
590 static usec_t
client_timeout_compute_random(usec_t val
) {
591 return val
- val
/ 10 +
592 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
595 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
597 sd_dhcp6_client
*client
= userdata
;
598 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
599 usec_t max_retransmit_duration
= 0;
600 uint8_t max_retransmit_count
= 0;
601 char time_string
[FORMAT_TIMESPAN_MAX
];
606 assert(client
->event
);
608 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
610 switch (client
->state
) {
611 case DHCP6_STATE_INFORMATION_REQUEST
:
612 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
613 max_retransmit_time
= DHCP6_INF_MAX_RT
;
617 case DHCP6_STATE_SOLICITATION
:
619 if (client
->retransmit_count
&& client
->lease
) {
620 client_start(client
, DHCP6_STATE_REQUEST
);
624 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
625 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
629 case DHCP6_STATE_REQUEST
:
630 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
631 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
632 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
636 case DHCP6_STATE_RENEW
:
637 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
638 max_retransmit_time
= DHCP6_REN_MAX_RT
;
640 /* RFC 3315, section 18.1.3. says max retransmit duration will
641 be the remaining time until T2. Instead of setting MRD,
642 wait for T2 to trigger with the same end result */
646 case DHCP6_STATE_REBIND
:
647 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
648 max_retransmit_time
= DHCP6_REB_MAX_RT
;
650 if (!client
->timeout_resend_expire
) {
651 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
654 client_stop(client
, r
);
657 max_retransmit_duration
= expire
* USEC_PER_SEC
;
662 case DHCP6_STATE_STOPPED
:
663 case DHCP6_STATE_BOUND
:
667 if (max_retransmit_count
&&
668 client
->retransmit_count
>= max_retransmit_count
) {
669 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
673 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
677 r
= client_send_message(client
, time_now
);
679 client
->retransmit_count
++;
681 if (!client
->retransmit_time
) {
682 client
->retransmit_time
=
683 client_timeout_compute_random(init_retransmit_time
);
685 if (client
->state
== DHCP6_STATE_SOLICITATION
)
686 client
->retransmit_time
+= init_retransmit_time
/ 10;
689 if (max_retransmit_time
&&
690 client
->retransmit_time
> max_retransmit_time
/ 2)
691 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
693 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
696 log_dhcp6_client(client
, "Next retransmission in %s",
697 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
699 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
700 clock_boottime_or_monotonic(),
701 time_now
+ client
->retransmit_time
,
702 10 * USEC_PER_MSEC
, client_timeout_resend
,
707 r
= sd_event_source_set_priority(client
->timeout_resend
,
708 client
->event_priority
);
712 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
716 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
718 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
719 max_retransmit_duration
/ USEC_PER_SEC
);
721 r
= sd_event_add_time(client
->event
,
722 &client
->timeout_resend_expire
,
723 clock_boottime_or_monotonic(),
724 time_now
+ max_retransmit_duration
,
726 client_timeout_resend_expire
, client
);
730 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
731 client
->event_priority
);
735 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
742 client_stop(client
, r
);
747 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
753 if (client
->ia_na
.ia_na
.id
)
756 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, &iaid
);
760 client
->ia_na
.ia_na
.id
= iaid
;
761 client
->ia_pd
.ia_pd
.id
= iaid
;
766 static int client_parse_message(
767 sd_dhcp6_client
*client
,
768 DHCP6Message
*message
,
770 sd_dhcp6_lease
*lease
) {
773 bool clientid
= false;
774 uint32_t lt_t1
= ~0, lt_t2
= ~0;
778 assert(len
>= sizeof(DHCP6Message
));
781 len
-= sizeof(DHCP6Message
);
784 DHCP6Option
*option
= (DHCP6Option
*)&message
->options
[pos
];
785 uint16_t optcode
, optlen
;
790 if (len
< offsetof(DHCP6Option
, data
) ||
791 len
< offsetof(DHCP6Option
, data
) + be16toh(option
->len
))
794 optcode
= be16toh(option
->code
);
795 optlen
= be16toh(option
->len
);
796 optval
= option
->data
;
799 case SD_DHCP6_OPTION_CLIENTID
:
801 log_dhcp6_client(client
, "%s contains multiple clientids",
802 dhcp6_message_type_to_string(message
->type
));
806 if (optlen
!= client
->duid_len
||
807 memcmp(&client
->duid
, optval
, optlen
) != 0) {
808 log_dhcp6_client(client
, "%s DUID does not match",
809 dhcp6_message_type_to_string(message
->type
));
817 case SD_DHCP6_OPTION_SERVERID
:
818 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
820 log_dhcp6_client(client
, "%s contains multiple serverids",
821 dhcp6_message_type_to_string(message
->type
));
825 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
831 case SD_DHCP6_OPTION_PREFERENCE
:
835 r
= dhcp6_lease_set_preference(lease
, optval
[0]);
841 case SD_DHCP6_OPTION_STATUS_CODE
:
842 status
= dhcp6_option_parse_status(option
);
844 log_dhcp6_client(client
, "%s Status %s",
845 dhcp6_message_type_to_string(message
->type
),
846 dhcp6_message_status_to_string(status
));
847 dhcp6_lease_free_ia(&lease
->ia
);
848 dhcp6_lease_free_ia(&lease
->pd
);
855 case SD_DHCP6_OPTION_IA_NA
:
856 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
857 log_dhcp6_client(client
, "Information request ignoring IA NA option");
862 r
= dhcp6_option_parse_ia(option
, &lease
->ia
);
863 if (r
< 0 && r
!= -ENOMSG
)
866 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
870 if (client
->ia_na
.ia_na
.id
!= iaid_lease
) {
871 log_dhcp6_client(client
, "%s has wrong IAID for IA NA",
872 dhcp6_message_type_to_string(message
->type
));
876 if (lease
->ia
.addresses
) {
877 lt_t1
= MIN(lt_t1
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
878 lt_t2
= MIN(lt_t2
, be32toh(lease
->ia
.ia_na
.lifetime_t1
));
883 case SD_DHCP6_OPTION_IA_PD
:
884 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
885 log_dhcp6_client(client
, "Information request ignoring IA PD option");
890 r
= dhcp6_option_parse_ia(option
, &lease
->pd
);
891 if (r
< 0 && r
!= -ENOMSG
)
894 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
898 if (client
->ia_pd
.ia_pd
.id
!= iaid_lease
) {
899 log_dhcp6_client(client
, "%s has wrong IAID for IA PD",
900 dhcp6_message_type_to_string(message
->type
));
904 if (lease
->pd
.addresses
) {
905 lt_t1
= MIN(lt_t1
, be32toh(lease
->pd
.ia_pd
.lifetime_t1
));
906 lt_t2
= MIN(lt_t2
, be32toh(lease
->pd
.ia_pd
.lifetime_t2
));
911 case SD_DHCP6_OPTION_RAPID_COMMIT
:
912 r
= dhcp6_lease_set_rapid_commit(lease
);
918 case SD_DHCP6_OPTION_DNS_SERVERS
:
919 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
925 case SD_DHCP6_OPTION_DOMAIN_LIST
:
926 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
932 case SD_DHCP6_OPTION_NTP_SERVER
:
933 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
939 case SD_DHCP6_OPTION_SNTP_SERVERS
:
940 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
947 pos
+= sizeof(*option
) + optlen
;
951 log_dhcp6_client(client
, "%s has incomplete options",
952 dhcp6_message_type_to_string(message
->type
));
956 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
957 r
= dhcp6_lease_get_serverid(lease
, NULL
, NULL
);
959 log_dhcp6_client(client
, "%s has no server id",
960 dhcp6_message_type_to_string(message
->type
));
965 if (lease
->ia
.addresses
) {
966 lease
->ia
.ia_na
.lifetime_t1
= htobe32(lt_t1
);
967 lease
->ia
.ia_na
.lifetime_t2
= htobe32(lt_t2
);
970 if (lease
->pd
.addresses
) {
971 lease
->pd
.ia_pd
.lifetime_t1
= htobe32(lt_t1
);
972 lease
->pd
.ia_pd
.lifetime_t2
= htobe32(lt_t2
);
979 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
980 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
987 if (reply
->type
!= DHCP6_REPLY
)
990 r
= dhcp6_lease_new(&lease
);
994 r
= client_parse_message(client
, reply
, len
, lease
);
998 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
999 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
1007 client_set_lease(client
, lease
);
1010 return DHCP6_STATE_BOUND
;
1013 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
1014 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
1015 uint8_t pref_advertise
= 0, pref_lease
= 0;
1018 if (advertise
->type
!= DHCP6_ADVERTISE
)
1021 r
= dhcp6_lease_new(&lease
);
1025 r
= client_parse_message(client
, advertise
, len
, lease
);
1029 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
1033 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
1035 if (r
< 0 || pref_advertise
> pref_lease
) {
1036 client_set_lease(client
, lease
);
1041 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
1042 r
= DHCP6_STATE_REQUEST
;
1047 static int client_receive_message(
1053 sd_dhcp6_client
*client
= userdata
;
1054 DHCP6_CLIENT_DONT_DESTROY(client
);
1055 _cleanup_free_ DHCP6Message
*message
= NULL
;
1056 ssize_t buflen
, len
;
1061 assert(client
->event
);
1063 buflen
= next_datagram_size_fd(fd
);
1067 message
= malloc(buflen
);
1071 len
= recv(fd
, message
, buflen
, 0);
1073 if (IN_SET(errno
, EAGAIN
, EINTR
))
1076 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
1079 if ((size_t) len
< sizeof(DHCP6Message
)) {
1080 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
1084 switch(message
->type
) {
1092 case DHCP6_INFORMATION_REQUEST
:
1093 case DHCP6_RELAY_FORW
:
1094 case DHCP6_RELAY_REPL
:
1097 case DHCP6_ADVERTISE
:
1099 case DHCP6_RECONFIGURE
:
1103 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
1107 if (client
->transaction_id
!= (message
->transaction_id
&
1108 htobe32(0x00ffffff)))
1111 switch (client
->state
) {
1112 case DHCP6_STATE_INFORMATION_REQUEST
:
1113 r
= client_receive_reply(client
, message
, len
);
1117 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
1119 client_start(client
, DHCP6_STATE_STOPPED
);
1123 case DHCP6_STATE_SOLICITATION
:
1124 r
= client_receive_advertise(client
, message
, len
);
1126 if (r
== DHCP6_STATE_REQUEST
) {
1127 client_start(client
, r
);
1132 _fallthrough_
; /* for Soliciation Rapid Commit option check */
1133 case DHCP6_STATE_REQUEST
:
1134 case DHCP6_STATE_RENEW
:
1135 case DHCP6_STATE_REBIND
:
1137 r
= client_receive_reply(client
, message
, len
);
1141 if (r
== DHCP6_STATE_BOUND
) {
1143 r
= client_start(client
, DHCP6_STATE_BOUND
);
1145 client_stop(client
, r
);
1149 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1154 case DHCP6_STATE_BOUND
:
1158 case DHCP6_STATE_STOPPED
:
1163 log_dhcp6_client(client
, "Recv %s",
1164 dhcp6_message_type_to_string(message
->type
));
1169 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1171 usec_t timeout
, time_now
;
1172 char time_string
[FORMAT_TIMESPAN_MAX
];
1174 assert_return(client
, -EINVAL
);
1175 assert_return(client
->event
, -EINVAL
);
1176 assert_return(client
->ifindex
> 0, -EINVAL
);
1177 assert_return(client
->state
!= state
, -EINVAL
);
1179 client
->timeout_resend_expire
=
1180 sd_event_source_unref(client
->timeout_resend_expire
);
1181 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1182 client
->retransmit_time
= 0;
1183 client
->retransmit_count
= 0;
1185 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1189 if (!client
->receive_message
) {
1190 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1191 client
->fd
, EPOLLIN
, client_receive_message
,
1196 r
= sd_event_source_set_priority(client
->receive_message
,
1197 client
->event_priority
);
1201 r
= sd_event_source_set_description(client
->receive_message
,
1202 "dhcp6-receive-message");
1208 case DHCP6_STATE_STOPPED
:
1209 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1210 client
->state
= DHCP6_STATE_STOPPED
;
1216 case DHCP6_STATE_SOLICITATION
:
1217 client
->state
= DHCP6_STATE_SOLICITATION
;
1221 case DHCP6_STATE_INFORMATION_REQUEST
:
1222 case DHCP6_STATE_REQUEST
:
1223 case DHCP6_STATE_RENEW
:
1224 case DHCP6_STATE_REBIND
:
1226 client
->state
= state
;
1230 case DHCP6_STATE_BOUND
:
1232 if (client
->lease
->ia
.ia_na
.lifetime_t1
== 0xffffffff ||
1233 client
->lease
->ia
.ia_na
.lifetime_t2
== 0xffffffff) {
1235 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1236 be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
),
1237 be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
));
1242 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.ia_na
.lifetime_t1
) * USEC_PER_SEC
);
1244 log_dhcp6_client(client
, "T1 expires in %s",
1245 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1247 r
= sd_event_add_time(client
->event
,
1248 &client
->lease
->ia
.timeout_t1
,
1249 clock_boottime_or_monotonic(), time_now
+ timeout
,
1250 10 * USEC_PER_SEC
, client_timeout_t1
,
1255 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1256 client
->event_priority
);
1260 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1264 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.ia_na
.lifetime_t2
) * USEC_PER_SEC
);
1266 log_dhcp6_client(client
, "T2 expires in %s",
1267 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1269 r
= sd_event_add_time(client
->event
,
1270 &client
->lease
->ia
.timeout_t2
,
1271 clock_boottime_or_monotonic(), time_now
+ timeout
,
1272 10 * USEC_PER_SEC
, client_timeout_t2
,
1277 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1278 client
->event_priority
);
1282 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1286 client
->state
= state
;
1291 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1292 client
->transaction_start
= time_now
;
1294 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1295 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1300 r
= sd_event_source_set_priority(client
->timeout_resend
,
1301 client
->event_priority
);
1305 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1312 client_reset(client
);
1316 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1317 assert_return(client
, -EINVAL
);
1319 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1321 client
->fd
= safe_close(client
->fd
);
1326 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1327 assert_return(client
, -EINVAL
);
1329 return client
->state
!= DHCP6_STATE_STOPPED
;
1332 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1333 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1336 assert_return(client
, -EINVAL
);
1337 assert_return(client
->event
, -EINVAL
);
1338 assert_return(client
->ifindex
> 0, -EINVAL
);
1339 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1341 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1344 r
= client_reset(client
);
1348 r
= client_ensure_iaid(client
);
1352 r
= client_ensure_duid(client
);
1356 if (client
->fd
< 0) {
1357 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1359 _cleanup_free_
char *p
= NULL
;
1361 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1362 return log_dhcp6_client_errno(client
, r
,
1363 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1369 if (client
->information_request
)
1370 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1372 log_dhcp6_client(client
, "Started in %s mode",
1373 client
->information_request
? "Information request":
1376 return client_start(client
, state
);
1379 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1382 assert_return(client
, -EINVAL
);
1383 assert_return(!client
->event
, -EBUSY
);
1386 client
->event
= sd_event_ref(event
);
1388 r
= sd_event_default(&client
->event
);
1393 client
->event_priority
= priority
;
1398 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1399 assert_return(client
, -EINVAL
);
1401 client
->event
= sd_event_unref(client
->event
);
1406 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1407 assert_return(client
, NULL
);
1409 return client
->event
;
1412 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1417 assert(client
->n_ref
>= 1);
1423 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1428 assert(client
->n_ref
>= 1);
1431 if (client
->n_ref
> 0)
1434 client_reset(client
);
1436 client
->fd
= safe_close(client
->fd
);
1438 sd_dhcp6_client_detach_event(client
);
1440 free(client
->req_opts
);
1442 return mfree(client
);
1445 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1446 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1449 assert_return(ret
, -EINVAL
);
1451 client
= new0(sd_dhcp6_client
, 1);
1456 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1457 client
->ia_pd
.type
= SD_DHCP6_OPTION_IA_PD
;
1458 client
->ifindex
= -1;
1461 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1462 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1463 if (!client
->req_opts
)
1466 for (t
= 0; t
< client
->req_opts_len
; t
++)
1467 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);