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 cb
;
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(sd_dhcp6_client
*client
, sd_dhcp6_client_callback_t cb
, void *userdata
) {
115 assert_return(client
, -EINVAL
);
118 client
->userdata
= userdata
;
123 int sd_dhcp6_client_set_index(sd_dhcp6_client
*client
, int interface_index
) {
124 assert_return(client
, -EINVAL
);
125 assert_return(interface_index
>= -1, -EINVAL
);
127 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
129 client
->index
= interface_index
;
134 int sd_dhcp6_client_set_local_address(sd_dhcp6_client
*client
, const struct in6_addr
*local_address
) {
135 assert_return(client
, -EINVAL
);
136 assert_return(local_address
, -EINVAL
);
137 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
139 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
141 client
->local_address
= *local_address
;
146 int sd_dhcp6_client_set_mac(
147 sd_dhcp6_client
*client
,
148 const uint8_t *addr
, size_t addr_len
,
151 assert_return(client
, -EINVAL
);
152 assert_return(addr
, -EINVAL
);
153 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
154 assert_return(arp_type
> 0, -EINVAL
);
156 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
158 if (arp_type
== ARPHRD_ETHER
)
159 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
160 else if (arp_type
== ARPHRD_INFINIBAND
)
161 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
165 if (client
->mac_addr_len
== addr_len
&&
166 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
169 memcpy(&client
->mac_addr
, addr
, addr_len
);
170 client
->mac_addr_len
= addr_len
;
171 client
->arp_type
= arp_type
;
176 static int client_ensure_duid(sd_dhcp6_client
*client
) {
177 if (client
->duid_len
!= 0)
180 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
183 int sd_dhcp6_client_set_duid(sd_dhcp6_client
*client
, uint16_t duid_type
,
184 uint8_t *duid
, size_t duid_len
) {
186 assert_return(client
, -EINVAL
);
187 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
190 r
= dhcp_validate_duid_len(duid_type
, duid_len
);
193 client
->duid
.type
= htobe16(duid_type
);
194 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
195 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
201 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, uint32_t iaid
) {
202 assert_return(client
, -EINVAL
);
203 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
205 client
->ia_na
.id
= htobe32(iaid
);
210 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
211 assert_return(client
, -EINVAL
);
212 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
214 client
->information_request
= enabled
;
219 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
220 assert_return(client
, -EINVAL
);
221 assert_return(enabled
, -EINVAL
);
223 *enabled
= client
->information_request
;
228 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
231 assert_return(client
, -EINVAL
);
232 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
235 case SD_DHCP6_OPTION_DNS_SERVERS
:
236 case SD_DHCP6_OPTION_DOMAIN_LIST
:
237 case SD_DHCP6_OPTION_SNTP_SERVERS
:
238 case SD_DHCP6_OPTION_NTP_SERVER
:
245 for (t
= 0; t
< client
->req_opts_len
; t
++)
246 if (client
->req_opts
[t
] == htobe16(option
))
249 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
250 client
->req_opts_len
+ 1))
253 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
258 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
259 assert_return(client
, -EINVAL
);
265 *ret
= client
->lease
;
270 static void client_notify(sd_dhcp6_client
*client
, int event
) {
272 client
->cb(client
, event
, client
->userdata
);
275 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
277 dhcp6_lease_clear_timers(&client
->lease
->ia
);
278 sd_dhcp6_lease_unref(client
->lease
);
280 client
->lease
= lease
;
283 static int client_reset(sd_dhcp6_client
*client
) {
284 assert_return(client
, -EINVAL
);
286 client_set_lease(client
, NULL
);
288 client
->receive_message
=
289 sd_event_source_unref(client
->receive_message
);
291 client
->fd
= safe_close(client
->fd
);
293 client
->transaction_id
= 0;
294 client
->transaction_start
= 0;
296 client
->ia_na
.timeout_t1
=
297 sd_event_source_unref(client
->ia_na
.timeout_t1
);
298 client
->ia_na
.timeout_t2
=
299 sd_event_source_unref(client
->ia_na
.timeout_t2
);
301 client
->retransmit_time
= 0;
302 client
->retransmit_count
= 0;
303 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
304 client
->timeout_resend_expire
=
305 sd_event_source_unref(client
->timeout_resend_expire
);
307 client
->state
= DHCP6_STATE_STOPPED
;
312 static void client_stop(sd_dhcp6_client
*client
, int error
) {
313 DHCP6_CLIENT_DONT_DESTROY(client
);
317 client_notify(client
, error
);
319 client_reset(client
);
322 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
323 _cleanup_free_ DHCP6Message
*message
= NULL
;
324 struct in6_addr all_servers
=
325 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
326 size_t len
, optlen
= 512;
332 len
= sizeof(DHCP6Message
) + optlen
;
334 message
= malloc0(len
);
338 opt
= (uint8_t *)(message
+ 1);
340 message
->transaction_id
= client
->transaction_id
;
342 switch(client
->state
) {
343 case DHCP6_STATE_INFORMATION_REQUEST
:
344 message
->type
= DHCP6_INFORMATION_REQUEST
;
348 case DHCP6_STATE_SOLICITATION
:
349 message
->type
= DHCP6_SOLICIT
;
351 r
= dhcp6_option_append(&opt
, &optlen
,
352 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
356 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
362 case DHCP6_STATE_REQUEST
:
363 case DHCP6_STATE_RENEW
:
365 if (client
->state
== DHCP6_STATE_REQUEST
)
366 message
->type
= DHCP6_REQUEST
;
368 message
->type
= DHCP6_RENEW
;
370 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
371 client
->lease
->serverid_len
,
372 client
->lease
->serverid
);
376 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
382 case DHCP6_STATE_REBIND
:
383 message
->type
= DHCP6_REBIND
;
385 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
391 case DHCP6_STATE_STOPPED
:
392 case DHCP6_STATE_BOUND
:
396 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
397 client
->req_opts_len
* sizeof(be16_t
),
402 assert (client
->duid_len
);
403 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
404 client
->duid_len
, &client
->duid
);
408 elapsed_usec
= time_now
- client
->transaction_start
;
409 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
410 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
412 elapsed_time
= 0xffff;
414 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
415 sizeof(elapsed_time
), &elapsed_time
);
419 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
424 log_dhcp6_client(client
, "Sent %s",
425 dhcp6_message_type_to_string(message
->type
));
430 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
432 sd_dhcp6_client
*client
= userdata
;
434 assert_return(s
, -EINVAL
);
435 assert_return(client
, -EINVAL
);
436 assert_return(client
->lease
, -EINVAL
);
438 client
->lease
->ia
.timeout_t2
=
439 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
441 log_dhcp6_client(client
, "Timeout T2");
443 client_start(client
, DHCP6_STATE_REBIND
);
448 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
450 sd_dhcp6_client
*client
= userdata
;
452 assert_return(s
, -EINVAL
);
453 assert_return(client
, -EINVAL
);
454 assert_return(client
->lease
, -EINVAL
);
456 client
->lease
->ia
.timeout_t1
=
457 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
459 log_dhcp6_client(client
, "Timeout T1");
461 client_start(client
, DHCP6_STATE_RENEW
);
466 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
468 sd_dhcp6_client
*client
= userdata
;
469 DHCP6_CLIENT_DONT_DESTROY(client
);
470 enum DHCP6State state
;
474 assert(client
->event
);
476 state
= client
->state
;
478 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
480 /* RFC 3315, section 18.1.4., says that "...the client may choose to
481 use a Solicit message to locate a new DHCP server..." */
482 if (state
== DHCP6_STATE_REBIND
)
483 client_start(client
, DHCP6_STATE_SOLICITATION
);
488 static usec_t
client_timeout_compute_random(usec_t val
) {
489 return val
- val
/ 10 +
490 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
493 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
496 sd_dhcp6_client
*client
= userdata
;
497 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
498 usec_t max_retransmit_duration
= 0;
499 uint8_t max_retransmit_count
= 0;
500 char time_string
[FORMAT_TIMESPAN_MAX
];
505 assert(client
->event
);
507 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
509 switch (client
->state
) {
510 case DHCP6_STATE_INFORMATION_REQUEST
:
511 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
512 max_retransmit_time
= DHCP6_INF_MAX_RT
;
516 case DHCP6_STATE_SOLICITATION
:
518 if (client
->retransmit_count
&& client
->lease
) {
519 client_start(client
, DHCP6_STATE_REQUEST
);
523 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
524 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
528 case DHCP6_STATE_REQUEST
:
529 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
530 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
531 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
535 case DHCP6_STATE_RENEW
:
536 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
537 max_retransmit_time
= DHCP6_REN_MAX_RT
;
539 /* RFC 3315, section 18.1.3. says max retransmit duration will
540 be the remaining time until T2. Instead of setting MRD,
541 wait for T2 to trigger with the same end result */
545 case DHCP6_STATE_REBIND
:
546 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
547 max_retransmit_time
= DHCP6_REB_MAX_RT
;
549 if (!client
->timeout_resend_expire
) {
550 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
553 client_stop(client
, r
);
556 max_retransmit_duration
= expire
* USEC_PER_SEC
;
561 case DHCP6_STATE_STOPPED
:
562 case DHCP6_STATE_BOUND
:
566 if (max_retransmit_count
&&
567 client
->retransmit_count
>= max_retransmit_count
) {
568 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
572 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
576 r
= client_send_message(client
, time_now
);
578 client
->retransmit_count
++;
580 if (!client
->retransmit_time
) {
581 client
->retransmit_time
=
582 client_timeout_compute_random(init_retransmit_time
);
584 if (client
->state
== DHCP6_STATE_SOLICITATION
)
585 client
->retransmit_time
+= init_retransmit_time
/ 10;
588 if (max_retransmit_time
&&
589 client
->retransmit_time
> max_retransmit_time
/ 2)
590 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
592 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
595 log_dhcp6_client(client
, "Next retransmission in %s",
596 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
598 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
599 clock_boottime_or_monotonic(),
600 time_now
+ client
->retransmit_time
,
601 10 * USEC_PER_MSEC
, client_timeout_resend
,
606 r
= sd_event_source_set_priority(client
->timeout_resend
,
607 client
->event_priority
);
611 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
615 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
617 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
618 max_retransmit_duration
/ USEC_PER_SEC
);
620 r
= sd_event_add_time(client
->event
,
621 &client
->timeout_resend_expire
,
622 clock_boottime_or_monotonic(),
623 time_now
+ max_retransmit_duration
,
625 client_timeout_resend_expire
, client
);
629 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
630 client
->event_priority
);
634 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
641 client_stop(client
, r
);
646 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
651 if (client
->ia_na
.id
)
654 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
661 static int client_parse_message(sd_dhcp6_client
*client
,
662 DHCP6Message
*message
, size_t len
,
663 sd_dhcp6_lease
*lease
) {
665 uint8_t *optval
, *option
, *id
= NULL
;
666 uint16_t optcode
, status
;
667 size_t optlen
, id_len
;
668 bool clientid
= false;
671 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
672 len
-= sizeof(DHCP6Message
);
674 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
677 case SD_DHCP6_OPTION_CLIENTID
:
679 log_dhcp6_client(client
, "%s contains multiple clientids",
680 dhcp6_message_type_to_string(message
->type
));
684 if (optlen
!= client
->duid_len
||
685 memcmp(&client
->duid
, optval
, optlen
) != 0) {
686 log_dhcp6_client(client
, "%s DUID does not match",
687 dhcp6_message_type_to_string(message
->type
));
695 case SD_DHCP6_OPTION_SERVERID
:
696 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
698 log_dhcp6_client(client
, "%s contains multiple serverids",
699 dhcp6_message_type_to_string(message
->type
));
703 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
709 case SD_DHCP6_OPTION_PREFERENCE
:
713 r
= dhcp6_lease_set_preference(lease
, *optval
);
719 case SD_DHCP6_OPTION_STATUS_CODE
:
723 status
= optval
[0] << 8 | optval
[1];
725 log_dhcp6_client(client
, "%s Status %s",
726 dhcp6_message_type_to_string(message
->type
),
727 dhcp6_message_status_to_string(status
));
733 case SD_DHCP6_OPTION_IA_NA
:
734 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
735 log_dhcp6_client(client
, "Information request ignoring IA NA option");
740 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
742 if (r
< 0 && r
!= -ENOMSG
)
745 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
749 if (client
->ia_na
.id
!= iaid_lease
) {
750 log_dhcp6_client(client
, "%s has wrong IAID",
751 dhcp6_message_type_to_string(message
->type
));
757 case SD_DHCP6_OPTION_RAPID_COMMIT
:
758 r
= dhcp6_lease_set_rapid_commit(lease
);
764 case SD_DHCP6_OPTION_DNS_SERVERS
:
765 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
771 case SD_DHCP6_OPTION_DOMAIN_LIST
:
772 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
778 case SD_DHCP6_OPTION_NTP_SERVER
:
779 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
785 case SD_DHCP6_OPTION_SNTP_SERVERS
:
786 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
798 if (r
< 0 || !clientid
) {
799 log_dhcp6_client(client
, "%s has incomplete options",
800 dhcp6_message_type_to_string(message
->type
));
804 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
805 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
807 log_dhcp6_client(client
, "%s has no server id",
808 dhcp6_message_type_to_string(message
->type
));
814 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
816 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
819 if (reply
->type
!= DHCP6_REPLY
)
822 r
= dhcp6_lease_new(&lease
);
826 r
= client_parse_message(client
, reply
, len
, lease
);
830 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
831 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
839 client_set_lease(client
, lease
);
842 return DHCP6_STATE_BOUND
;
845 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
847 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
848 uint8_t pref_advertise
= 0, pref_lease
= 0;
850 if (advertise
->type
!= DHCP6_ADVERTISE
)
853 r
= dhcp6_lease_new(&lease
);
857 r
= client_parse_message(client
, advertise
, len
, lease
);
861 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
865 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
867 if (r
< 0 || pref_advertise
> pref_lease
) {
868 client_set_lease(client
, lease
);
873 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
874 r
= DHCP6_STATE_REQUEST
;
879 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
880 sd_dhcp6_client
*client
= userdata
;
881 DHCP6_CLIENT_DONT_DESTROY(client
);
882 _cleanup_free_ DHCP6Message
*message
= NULL
;
888 assert(client
->event
);
890 buflen
= next_datagram_size_fd(fd
);
894 message
= malloc(buflen
);
898 len
= read(fd
, message
, buflen
);
900 if (errno
== EAGAIN
|| errno
== EINTR
)
903 log_dhcp6_client(client
, "Could not receive message from UDP socket: %m");
906 } else if ((size_t)len
< sizeof(DHCP6Message
))
909 switch(message
->type
) {
917 case DHCP6_INFORMATION_REQUEST
:
918 case DHCP6_RELAY_FORW
:
919 case DHCP6_RELAY_REPL
:
922 case DHCP6_ADVERTISE
:
924 case DHCP6_RECONFIGURE
:
928 log_dhcp6_client(client
, "unknown message type %d",
933 if (client
->transaction_id
!= (message
->transaction_id
&
934 htobe32(0x00ffffff)))
937 switch (client
->state
) {
938 case DHCP6_STATE_INFORMATION_REQUEST
:
939 r
= client_receive_reply(client
, message
, len
);
943 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
945 client_start(client
, DHCP6_STATE_STOPPED
);
949 case DHCP6_STATE_SOLICITATION
:
950 r
= client_receive_advertise(client
, message
, len
);
952 if (r
== DHCP6_STATE_REQUEST
) {
953 client_start(client
, r
);
958 /* fall through for Soliciation Rapid Commit option check */
959 case DHCP6_STATE_REQUEST
:
960 case DHCP6_STATE_RENEW
:
961 case DHCP6_STATE_REBIND
:
963 r
= client_receive_reply(client
, message
, len
);
967 if (r
== DHCP6_STATE_BOUND
) {
969 r
= client_start(client
, DHCP6_STATE_BOUND
);
971 client_stop(client
, r
);
975 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
980 case DHCP6_STATE_BOUND
:
984 case DHCP6_STATE_STOPPED
:
989 log_dhcp6_client(client
, "Recv %s",
990 dhcp6_message_type_to_string(message
->type
));
996 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
998 usec_t timeout
, time_now
;
999 char time_string
[FORMAT_TIMESPAN_MAX
];
1001 assert_return(client
, -EINVAL
);
1002 assert_return(client
->event
, -EINVAL
);
1003 assert_return(client
->index
> 0, -EINVAL
);
1004 assert_return(client
->state
!= state
, -EINVAL
);
1006 client
->timeout_resend_expire
=
1007 sd_event_source_unref(client
->timeout_resend_expire
);
1008 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1009 client
->retransmit_time
= 0;
1010 client
->retransmit_count
= 0;
1012 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1017 case DHCP6_STATE_STOPPED
:
1018 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1019 client
->state
= DHCP6_STATE_STOPPED
;
1025 case DHCP6_STATE_SOLICITATION
:
1026 client
->state
= DHCP6_STATE_SOLICITATION
;
1030 case DHCP6_STATE_INFORMATION_REQUEST
:
1031 case DHCP6_STATE_REQUEST
:
1032 case DHCP6_STATE_RENEW
:
1033 case DHCP6_STATE_REBIND
:
1035 client
->state
= state
;
1039 case DHCP6_STATE_BOUND
:
1041 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1042 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1044 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1045 be32toh(client
->lease
->ia
.lifetime_t1
),
1046 be32toh(client
->lease
->ia
.lifetime_t2
));
1051 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1053 log_dhcp6_client(client
, "T1 expires in %s",
1054 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1056 r
= sd_event_add_time(client
->event
,
1057 &client
->lease
->ia
.timeout_t1
,
1058 clock_boottime_or_monotonic(), time_now
+ timeout
,
1059 10 * USEC_PER_SEC
, client_timeout_t1
,
1064 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1065 client
->event_priority
);
1069 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1073 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1075 log_dhcp6_client(client
, "T2 expires in %s",
1076 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1078 r
= sd_event_add_time(client
->event
,
1079 &client
->lease
->ia
.timeout_t2
,
1080 clock_boottime_or_monotonic(), time_now
+ timeout
,
1081 10 * USEC_PER_SEC
, client_timeout_t2
,
1086 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1087 client
->event_priority
);
1091 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1095 client
->state
= state
;
1100 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1101 client
->transaction_start
= time_now
;
1103 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1104 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1109 r
= sd_event_source_set_priority(client
->timeout_resend
,
1110 client
->event_priority
);
1114 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1121 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1122 assert_return(client
, -EINVAL
);
1124 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1129 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1130 assert_return(client
, -EINVAL
);
1132 return client
->state
!= DHCP6_STATE_STOPPED
;
1135 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1137 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1139 assert_return(client
, -EINVAL
);
1140 assert_return(client
->event
, -EINVAL
);
1141 assert_return(client
->index
> 0, -EINVAL
);
1142 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1144 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1147 r
= client_reset(client
);
1151 r
= client_ensure_iaid(client
);
1155 r
= client_ensure_duid(client
);
1159 r
= dhcp6_network_bind_udp_socket(client
->index
, &client
->local_address
);
1165 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1166 client
->fd
, EPOLLIN
, client_receive_message
,
1171 r
= sd_event_source_set_priority(client
->receive_message
,
1172 client
->event_priority
);
1176 r
= sd_event_source_set_description(client
->receive_message
,
1177 "dhcp6-receive-message");
1181 if (client
->information_request
)
1182 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1184 log_dhcp6_client(client
, "Started in %s mode",
1185 client
->information_request
? "Information request":
1188 return client_start(client
, state
);
1191 client_reset(client
);
1195 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1198 assert_return(client
, -EINVAL
);
1199 assert_return(!client
->event
, -EBUSY
);
1202 client
->event
= sd_event_ref(event
);
1204 r
= sd_event_default(&client
->event
);
1209 client
->event_priority
= priority
;
1214 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1215 assert_return(client
, -EINVAL
);
1217 client
->event
= sd_event_unref(client
->event
);
1222 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1226 return client
->event
;
1229 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1234 assert(client
->n_ref
>= 1);
1240 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1245 assert(client
->n_ref
>= 1);
1248 if (client
->n_ref
> 0)
1251 client_reset(client
);
1253 sd_dhcp6_client_detach_event(client
);
1255 free(client
->req_opts
);
1261 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1262 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1265 assert_return(ret
, -EINVAL
);
1267 client
= new0(sd_dhcp6_client
, 1);
1273 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1279 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1281 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1282 if (!client
->req_opts
)
1285 for (t
= 0; t
< client
->req_opts_len
; t
++)
1286 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);