1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/ioctl.h>
25 #include <linux/if_infiniband.h>
27 #include "sd-dhcp6-client.h"
29 #include "alloc-util.h"
30 #include "dhcp-identifier.h"
31 #include "dhcp6-internal.h"
32 #include "dhcp6-lease-internal.h"
33 #include "dhcp6-protocol.h"
35 #include "network-internal.h"
36 #include "random-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 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
53 be32_t transaction_id
;
54 usec_t transaction_start
;
55 struct sd_dhcp6_lease
*lease
;
57 bool information_request
;
59 size_t req_opts_allocated
;
61 sd_event_source
*receive_message
;
62 usec_t retransmit_time
;
63 uint8_t retransmit_count
;
64 sd_event_source
*timeout_resend
;
65 sd_event_source
*timeout_resend_expire
;
66 sd_dhcp6_client_cb_t cb
;
72 static const uint16_t default_req_opts
[] = {
73 DHCP6_OPTION_DNS_SERVERS
,
74 DHCP6_OPTION_DOMAIN_LIST
,
75 DHCP6_OPTION_NTP_SERVER
,
76 DHCP6_OPTION_SNTP_SERVERS
,
79 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
80 [DHCP6_SOLICIT
] = "SOLICIT",
81 [DHCP6_ADVERTISE
] = "ADVERTISE",
82 [DHCP6_REQUEST
] = "REQUEST",
83 [DHCP6_CONFIRM
] = "CONFIRM",
84 [DHCP6_RENEW
] = "RENEW",
85 [DHCP6_REBIND
] = "REBIND",
86 [DHCP6_REPLY
] = "REPLY",
87 [DHCP6_RELEASE
] = "RELEASE",
88 [DHCP6_DECLINE
] = "DECLINE",
89 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
90 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
91 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
92 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
95 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
97 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
98 [DHCP6_STATUS_SUCCESS
] = "Success",
99 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
100 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
101 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
102 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
103 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
106 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
108 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client
*, sd_dhcp6_client_unref
);
109 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
111 #define DHCP6_CLIENT_DONT_DESTROY(client) \
112 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
114 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
116 int sd_dhcp6_client_set_callback(sd_dhcp6_client
*client
, sd_dhcp6_client_cb_t cb
, void *userdata
) {
117 assert_return(client
, -EINVAL
);
120 client
->userdata
= userdata
;
125 int sd_dhcp6_client_set_index(sd_dhcp6_client
*client
, int interface_index
) {
126 assert_return(client
, -EINVAL
);
127 assert_return(interface_index
>= -1, -EINVAL
);
129 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
131 client
->index
= interface_index
;
136 int sd_dhcp6_client_set_mac(
137 sd_dhcp6_client
*client
,
138 const uint8_t *addr
, size_t addr_len
,
141 assert_return(client
, -EINVAL
);
142 assert_return(addr
, -EINVAL
);
143 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
144 assert_return(arp_type
> 0, -EINVAL
);
146 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
148 if (arp_type
== ARPHRD_ETHER
)
149 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
150 else if (arp_type
== ARPHRD_INFINIBAND
)
151 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
155 if (client
->mac_addr_len
== addr_len
&&
156 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
159 memcpy(&client
->mac_addr
, addr
, addr_len
);
160 client
->mac_addr_len
= addr_len
;
161 client
->arp_type
= arp_type
;
166 static int client_ensure_duid(sd_dhcp6_client
*client
) {
167 if (client
->duid_len
!= 0)
170 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
173 int sd_dhcp6_client_set_duid(
174 sd_dhcp6_client
*client
,
176 uint8_t *duid
, size_t duid_len
) {
177 assert_return(client
, -EINVAL
);
178 assert_return(duid
, -EINVAL
);
179 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
181 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
185 if (duid_len
<= sizeof(client
->duid
.llt
))
189 if (duid_len
!= sizeof(client
->duid
.en
))
193 if (duid_len
<= sizeof(client
->duid
.ll
))
196 case DHCP6_DUID_UUID
:
197 if (duid_len
!= sizeof(client
->duid
.uuid
))
201 /* accept unknown type in order to be forward compatible */
205 client
->duid
.type
= htobe16(type
);
206 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
207 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
212 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
213 assert_return(client
, -EINVAL
);
214 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
216 client
->information_request
= enabled
;
221 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
222 assert_return(client
, -EINVAL
);
223 assert_return(enabled
, -EINVAL
);
225 *enabled
= client
->information_request
;
230 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
233 assert_return(client
, -EINVAL
);
234 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
237 case DHCP6_OPTION_DNS_SERVERS
:
238 case DHCP6_OPTION_DOMAIN_LIST
:
239 case DHCP6_OPTION_SNTP_SERVERS
:
240 case DHCP6_OPTION_NTP_SERVER
:
247 for (t
= 0; t
< client
->req_opts_len
; t
++)
248 if (client
->req_opts
[t
] == htobe16(option
))
251 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
252 client
->req_opts_len
+ 1))
255 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
260 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
261 assert_return(client
, -EINVAL
);
267 *ret
= client
->lease
;
272 static void client_notify(sd_dhcp6_client
*client
, int event
) {
274 client
->cb(client
, event
, client
->userdata
);
277 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
279 dhcp6_lease_clear_timers(&client
->lease
->ia
);
280 sd_dhcp6_lease_unref(client
->lease
);
282 client
->lease
= lease
;
285 static int client_reset(sd_dhcp6_client
*client
) {
286 assert_return(client
, -EINVAL
);
288 client_set_lease(client
, NULL
);
290 client
->receive_message
=
291 sd_event_source_unref(client
->receive_message
);
293 client
->fd
= safe_close(client
->fd
);
295 client
->transaction_id
= 0;
296 client
->transaction_start
= 0;
298 client
->ia_na
.timeout_t1
=
299 sd_event_source_unref(client
->ia_na
.timeout_t1
);
300 client
->ia_na
.timeout_t2
=
301 sd_event_source_unref(client
->ia_na
.timeout_t2
);
303 client
->retransmit_time
= 0;
304 client
->retransmit_count
= 0;
305 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
306 client
->timeout_resend_expire
=
307 sd_event_source_unref(client
->timeout_resend_expire
);
309 client
->state
= DHCP6_STATE_STOPPED
;
314 static void client_stop(sd_dhcp6_client
*client
, int error
) {
315 DHCP6_CLIENT_DONT_DESTROY(client
);
319 client_notify(client
, error
);
321 client_reset(client
);
324 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
325 _cleanup_free_ DHCP6Message
*message
= NULL
;
326 struct in6_addr all_servers
=
327 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
328 size_t len
, optlen
= 512;
334 len
= sizeof(DHCP6Message
) + optlen
;
336 message
= malloc0(len
);
340 opt
= (uint8_t *)(message
+ 1);
342 message
->transaction_id
= client
->transaction_id
;
344 switch(client
->state
) {
345 case DHCP6_STATE_INFORMATION_REQUEST
:
346 message
->type
= DHCP6_INFORMATION_REQUEST
;
350 case DHCP6_STATE_SOLICITATION
:
351 message
->type
= DHCP6_SOLICIT
;
353 r
= dhcp6_option_append(&opt
, &optlen
,
354 DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
358 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
364 case DHCP6_STATE_REQUEST
:
365 case DHCP6_STATE_RENEW
:
367 if (client
->state
== DHCP6_STATE_REQUEST
)
368 message
->type
= DHCP6_REQUEST
;
370 message
->type
= DHCP6_RENEW
;
372 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_SERVERID
,
373 client
->lease
->serverid_len
,
374 client
->lease
->serverid
);
378 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
384 case DHCP6_STATE_REBIND
:
385 message
->type
= DHCP6_REBIND
;
387 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
393 case DHCP6_STATE_STOPPED
:
394 case DHCP6_STATE_BOUND
:
398 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ORO
,
399 client
->req_opts_len
* sizeof(be16_t
),
404 assert (client
->duid_len
);
405 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_CLIENTID
,
406 client
->duid_len
, &client
->duid
);
410 elapsed_usec
= time_now
- client
->transaction_start
;
411 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
412 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
414 elapsed_time
= 0xffff;
416 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ELAPSED_TIME
,
417 sizeof(elapsed_time
), &elapsed_time
);
421 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
426 log_dhcp6_client(client
, "Sent %s",
427 dhcp6_message_type_to_string(message
->type
));
432 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
434 sd_dhcp6_client
*client
= userdata
;
436 assert_return(s
, -EINVAL
);
437 assert_return(client
, -EINVAL
);
438 assert_return(client
->lease
, -EINVAL
);
440 client
->lease
->ia
.timeout_t2
=
441 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
443 log_dhcp6_client(client
, "Timeout T2");
445 client_start(client
, DHCP6_STATE_REBIND
);
450 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
452 sd_dhcp6_client
*client
= userdata
;
454 assert_return(s
, -EINVAL
);
455 assert_return(client
, -EINVAL
);
456 assert_return(client
->lease
, -EINVAL
);
458 client
->lease
->ia
.timeout_t1
=
459 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
461 log_dhcp6_client(client
, "Timeout T1");
463 client_start(client
, DHCP6_STATE_RENEW
);
468 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
470 sd_dhcp6_client
*client
= userdata
;
471 DHCP6_CLIENT_DONT_DESTROY(client
);
472 enum DHCP6State state
;
476 assert(client
->event
);
478 state
= client
->state
;
480 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
482 /* RFC 3315, section 18.1.4., says that "...the client may choose to
483 use a Solicit message to locate a new DHCP server..." */
484 if (state
== DHCP6_STATE_REBIND
)
485 client_start(client
, DHCP6_STATE_SOLICITATION
);
490 static usec_t
client_timeout_compute_random(usec_t val
) {
491 return val
- val
/ 10 +
492 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
495 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
498 sd_dhcp6_client
*client
= userdata
;
499 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
500 usec_t max_retransmit_duration
= 0;
501 uint8_t max_retransmit_count
= 0;
502 char time_string
[FORMAT_TIMESPAN_MAX
];
507 assert(client
->event
);
509 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
511 switch (client
->state
) {
512 case DHCP6_STATE_INFORMATION_REQUEST
:
513 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
514 max_retransmit_time
= DHCP6_INF_MAX_RT
;
518 case DHCP6_STATE_SOLICITATION
:
520 if (client
->retransmit_count
&& client
->lease
) {
521 client_start(client
, DHCP6_STATE_REQUEST
);
525 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
526 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
530 case DHCP6_STATE_REQUEST
:
531 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
532 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
533 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
537 case DHCP6_STATE_RENEW
:
538 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
539 max_retransmit_time
= DHCP6_REN_MAX_RT
;
541 /* RFC 3315, section 18.1.3. says max retransmit duration will
542 be the remaining time until T2. Instead of setting MRD,
543 wait for T2 to trigger with the same end result */
547 case DHCP6_STATE_REBIND
:
548 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
549 max_retransmit_time
= DHCP6_REB_MAX_RT
;
551 if (!client
->timeout_resend_expire
) {
552 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
555 client_stop(client
, r
);
558 max_retransmit_duration
= expire
* USEC_PER_SEC
;
563 case DHCP6_STATE_STOPPED
:
564 case DHCP6_STATE_BOUND
:
568 if (max_retransmit_count
&&
569 client
->retransmit_count
>= max_retransmit_count
) {
570 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
574 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
578 r
= client_send_message(client
, time_now
);
580 client
->retransmit_count
++;
582 if (!client
->retransmit_time
) {
583 client
->retransmit_time
=
584 client_timeout_compute_random(init_retransmit_time
);
586 if (client
->state
== DHCP6_STATE_SOLICITATION
)
587 client
->retransmit_time
+= init_retransmit_time
/ 10;
590 if (max_retransmit_time
&&
591 client
->retransmit_time
> max_retransmit_time
/ 2)
592 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
594 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
597 log_dhcp6_client(client
, "Next retransmission in %s",
598 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
600 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
601 clock_boottime_or_monotonic(),
602 time_now
+ client
->retransmit_time
,
603 10 * USEC_PER_MSEC
, client_timeout_resend
,
608 r
= sd_event_source_set_priority(client
->timeout_resend
,
609 client
->event_priority
);
613 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
617 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
619 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
620 max_retransmit_duration
/ USEC_PER_SEC
);
622 r
= sd_event_add_time(client
->event
,
623 &client
->timeout_resend_expire
,
624 clock_boottime_or_monotonic(),
625 time_now
+ max_retransmit_duration
,
627 client_timeout_resend_expire
, client
);
631 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
632 client
->event_priority
);
636 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
643 client_stop(client
, r
);
648 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
653 if (client
->ia_na
.id
)
656 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
663 static int client_parse_message(sd_dhcp6_client
*client
,
664 DHCP6Message
*message
, size_t len
,
665 sd_dhcp6_lease
*lease
) {
667 uint8_t *optval
, *option
, *id
= NULL
;
668 uint16_t optcode
, status
;
669 size_t optlen
, id_len
;
670 bool clientid
= false;
673 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
674 len
-= sizeof(DHCP6Message
);
676 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
679 case DHCP6_OPTION_CLIENTID
:
681 log_dhcp6_client(client
, "%s contains multiple clientids",
682 dhcp6_message_type_to_string(message
->type
));
686 if (optlen
!= client
->duid_len
||
687 memcmp(&client
->duid
, optval
, optlen
) != 0) {
688 log_dhcp6_client(client
, "%s DUID does not match",
689 dhcp6_message_type_to_string(message
->type
));
697 case DHCP6_OPTION_SERVERID
:
698 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
700 log_dhcp6_client(client
, "%s contains multiple serverids",
701 dhcp6_message_type_to_string(message
->type
));
705 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
711 case DHCP6_OPTION_PREFERENCE
:
715 r
= dhcp6_lease_set_preference(lease
, *optval
);
721 case DHCP6_OPTION_STATUS_CODE
:
725 status
= optval
[0] << 8 | optval
[1];
727 log_dhcp6_client(client
, "%s Status %s",
728 dhcp6_message_type_to_string(message
->type
),
729 dhcp6_message_status_to_string(status
));
735 case DHCP6_OPTION_IA_NA
:
736 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
737 log_dhcp6_client(client
, "Information request ignoring IA NA option");
742 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
744 if (r
< 0 && r
!= -ENOMSG
)
747 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
751 if (client
->ia_na
.id
!= iaid_lease
) {
752 log_dhcp6_client(client
, "%s has wrong IAID",
753 dhcp6_message_type_to_string(message
->type
));
759 case DHCP6_OPTION_RAPID_COMMIT
:
760 r
= dhcp6_lease_set_rapid_commit(lease
);
766 case DHCP6_OPTION_DNS_SERVERS
:
767 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
773 case DHCP6_OPTION_DOMAIN_LIST
:
774 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
780 case DHCP6_OPTION_NTP_SERVER
:
781 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
787 case DHCP6_OPTION_SNTP_SERVERS
:
788 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
800 if (r
< 0 || !clientid
) {
801 log_dhcp6_client(client
, "%s has incomplete options",
802 dhcp6_message_type_to_string(message
->type
));
806 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
807 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
809 log_dhcp6_client(client
, "%s has no server id",
810 dhcp6_message_type_to_string(message
->type
));
816 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
818 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
821 if (reply
->type
!= DHCP6_REPLY
)
824 r
= dhcp6_lease_new(&lease
);
828 r
= client_parse_message(client
, reply
, len
, lease
);
832 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
833 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
841 client_set_lease(client
, lease
);
844 return DHCP6_STATE_BOUND
;
847 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
849 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
850 uint8_t pref_advertise
= 0, pref_lease
= 0;
852 if (advertise
->type
!= DHCP6_ADVERTISE
)
855 r
= dhcp6_lease_new(&lease
);
859 r
= client_parse_message(client
, advertise
, len
, lease
);
863 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
867 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
869 if (r
< 0 || pref_advertise
> pref_lease
) {
870 client_set_lease(client
, lease
);
875 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
876 r
= DHCP6_STATE_REQUEST
;
881 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
882 sd_dhcp6_client
*client
= userdata
;
883 DHCP6_CLIENT_DONT_DESTROY(client
);
884 _cleanup_free_ DHCP6Message
*message
;
889 assert(client
->event
);
891 r
= ioctl(fd
, FIONREAD
, &buflen
);
892 if (r
< 0 || buflen
<= 0)
893 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
895 message
= malloc0(buflen
);
899 len
= read(fd
, message
, buflen
);
900 if ((size_t)len
< sizeof(DHCP6Message
)) {
901 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
905 switch(message
->type
) {
913 case DHCP6_INFORMATION_REQUEST
:
914 case DHCP6_RELAY_FORW
:
915 case DHCP6_RELAY_REPL
:
918 case DHCP6_ADVERTISE
:
920 case DHCP6_RECONFIGURE
:
924 log_dhcp6_client(client
, "unknown message type %d",
929 if (client
->transaction_id
!= (message
->transaction_id
&
930 htobe32(0x00ffffff)))
933 switch (client
->state
) {
934 case DHCP6_STATE_INFORMATION_REQUEST
:
935 r
= client_receive_reply(client
, message
, len
);
939 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
941 client_start(client
, DHCP6_STATE_STOPPED
);
945 case DHCP6_STATE_SOLICITATION
:
946 r
= client_receive_advertise(client
, message
, len
);
948 if (r
== DHCP6_STATE_REQUEST
) {
949 client_start(client
, r
);
954 /* fall through for Soliciation Rapid Commit option check */
955 case DHCP6_STATE_REQUEST
:
956 case DHCP6_STATE_RENEW
:
957 case DHCP6_STATE_REBIND
:
959 r
= client_receive_reply(client
, message
, len
);
963 if (r
== DHCP6_STATE_BOUND
) {
965 r
= client_start(client
, DHCP6_STATE_BOUND
);
967 client_stop(client
, r
);
971 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
976 case DHCP6_STATE_BOUND
:
980 case DHCP6_STATE_STOPPED
:
985 log_dhcp6_client(client
, "Recv %s",
986 dhcp6_message_type_to_string(message
->type
));
992 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
994 usec_t timeout
, time_now
;
995 char time_string
[FORMAT_TIMESPAN_MAX
];
997 assert_return(client
, -EINVAL
);
998 assert_return(client
->event
, -EINVAL
);
999 assert_return(client
->index
> 0, -EINVAL
);
1000 assert_return(client
->state
!= state
, -EINVAL
);
1002 client
->timeout_resend_expire
=
1003 sd_event_source_unref(client
->timeout_resend_expire
);
1004 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1005 client
->retransmit_time
= 0;
1006 client
->retransmit_count
= 0;
1008 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1013 case DHCP6_STATE_STOPPED
:
1014 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1015 client
->state
= DHCP6_STATE_STOPPED
;
1021 case DHCP6_STATE_SOLICITATION
:
1022 client
->state
= DHCP6_STATE_SOLICITATION
;
1026 case DHCP6_STATE_INFORMATION_REQUEST
:
1027 case DHCP6_STATE_REQUEST
:
1028 case DHCP6_STATE_RENEW
:
1029 case DHCP6_STATE_REBIND
:
1031 client
->state
= state
;
1035 case DHCP6_STATE_BOUND
:
1037 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1038 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1040 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1041 be32toh(client
->lease
->ia
.lifetime_t1
),
1042 be32toh(client
->lease
->ia
.lifetime_t2
));
1047 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1049 log_dhcp6_client(client
, "T1 expires in %s",
1050 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1052 r
= sd_event_add_time(client
->event
,
1053 &client
->lease
->ia
.timeout_t1
,
1054 clock_boottime_or_monotonic(), time_now
+ timeout
,
1055 10 * USEC_PER_SEC
, client_timeout_t1
,
1060 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1061 client
->event_priority
);
1065 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1069 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1071 log_dhcp6_client(client
, "T2 expires in %s",
1072 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1074 r
= sd_event_add_time(client
->event
,
1075 &client
->lease
->ia
.timeout_t2
,
1076 clock_boottime_or_monotonic(), time_now
+ timeout
,
1077 10 * USEC_PER_SEC
, client_timeout_t2
,
1082 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1083 client
->event_priority
);
1087 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1091 client
->state
= state
;
1096 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1097 client
->transaction_start
= time_now
;
1099 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1100 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1105 r
= sd_event_source_set_priority(client
->timeout_resend
,
1106 client
->event_priority
);
1110 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1117 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1118 assert_return(client
, -EINVAL
);
1120 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1125 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1126 assert_return(client
, -EINVAL
);
1128 return client
->state
!= DHCP6_STATE_STOPPED
;
1131 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1133 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1135 assert_return(client
, -EINVAL
);
1136 assert_return(client
->event
, -EINVAL
);
1137 assert_return(client
->index
> 0, -EINVAL
);
1139 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1142 r
= client_reset(client
);
1146 r
= client_ensure_iaid(client
);
1150 r
= client_ensure_duid(client
);
1154 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1160 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1161 client
->fd
, EPOLLIN
, client_receive_message
,
1166 r
= sd_event_source_set_priority(client
->receive_message
,
1167 client
->event_priority
);
1171 r
= sd_event_source_set_description(client
->receive_message
,
1172 "dhcp6-receive-message");
1176 if (client
->information_request
)
1177 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1179 log_dhcp6_client(client
, "Started in %s mode",
1180 client
->information_request
? "Information request":
1183 return client_start(client
, state
);
1186 client_reset(client
);
1190 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int priority
) {
1193 assert_return(client
, -EINVAL
);
1194 assert_return(!client
->event
, -EBUSY
);
1197 client
->event
= sd_event_ref(event
);
1199 r
= sd_event_default(&client
->event
);
1204 client
->event_priority
= priority
;
1209 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1210 assert_return(client
, -EINVAL
);
1212 client
->event
= sd_event_unref(client
->event
);
1217 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1221 return client
->event
;
1224 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1229 assert(client
->n_ref
>= 1);
1235 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1240 assert(client
->n_ref
>= 1);
1243 if (client
->n_ref
> 0)
1246 client_reset(client
);
1248 sd_dhcp6_client_detach_event(client
);
1250 free(client
->req_opts
);
1256 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1257 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1260 assert_return(ret
, -EINVAL
);
1262 client
= new0(sd_dhcp6_client
, 1);
1268 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1274 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1276 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1277 if (!client
->req_opts
)
1280 for (t
= 0; t
< client
->req_opts_len
; t
++)
1281 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);