2 This file is part of systemd.
4 Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/ioctl.h>
23 #include <linux/if_infiniband.h>
25 #include "sd-dhcp6-client.h"
27 #include "alloc-util.h"
28 #include "dhcp-identifier.h"
29 #include "dhcp6-internal.h"
30 #include "dhcp6-lease-internal.h"
31 #include "dhcp6-protocol.h"
33 #include "in-addr-util.h"
34 #include "network-internal.h"
35 #include "random-util.h"
36 #include "socket-util.h"
37 #include "string-table.h"
40 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
42 struct sd_dhcp6_client
{
45 enum DHCP6State state
;
49 struct in6_addr local_address
;
50 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
54 be32_t transaction_id
;
55 usec_t transaction_start
;
56 struct sd_dhcp6_lease
*lease
;
58 bool information_request
;
60 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 int sd_dhcp6_client_set_duid(
195 sd_dhcp6_client
*client
,
201 assert_return(client
, -EINVAL
);
202 assert_return(duid_len
== 0 || duid
!= NULL
, -EINVAL
);
203 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
206 r
= dhcp_validate_duid_len(duid_type
, duid_len
);
212 client
->duid
.type
= htobe16(duid_type
);
213 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
214 client
->duid_len
= sizeof(client
->duid
.type
) + duid_len
;
215 } else if (duid_type
== DUID_TYPE_EN
) {
216 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
225 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
226 assert_return(client
, -EINVAL
);
227 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
229 client
->ia_na
.id
= htobe32(iaid
);
234 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
235 assert_return(client
, -EINVAL
);
236 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
238 client
->information_request
= enabled
;
243 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
244 assert_return(client
, -EINVAL
);
245 assert_return(enabled
, -EINVAL
);
247 *enabled
= client
->information_request
;
252 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
255 assert_return(client
, -EINVAL
);
256 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
260 case SD_DHCP6_OPTION_DNS_SERVERS
:
261 case SD_DHCP6_OPTION_DOMAIN_LIST
:
262 case SD_DHCP6_OPTION_SNTP_SERVERS
:
263 case SD_DHCP6_OPTION_NTP_SERVER
:
270 for (t
= 0; t
< client
->req_opts_len
; t
++)
271 if (client
->req_opts
[t
] == htobe16(option
))
274 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
275 client
->req_opts_len
+ 1))
278 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
283 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
284 assert_return(client
, -EINVAL
);
290 *ret
= client
->lease
;
295 static void client_notify(sd_dhcp6_client
*client
, int event
) {
298 if (client
->callback
)
299 client
->callback(client
, event
, client
->userdata
);
302 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
306 dhcp6_lease_clear_timers(&client
->lease
->ia
);
307 sd_dhcp6_lease_unref(client
->lease
);
310 client
->lease
= lease
;
313 static int client_reset(sd_dhcp6_client
*client
) {
316 client_set_lease(client
, NULL
);
318 client
->receive_message
=
319 sd_event_source_unref(client
->receive_message
);
321 client
->fd
= safe_close(client
->fd
);
323 client
->transaction_id
= 0;
324 client
->transaction_start
= 0;
326 client
->ia_na
.timeout_t1
=
327 sd_event_source_unref(client
->ia_na
.timeout_t1
);
328 client
->ia_na
.timeout_t2
=
329 sd_event_source_unref(client
->ia_na
.timeout_t2
);
331 client
->retransmit_time
= 0;
332 client
->retransmit_count
= 0;
333 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
334 client
->timeout_resend_expire
=
335 sd_event_source_unref(client
->timeout_resend_expire
);
337 client
->state
= DHCP6_STATE_STOPPED
;
342 static void client_stop(sd_dhcp6_client
*client
, int error
) {
343 DHCP6_CLIENT_DONT_DESTROY(client
);
347 client_notify(client
, error
);
349 client_reset(client
);
352 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
353 _cleanup_free_ DHCP6Message
*message
= NULL
;
354 struct in6_addr all_servers
=
355 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
356 size_t len
, optlen
= 512;
364 len
= sizeof(DHCP6Message
) + optlen
;
366 message
= malloc0(len
);
370 opt
= (uint8_t *)(message
+ 1);
372 message
->transaction_id
= client
->transaction_id
;
374 switch(client
->state
) {
375 case DHCP6_STATE_INFORMATION_REQUEST
:
376 message
->type
= DHCP6_INFORMATION_REQUEST
;
380 case DHCP6_STATE_SOLICITATION
:
381 message
->type
= DHCP6_SOLICIT
;
383 r
= dhcp6_option_append(&opt
, &optlen
,
384 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
388 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
394 case DHCP6_STATE_REQUEST
:
395 case DHCP6_STATE_RENEW
:
397 if (client
->state
== DHCP6_STATE_REQUEST
)
398 message
->type
= DHCP6_REQUEST
;
400 message
->type
= DHCP6_RENEW
;
402 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
403 client
->lease
->serverid_len
,
404 client
->lease
->serverid
);
408 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
414 case DHCP6_STATE_REBIND
:
415 message
->type
= DHCP6_REBIND
;
417 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
423 case DHCP6_STATE_STOPPED
:
424 case DHCP6_STATE_BOUND
:
428 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
429 client
->req_opts_len
* sizeof(be16_t
),
434 assert (client
->duid_len
);
435 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
436 client
->duid_len
, &client
->duid
);
440 elapsed_usec
= time_now
- client
->transaction_start
;
441 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
442 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
444 elapsed_time
= 0xffff;
446 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
447 sizeof(elapsed_time
), &elapsed_time
);
451 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
456 log_dhcp6_client(client
, "Sent %s",
457 dhcp6_message_type_to_string(message
->type
));
462 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
463 sd_dhcp6_client
*client
= userdata
;
467 assert(client
->lease
);
469 client
->lease
->ia
.timeout_t2
=
470 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
472 log_dhcp6_client(client
, "Timeout T2");
474 client_start(client
, DHCP6_STATE_REBIND
);
479 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
480 sd_dhcp6_client
*client
= userdata
;
484 assert(client
->lease
);
486 client
->lease
->ia
.timeout_t1
=
487 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
489 log_dhcp6_client(client
, "Timeout T1");
491 client_start(client
, DHCP6_STATE_RENEW
);
496 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
497 sd_dhcp6_client
*client
= userdata
;
498 DHCP6_CLIENT_DONT_DESTROY(client
);
499 enum DHCP6State state
;
503 assert(client
->event
);
505 state
= client
->state
;
507 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
509 /* RFC 3315, section 18.1.4., says that "...the client may choose to
510 use a Solicit message to locate a new DHCP server..." */
511 if (state
== DHCP6_STATE_REBIND
)
512 client_start(client
, DHCP6_STATE_SOLICITATION
);
517 static usec_t
client_timeout_compute_random(usec_t val
) {
518 return val
- val
/ 10 +
519 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
522 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
524 sd_dhcp6_client
*client
= userdata
;
525 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
526 usec_t max_retransmit_duration
= 0;
527 uint8_t max_retransmit_count
= 0;
528 char time_string
[FORMAT_TIMESPAN_MAX
];
533 assert(client
->event
);
535 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
537 switch (client
->state
) {
538 case DHCP6_STATE_INFORMATION_REQUEST
:
539 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
540 max_retransmit_time
= DHCP6_INF_MAX_RT
;
544 case DHCP6_STATE_SOLICITATION
:
546 if (client
->retransmit_count
&& client
->lease
) {
547 client_start(client
, DHCP6_STATE_REQUEST
);
551 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
552 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
556 case DHCP6_STATE_REQUEST
:
557 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
558 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
559 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
563 case DHCP6_STATE_RENEW
:
564 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
565 max_retransmit_time
= DHCP6_REN_MAX_RT
;
567 /* RFC 3315, section 18.1.3. says max retransmit duration will
568 be the remaining time until T2. Instead of setting MRD,
569 wait for T2 to trigger with the same end result */
573 case DHCP6_STATE_REBIND
:
574 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
575 max_retransmit_time
= DHCP6_REB_MAX_RT
;
577 if (!client
->timeout_resend_expire
) {
578 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
581 client_stop(client
, r
);
584 max_retransmit_duration
= expire
* USEC_PER_SEC
;
589 case DHCP6_STATE_STOPPED
:
590 case DHCP6_STATE_BOUND
:
594 if (max_retransmit_count
&&
595 client
->retransmit_count
>= max_retransmit_count
) {
596 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
600 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
604 r
= client_send_message(client
, time_now
);
606 client
->retransmit_count
++;
608 if (!client
->retransmit_time
) {
609 client
->retransmit_time
=
610 client_timeout_compute_random(init_retransmit_time
);
612 if (client
->state
== DHCP6_STATE_SOLICITATION
)
613 client
->retransmit_time
+= init_retransmit_time
/ 10;
616 if (max_retransmit_time
&&
617 client
->retransmit_time
> max_retransmit_time
/ 2)
618 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
620 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
623 log_dhcp6_client(client
, "Next retransmission in %s",
624 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
626 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
627 clock_boottime_or_monotonic(),
628 time_now
+ client
->retransmit_time
,
629 10 * USEC_PER_MSEC
, client_timeout_resend
,
634 r
= sd_event_source_set_priority(client
->timeout_resend
,
635 client
->event_priority
);
639 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
643 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
645 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
646 max_retransmit_duration
/ USEC_PER_SEC
);
648 r
= sd_event_add_time(client
->event
,
649 &client
->timeout_resend_expire
,
650 clock_boottime_or_monotonic(),
651 time_now
+ max_retransmit_duration
,
653 client_timeout_resend_expire
, client
);
657 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
658 client
->event_priority
);
662 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
669 client_stop(client
, r
);
674 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
679 if (client
->ia_na
.id
)
682 r
= dhcp_identifier_set_iaid(client
->ifindex
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
689 static int client_parse_message(
690 sd_dhcp6_client
*client
,
691 DHCP6Message
*message
,
693 sd_dhcp6_lease
*lease
) {
695 uint8_t *optval
, *option
, *id
= NULL
;
696 uint16_t optcode
, status
;
697 size_t optlen
, id_len
;
698 bool clientid
= false;
703 assert(len
>= sizeof(DHCP6Message
));
706 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
707 len
-= sizeof(DHCP6Message
);
709 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
712 case SD_DHCP6_OPTION_CLIENTID
:
714 log_dhcp6_client(client
, "%s contains multiple clientids",
715 dhcp6_message_type_to_string(message
->type
));
719 if (optlen
!= client
->duid_len
||
720 memcmp(&client
->duid
, optval
, optlen
) != 0) {
721 log_dhcp6_client(client
, "%s DUID does not match",
722 dhcp6_message_type_to_string(message
->type
));
730 case SD_DHCP6_OPTION_SERVERID
:
731 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
733 log_dhcp6_client(client
, "%s contains multiple serverids",
734 dhcp6_message_type_to_string(message
->type
));
738 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
744 case SD_DHCP6_OPTION_PREFERENCE
:
748 r
= dhcp6_lease_set_preference(lease
, *optval
);
754 case SD_DHCP6_OPTION_STATUS_CODE
:
758 status
= optval
[0] << 8 | optval
[1];
760 log_dhcp6_client(client
, "%s Status %s",
761 dhcp6_message_type_to_string(message
->type
),
762 dhcp6_message_status_to_string(status
));
768 case SD_DHCP6_OPTION_IA_NA
:
769 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
770 log_dhcp6_client(client
, "Information request ignoring IA NA option");
775 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
777 if (r
< 0 && r
!= -ENOMSG
)
780 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
784 if (client
->ia_na
.id
!= iaid_lease
) {
785 log_dhcp6_client(client
, "%s has wrong IAID",
786 dhcp6_message_type_to_string(message
->type
));
792 case SD_DHCP6_OPTION_RAPID_COMMIT
:
793 r
= dhcp6_lease_set_rapid_commit(lease
);
799 case SD_DHCP6_OPTION_DNS_SERVERS
:
800 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
806 case SD_DHCP6_OPTION_DOMAIN_LIST
:
807 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
813 case SD_DHCP6_OPTION_NTP_SERVER
:
814 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
820 case SD_DHCP6_OPTION_SNTP_SERVERS
:
821 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
833 if (r
< 0 || !clientid
) {
834 log_dhcp6_client(client
, "%s has incomplete options",
835 dhcp6_message_type_to_string(message
->type
));
839 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
840 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
842 log_dhcp6_client(client
, "%s has no server id",
843 dhcp6_message_type_to_string(message
->type
));
849 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
850 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
857 if (reply
->type
!= DHCP6_REPLY
)
860 r
= dhcp6_lease_new(&lease
);
864 r
= client_parse_message(client
, reply
, len
, lease
);
868 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
869 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
877 client_set_lease(client
, lease
);
880 return DHCP6_STATE_BOUND
;
883 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
884 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
885 uint8_t pref_advertise
= 0, pref_lease
= 0;
888 if (advertise
->type
!= DHCP6_ADVERTISE
)
891 r
= dhcp6_lease_new(&lease
);
895 r
= client_parse_message(client
, advertise
, len
, lease
);
899 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
903 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
905 if (r
< 0 || pref_advertise
> pref_lease
) {
906 client_set_lease(client
, lease
);
911 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
912 r
= DHCP6_STATE_REQUEST
;
917 static int client_receive_message(
923 sd_dhcp6_client
*client
= userdata
;
924 DHCP6_CLIENT_DONT_DESTROY(client
);
925 _cleanup_free_ DHCP6Message
*message
= NULL
;
931 assert(client
->event
);
933 buflen
= next_datagram_size_fd(fd
);
937 message
= malloc(buflen
);
941 len
= recv(fd
, message
, buflen
, 0);
943 if (errno
== EAGAIN
|| errno
== EINTR
)
946 return log_dhcp6_client_errno(client
, errno
, "Could not receive message from UDP socket: %m");
949 if ((size_t) len
< sizeof(DHCP6Message
)) {
950 log_dhcp6_client(client
, "Too small to be DHCP6 message: ignoring");
954 switch(message
->type
) {
962 case DHCP6_INFORMATION_REQUEST
:
963 case DHCP6_RELAY_FORW
:
964 case DHCP6_RELAY_REPL
:
967 case DHCP6_ADVERTISE
:
969 case DHCP6_RECONFIGURE
:
973 log_dhcp6_client(client
, "Unknown message type %d", message
->type
);
977 if (client
->transaction_id
!= (message
->transaction_id
&
978 htobe32(0x00ffffff)))
981 switch (client
->state
) {
982 case DHCP6_STATE_INFORMATION_REQUEST
:
983 r
= client_receive_reply(client
, message
, len
);
987 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
989 client_start(client
, DHCP6_STATE_STOPPED
);
993 case DHCP6_STATE_SOLICITATION
:
994 r
= client_receive_advertise(client
, message
, len
);
996 if (r
== DHCP6_STATE_REQUEST
) {
997 client_start(client
, r
);
1002 /* fall through */ /* for Soliciation Rapid Commit option check */
1003 case DHCP6_STATE_REQUEST
:
1004 case DHCP6_STATE_RENEW
:
1005 case DHCP6_STATE_REBIND
:
1007 r
= client_receive_reply(client
, message
, len
);
1011 if (r
== DHCP6_STATE_BOUND
) {
1013 r
= client_start(client
, DHCP6_STATE_BOUND
);
1015 client_stop(client
, r
);
1019 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
1024 case DHCP6_STATE_BOUND
:
1028 case DHCP6_STATE_STOPPED
:
1033 log_dhcp6_client(client
, "Recv %s",
1034 dhcp6_message_type_to_string(message
->type
));
1039 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1041 usec_t timeout
, time_now
;
1042 char time_string
[FORMAT_TIMESPAN_MAX
];
1044 assert_return(client
, -EINVAL
);
1045 assert_return(client
->event
, -EINVAL
);
1046 assert_return(client
->ifindex
> 0, -EINVAL
);
1047 assert_return(client
->state
!= state
, -EINVAL
);
1049 client
->timeout_resend_expire
=
1050 sd_event_source_unref(client
->timeout_resend_expire
);
1051 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1052 client
->retransmit_time
= 0;
1053 client
->retransmit_count
= 0;
1055 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1060 case DHCP6_STATE_STOPPED
:
1061 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1062 client
->state
= DHCP6_STATE_STOPPED
;
1068 case DHCP6_STATE_SOLICITATION
:
1069 client
->state
= DHCP6_STATE_SOLICITATION
;
1073 case DHCP6_STATE_INFORMATION_REQUEST
:
1074 case DHCP6_STATE_REQUEST
:
1075 case DHCP6_STATE_RENEW
:
1076 case DHCP6_STATE_REBIND
:
1078 client
->state
= state
;
1082 case DHCP6_STATE_BOUND
:
1084 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1085 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1087 log_dhcp6_client(client
, "Infinite T1 0x%08x or T2 0x%08x",
1088 be32toh(client
->lease
->ia
.lifetime_t1
),
1089 be32toh(client
->lease
->ia
.lifetime_t2
));
1094 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1096 log_dhcp6_client(client
, "T1 expires in %s",
1097 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1099 r
= sd_event_add_time(client
->event
,
1100 &client
->lease
->ia
.timeout_t1
,
1101 clock_boottime_or_monotonic(), time_now
+ timeout
,
1102 10 * USEC_PER_SEC
, client_timeout_t1
,
1107 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1108 client
->event_priority
);
1112 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1116 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1118 log_dhcp6_client(client
, "T2 expires in %s",
1119 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1121 r
= sd_event_add_time(client
->event
,
1122 &client
->lease
->ia
.timeout_t2
,
1123 clock_boottime_or_monotonic(), time_now
+ timeout
,
1124 10 * USEC_PER_SEC
, client_timeout_t2
,
1129 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1130 client
->event_priority
);
1134 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1138 client
->state
= state
;
1143 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1144 client
->transaction_start
= time_now
;
1146 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1147 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1152 r
= sd_event_source_set_priority(client
->timeout_resend
,
1153 client
->event_priority
);
1157 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1164 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1165 assert_return(client
, -EINVAL
);
1167 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1172 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1173 assert_return(client
, -EINVAL
);
1175 return client
->state
!= DHCP6_STATE_STOPPED
;
1178 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1179 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1182 assert_return(client
, -EINVAL
);
1183 assert_return(client
->event
, -EINVAL
);
1184 assert_return(client
->ifindex
> 0, -EINVAL
);
1185 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1187 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1190 r
= client_reset(client
);
1194 r
= client_ensure_iaid(client
);
1198 r
= client_ensure_duid(client
);
1202 r
= dhcp6_network_bind_udp_socket(client
->ifindex
, &client
->local_address
);
1204 _cleanup_free_
char *p
= NULL
;
1206 (void) in_addr_to_string(AF_INET6
, (const union in_addr_union
*) &client
->local_address
, &p
);
1207 return log_dhcp6_client_errno(client
, r
,
1208 "Failed to bind to UDP socket at address %s: %m", strna(p
));
1213 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1214 client
->fd
, EPOLLIN
, client_receive_message
,
1219 r
= sd_event_source_set_priority(client
->receive_message
,
1220 client
->event_priority
);
1224 r
= sd_event_source_set_description(client
->receive_message
,
1225 "dhcp6-receive-message");
1229 if (client
->information_request
)
1230 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1232 log_dhcp6_client(client
, "Started in %s mode",
1233 client
->information_request
? "Information request":
1236 return client_start(client
, state
);
1239 client_reset(client
);
1243 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1246 assert_return(client
, -EINVAL
);
1247 assert_return(!client
->event
, -EBUSY
);
1250 client
->event
= sd_event_ref(event
);
1252 r
= sd_event_default(&client
->event
);
1257 client
->event_priority
= priority
;
1262 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1263 assert_return(client
, -EINVAL
);
1265 client
->event
= sd_event_unref(client
->event
);
1270 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1271 assert_return(client
, NULL
);
1273 return client
->event
;
1276 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1281 assert(client
->n_ref
>= 1);
1287 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1292 assert(client
->n_ref
>= 1);
1295 if (client
->n_ref
> 0)
1298 client_reset(client
);
1300 sd_dhcp6_client_detach_event(client
);
1302 free(client
->req_opts
);
1303 return mfree(client
);
1306 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1307 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1310 assert_return(ret
, -EINVAL
);
1312 client
= new0(sd_dhcp6_client
, 1);
1317 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1318 client
->ifindex
= -1;
1321 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1322 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1323 if (!client
->req_opts
)
1326 for (t
= 0; t
< client
->req_opts_len
; t
++)
1327 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);