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
, 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
,
191 duid_len
- sizeof(duid
->type
));
195 memcpy(&client
->duid
, duid
, duid_len
);
196 client
->duid_len
= duid_len
;
202 int sd_dhcp6_client_set_iaid(sd_dhcp6_client
*client
, be32_t iaid
) {
203 assert_return(client
, -EINVAL
);
204 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
206 client
->ia_na
.id
= iaid
;
211 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
212 assert_return(client
, -EINVAL
);
213 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
215 client
->information_request
= enabled
;
220 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
221 assert_return(client
, -EINVAL
);
222 assert_return(enabled
, -EINVAL
);
224 *enabled
= client
->information_request
;
229 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
232 assert_return(client
, -EINVAL
);
233 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
236 case SD_DHCP6_OPTION_DNS_SERVERS
:
237 case SD_DHCP6_OPTION_DOMAIN_LIST
:
238 case SD_DHCP6_OPTION_SNTP_SERVERS
:
239 case SD_DHCP6_OPTION_NTP_SERVER
:
246 for (t
= 0; t
< client
->req_opts_len
; t
++)
247 if (client
->req_opts
[t
] == htobe16(option
))
250 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
251 client
->req_opts_len
+ 1))
254 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
259 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
260 assert_return(client
, -EINVAL
);
266 *ret
= client
->lease
;
271 static void client_notify(sd_dhcp6_client
*client
, int event
) {
273 client
->cb(client
, event
, client
->userdata
);
276 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
278 dhcp6_lease_clear_timers(&client
->lease
->ia
);
279 sd_dhcp6_lease_unref(client
->lease
);
281 client
->lease
= lease
;
284 static int client_reset(sd_dhcp6_client
*client
) {
285 assert_return(client
, -EINVAL
);
287 client_set_lease(client
, NULL
);
289 client
->receive_message
=
290 sd_event_source_unref(client
->receive_message
);
292 client
->fd
= safe_close(client
->fd
);
294 client
->transaction_id
= 0;
295 client
->transaction_start
= 0;
297 client
->ia_na
.timeout_t1
=
298 sd_event_source_unref(client
->ia_na
.timeout_t1
);
299 client
->ia_na
.timeout_t2
=
300 sd_event_source_unref(client
->ia_na
.timeout_t2
);
302 client
->retransmit_time
= 0;
303 client
->retransmit_count
= 0;
304 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
305 client
->timeout_resend_expire
=
306 sd_event_source_unref(client
->timeout_resend_expire
);
308 client
->state
= DHCP6_STATE_STOPPED
;
313 static void client_stop(sd_dhcp6_client
*client
, int error
) {
314 DHCP6_CLIENT_DONT_DESTROY(client
);
318 client_notify(client
, error
);
320 client_reset(client
);
323 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
324 _cleanup_free_ DHCP6Message
*message
= NULL
;
325 struct in6_addr all_servers
=
326 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
327 size_t len
, optlen
= 512;
333 len
= sizeof(DHCP6Message
) + optlen
;
335 message
= malloc0(len
);
339 opt
= (uint8_t *)(message
+ 1);
341 message
->transaction_id
= client
->transaction_id
;
343 switch(client
->state
) {
344 case DHCP6_STATE_INFORMATION_REQUEST
:
345 message
->type
= DHCP6_INFORMATION_REQUEST
;
349 case DHCP6_STATE_SOLICITATION
:
350 message
->type
= DHCP6_SOLICIT
;
352 r
= dhcp6_option_append(&opt
, &optlen
,
353 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
357 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
363 case DHCP6_STATE_REQUEST
:
364 case DHCP6_STATE_RENEW
:
366 if (client
->state
== DHCP6_STATE_REQUEST
)
367 message
->type
= DHCP6_REQUEST
;
369 message
->type
= DHCP6_RENEW
;
371 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
372 client
->lease
->serverid_len
,
373 client
->lease
->serverid
);
377 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
383 case DHCP6_STATE_REBIND
:
384 message
->type
= DHCP6_REBIND
;
386 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
392 case DHCP6_STATE_STOPPED
:
393 case DHCP6_STATE_BOUND
:
397 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
398 client
->req_opts_len
* sizeof(be16_t
),
403 assert (client
->duid_len
);
404 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
405 client
->duid_len
, &client
->duid
);
409 elapsed_usec
= time_now
- client
->transaction_start
;
410 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
411 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
413 elapsed_time
= 0xffff;
415 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
416 sizeof(elapsed_time
), &elapsed_time
);
420 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
425 log_dhcp6_client(client
, "Sent %s",
426 dhcp6_message_type_to_string(message
->type
));
431 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
433 sd_dhcp6_client
*client
= userdata
;
435 assert_return(s
, -EINVAL
);
436 assert_return(client
, -EINVAL
);
437 assert_return(client
->lease
, -EINVAL
);
439 client
->lease
->ia
.timeout_t2
=
440 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
442 log_dhcp6_client(client
, "Timeout T2");
444 client_start(client
, DHCP6_STATE_REBIND
);
449 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
451 sd_dhcp6_client
*client
= userdata
;
453 assert_return(s
, -EINVAL
);
454 assert_return(client
, -EINVAL
);
455 assert_return(client
->lease
, -EINVAL
);
457 client
->lease
->ia
.timeout_t1
=
458 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
460 log_dhcp6_client(client
, "Timeout T1");
462 client_start(client
, DHCP6_STATE_RENEW
);
467 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
469 sd_dhcp6_client
*client
= userdata
;
470 DHCP6_CLIENT_DONT_DESTROY(client
);
471 enum DHCP6State state
;
475 assert(client
->event
);
477 state
= client
->state
;
479 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
481 /* RFC 3315, section 18.1.4., says that "...the client may choose to
482 use a Solicit message to locate a new DHCP server..." */
483 if (state
== DHCP6_STATE_REBIND
)
484 client_start(client
, DHCP6_STATE_SOLICITATION
);
489 static usec_t
client_timeout_compute_random(usec_t val
) {
490 return val
- val
/ 10 +
491 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
494 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
497 sd_dhcp6_client
*client
= userdata
;
498 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
499 usec_t max_retransmit_duration
= 0;
500 uint8_t max_retransmit_count
= 0;
501 char time_string
[FORMAT_TIMESPAN_MAX
];
506 assert(client
->event
);
508 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
510 switch (client
->state
) {
511 case DHCP6_STATE_INFORMATION_REQUEST
:
512 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
513 max_retransmit_time
= DHCP6_INF_MAX_RT
;
517 case DHCP6_STATE_SOLICITATION
:
519 if (client
->retransmit_count
&& client
->lease
) {
520 client_start(client
, DHCP6_STATE_REQUEST
);
524 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
525 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
529 case DHCP6_STATE_REQUEST
:
530 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
531 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
532 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
536 case DHCP6_STATE_RENEW
:
537 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
538 max_retransmit_time
= DHCP6_REN_MAX_RT
;
540 /* RFC 3315, section 18.1.3. says max retransmit duration will
541 be the remaining time until T2. Instead of setting MRD,
542 wait for T2 to trigger with the same end result */
546 case DHCP6_STATE_REBIND
:
547 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
548 max_retransmit_time
= DHCP6_REB_MAX_RT
;
550 if (!client
->timeout_resend_expire
) {
551 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
554 client_stop(client
, r
);
557 max_retransmit_duration
= expire
* USEC_PER_SEC
;
562 case DHCP6_STATE_STOPPED
:
563 case DHCP6_STATE_BOUND
:
567 if (max_retransmit_count
&&
568 client
->retransmit_count
>= max_retransmit_count
) {
569 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
573 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
577 r
= client_send_message(client
, time_now
);
579 client
->retransmit_count
++;
581 if (!client
->retransmit_time
) {
582 client
->retransmit_time
=
583 client_timeout_compute_random(init_retransmit_time
);
585 if (client
->state
== DHCP6_STATE_SOLICITATION
)
586 client
->retransmit_time
+= init_retransmit_time
/ 10;
589 if (max_retransmit_time
&&
590 client
->retransmit_time
> max_retransmit_time
/ 2)
591 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
593 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
596 log_dhcp6_client(client
, "Next retransmission in %s",
597 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
599 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
600 clock_boottime_or_monotonic(),
601 time_now
+ client
->retransmit_time
,
602 10 * USEC_PER_MSEC
, client_timeout_resend
,
607 r
= sd_event_source_set_priority(client
->timeout_resend
,
608 client
->event_priority
);
612 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
616 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
618 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
619 max_retransmit_duration
/ USEC_PER_SEC
);
621 r
= sd_event_add_time(client
->event
,
622 &client
->timeout_resend_expire
,
623 clock_boottime_or_monotonic(),
624 time_now
+ max_retransmit_duration
,
626 client_timeout_resend_expire
, client
);
630 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
631 client
->event_priority
);
635 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
642 client_stop(client
, r
);
647 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
652 if (client
->ia_na
.id
)
655 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
662 static int client_parse_message(sd_dhcp6_client
*client
,
663 DHCP6Message
*message
, size_t len
,
664 sd_dhcp6_lease
*lease
) {
666 uint8_t *optval
, *option
, *id
= NULL
;
667 uint16_t optcode
, status
;
668 size_t optlen
, id_len
;
669 bool clientid
= false;
672 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
673 len
-= sizeof(DHCP6Message
);
675 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
678 case SD_DHCP6_OPTION_CLIENTID
:
680 log_dhcp6_client(client
, "%s contains multiple clientids",
681 dhcp6_message_type_to_string(message
->type
));
685 if (optlen
!= client
->duid_len
||
686 memcmp(&client
->duid
, optval
, optlen
) != 0) {
687 log_dhcp6_client(client
, "%s DUID does not match",
688 dhcp6_message_type_to_string(message
->type
));
696 case SD_DHCP6_OPTION_SERVERID
:
697 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
699 log_dhcp6_client(client
, "%s contains multiple serverids",
700 dhcp6_message_type_to_string(message
->type
));
704 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
710 case SD_DHCP6_OPTION_PREFERENCE
:
714 r
= dhcp6_lease_set_preference(lease
, *optval
);
720 case SD_DHCP6_OPTION_STATUS_CODE
:
724 status
= optval
[0] << 8 | optval
[1];
726 log_dhcp6_client(client
, "%s Status %s",
727 dhcp6_message_type_to_string(message
->type
),
728 dhcp6_message_status_to_string(status
));
734 case SD_DHCP6_OPTION_IA_NA
:
735 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
736 log_dhcp6_client(client
, "Information request ignoring IA NA option");
741 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
743 if (r
< 0 && r
!= -ENOMSG
)
746 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
750 if (client
->ia_na
.id
!= iaid_lease
) {
751 log_dhcp6_client(client
, "%s has wrong IAID",
752 dhcp6_message_type_to_string(message
->type
));
758 case SD_DHCP6_OPTION_RAPID_COMMIT
:
759 r
= dhcp6_lease_set_rapid_commit(lease
);
765 case SD_DHCP6_OPTION_DNS_SERVERS
:
766 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
772 case SD_DHCP6_OPTION_DOMAIN_LIST
:
773 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
779 case SD_DHCP6_OPTION_NTP_SERVER
:
780 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
786 case SD_DHCP6_OPTION_SNTP_SERVERS
:
787 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
799 if (r
< 0 || !clientid
) {
800 log_dhcp6_client(client
, "%s has incomplete options",
801 dhcp6_message_type_to_string(message
->type
));
805 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
806 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
808 log_dhcp6_client(client
, "%s has no server id",
809 dhcp6_message_type_to_string(message
->type
));
815 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
817 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
820 if (reply
->type
!= DHCP6_REPLY
)
823 r
= dhcp6_lease_new(&lease
);
827 r
= client_parse_message(client
, reply
, len
, lease
);
831 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
832 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
840 client_set_lease(client
, lease
);
843 return DHCP6_STATE_BOUND
;
846 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
848 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
849 uint8_t pref_advertise
= 0, pref_lease
= 0;
851 if (advertise
->type
!= DHCP6_ADVERTISE
)
854 r
= dhcp6_lease_new(&lease
);
858 r
= client_parse_message(client
, advertise
, len
, lease
);
862 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
866 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
868 if (r
< 0 || pref_advertise
> pref_lease
) {
869 client_set_lease(client
, lease
);
874 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
875 r
= DHCP6_STATE_REQUEST
;
880 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
881 sd_dhcp6_client
*client
= userdata
;
882 DHCP6_CLIENT_DONT_DESTROY(client
);
883 _cleanup_free_ DHCP6Message
*message
= NULL
;
889 assert(client
->event
);
891 buflen
= next_datagram_size_fd(fd
);
895 message
= malloc(buflen
);
899 len
= read(fd
, message
, buflen
);
901 if (errno
== EAGAIN
|| errno
== EINTR
)
904 log_dhcp6_client(client
, "Could not receive message from UDP socket: %m");
907 } else if ((size_t)len
< sizeof(DHCP6Message
))
910 switch(message
->type
) {
918 case DHCP6_INFORMATION_REQUEST
:
919 case DHCP6_RELAY_FORW
:
920 case DHCP6_RELAY_REPL
:
923 case DHCP6_ADVERTISE
:
925 case DHCP6_RECONFIGURE
:
929 log_dhcp6_client(client
, "unknown message type %d",
934 if (client
->transaction_id
!= (message
->transaction_id
&
935 htobe32(0x00ffffff)))
938 switch (client
->state
) {
939 case DHCP6_STATE_INFORMATION_REQUEST
:
940 r
= client_receive_reply(client
, message
, len
);
944 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
946 client_start(client
, DHCP6_STATE_STOPPED
);
950 case DHCP6_STATE_SOLICITATION
:
951 r
= client_receive_advertise(client
, message
, len
);
953 if (r
== DHCP6_STATE_REQUEST
) {
954 client_start(client
, r
);
959 /* fall through for Soliciation Rapid Commit option check */
960 case DHCP6_STATE_REQUEST
:
961 case DHCP6_STATE_RENEW
:
962 case DHCP6_STATE_REBIND
:
964 r
= client_receive_reply(client
, message
, len
);
968 if (r
== DHCP6_STATE_BOUND
) {
970 r
= client_start(client
, DHCP6_STATE_BOUND
);
972 client_stop(client
, r
);
976 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
981 case DHCP6_STATE_BOUND
:
985 case DHCP6_STATE_STOPPED
:
990 log_dhcp6_client(client
, "Recv %s",
991 dhcp6_message_type_to_string(message
->type
));
997 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
999 usec_t timeout
, time_now
;
1000 char time_string
[FORMAT_TIMESPAN_MAX
];
1002 assert_return(client
, -EINVAL
);
1003 assert_return(client
->event
, -EINVAL
);
1004 assert_return(client
->index
> 0, -EINVAL
);
1005 assert_return(client
->state
!= state
, -EINVAL
);
1007 client
->timeout_resend_expire
=
1008 sd_event_source_unref(client
->timeout_resend_expire
);
1009 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1010 client
->retransmit_time
= 0;
1011 client
->retransmit_count
= 0;
1013 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1018 case DHCP6_STATE_STOPPED
:
1019 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1020 client
->state
= DHCP6_STATE_STOPPED
;
1026 case DHCP6_STATE_SOLICITATION
:
1027 client
->state
= DHCP6_STATE_SOLICITATION
;
1031 case DHCP6_STATE_INFORMATION_REQUEST
:
1032 case DHCP6_STATE_REQUEST
:
1033 case DHCP6_STATE_RENEW
:
1034 case DHCP6_STATE_REBIND
:
1036 client
->state
= state
;
1040 case DHCP6_STATE_BOUND
:
1042 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1043 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1045 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1046 be32toh(client
->lease
->ia
.lifetime_t1
),
1047 be32toh(client
->lease
->ia
.lifetime_t2
));
1052 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1054 log_dhcp6_client(client
, "T1 expires in %s",
1055 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1057 r
= sd_event_add_time(client
->event
,
1058 &client
->lease
->ia
.timeout_t1
,
1059 clock_boottime_or_monotonic(), time_now
+ timeout
,
1060 10 * USEC_PER_SEC
, client_timeout_t1
,
1065 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1066 client
->event_priority
);
1070 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1074 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1076 log_dhcp6_client(client
, "T2 expires in %s",
1077 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1079 r
= sd_event_add_time(client
->event
,
1080 &client
->lease
->ia
.timeout_t2
,
1081 clock_boottime_or_monotonic(), time_now
+ timeout
,
1082 10 * USEC_PER_SEC
, client_timeout_t2
,
1087 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1088 client
->event_priority
);
1092 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1096 client
->state
= state
;
1101 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1102 client
->transaction_start
= time_now
;
1104 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1105 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1110 r
= sd_event_source_set_priority(client
->timeout_resend
,
1111 client
->event_priority
);
1115 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1122 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1123 assert_return(client
, -EINVAL
);
1125 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1130 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1131 assert_return(client
, -EINVAL
);
1133 return client
->state
!= DHCP6_STATE_STOPPED
;
1136 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1138 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1140 assert_return(client
, -EINVAL
);
1141 assert_return(client
->event
, -EINVAL
);
1142 assert_return(client
->index
> 0, -EINVAL
);
1143 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1145 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1148 r
= client_reset(client
);
1152 r
= client_ensure_iaid(client
);
1156 r
= client_ensure_duid(client
);
1160 r
= dhcp6_network_bind_udp_socket(client
->index
, &client
->local_address
);
1166 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1167 client
->fd
, EPOLLIN
, client_receive_message
,
1172 r
= sd_event_source_set_priority(client
->receive_message
,
1173 client
->event_priority
);
1177 r
= sd_event_source_set_description(client
->receive_message
,
1178 "dhcp6-receive-message");
1182 if (client
->information_request
)
1183 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1185 log_dhcp6_client(client
, "Started in %s mode",
1186 client
->information_request
? "Information request":
1189 return client_start(client
, state
);
1192 client_reset(client
);
1196 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1199 assert_return(client
, -EINVAL
);
1200 assert_return(!client
->event
, -EBUSY
);
1203 client
->event
= sd_event_ref(event
);
1205 r
= sd_event_default(&client
->event
);
1210 client
->event_priority
= priority
;
1215 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1216 assert_return(client
, -EINVAL
);
1218 client
->event
= sd_event_unref(client
->event
);
1223 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1227 return client
->event
;
1230 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1235 assert(client
->n_ref
>= 1);
1241 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1246 assert(client
->n_ref
>= 1);
1249 if (client
->n_ref
> 0)
1252 client_reset(client
);
1254 sd_dhcp6_client_detach_event(client
);
1256 free(client
->req_opts
);
1262 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1263 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1266 assert_return(ret
, -EINVAL
);
1268 client
= new0(sd_dhcp6_client
, 1);
1274 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1280 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1282 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1283 if (!client
->req_opts
)
1286 for (t
= 0; t
< client
->req_opts_len
; t
++)
1287 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);