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 "dhcp-identifier.h"
30 #include "dhcp6-internal.h"
31 #include "dhcp6-lease-internal.h"
32 #include "dhcp6-protocol.h"
34 #include "network-internal.h"
35 #include "random-util.h"
38 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
40 struct sd_dhcp6_client
{
43 enum DHCP6State state
;
47 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
51 be32_t transaction_id
;
52 usec_t transaction_start
;
53 struct sd_dhcp6_lease
*lease
;
55 bool information_request
;
57 size_t req_opts_allocated
;
59 sd_event_source
*receive_message
;
60 usec_t retransmit_time
;
61 uint8_t retransmit_count
;
62 sd_event_source
*timeout_resend
;
63 sd_event_source
*timeout_resend_expire
;
64 sd_dhcp6_client_cb_t cb
;
70 static const uint16_t default_req_opts
[] = {
71 DHCP6_OPTION_DNS_SERVERS
,
72 DHCP6_OPTION_DOMAIN_LIST
,
73 DHCP6_OPTION_NTP_SERVER
,
74 DHCP6_OPTION_SNTP_SERVERS
,
77 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
78 [DHCP6_SOLICIT
] = "SOLICIT",
79 [DHCP6_ADVERTISE
] = "ADVERTISE",
80 [DHCP6_REQUEST
] = "REQUEST",
81 [DHCP6_CONFIRM
] = "CONFIRM",
82 [DHCP6_RENEW
] = "RENEW",
83 [DHCP6_REBIND
] = "REBIND",
84 [DHCP6_REPLY
] = "REPLY",
85 [DHCP6_RELEASE
] = "RELEASE",
86 [DHCP6_DECLINE
] = "DECLINE",
87 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
88 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
89 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
90 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
93 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
95 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
96 [DHCP6_STATUS_SUCCESS
] = "Success",
97 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
98 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
99 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
100 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
101 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
104 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
106 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client
*, sd_dhcp6_client_unref
);
107 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
109 #define DHCP6_CLIENT_DONT_DESTROY(client) \
110 _cleanup_dhcp6_client_unref_ _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_cb_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_mac(
135 sd_dhcp6_client
*client
,
136 const uint8_t *addr
, size_t addr_len
,
139 assert_return(client
, -EINVAL
);
140 assert_return(addr
, -EINVAL
);
141 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
142 assert_return(arp_type
> 0, -EINVAL
);
144 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
146 if (arp_type
== ARPHRD_ETHER
)
147 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
148 else if (arp_type
== ARPHRD_INFINIBAND
)
149 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
153 if (client
->mac_addr_len
== addr_len
&&
154 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
157 memcpy(&client
->mac_addr
, addr
, addr_len
);
158 client
->mac_addr_len
= addr_len
;
159 client
->arp_type
= arp_type
;
164 static int client_ensure_duid(sd_dhcp6_client
*client
) {
165 if (client
->duid_len
!= 0)
168 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
171 int sd_dhcp6_client_set_duid(
172 sd_dhcp6_client
*client
,
174 uint8_t *duid
, size_t duid_len
) {
175 assert_return(client
, -EINVAL
);
176 assert_return(duid
, -EINVAL
);
177 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
179 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
183 if (duid_len
<= sizeof(client
->duid
.llt
))
187 if (duid_len
!= sizeof(client
->duid
.en
))
191 if (duid_len
<= sizeof(client
->duid
.ll
))
194 case DHCP6_DUID_UUID
:
195 if (duid_len
!= sizeof(client
->duid
.uuid
))
199 /* accept unknown type in order to be forward compatible */
203 client
->duid
.type
= htobe16(type
);
204 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
205 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
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 DHCP6_OPTION_DNS_SERVERS
:
236 case DHCP6_OPTION_DOMAIN_LIST
:
237 case DHCP6_OPTION_SNTP_SERVERS
:
238 case 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
);
260 assert_return(ret
, -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 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
, 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
, DHCP6_OPTION_ORO
,
397 client
->req_opts_len
* sizeof(be16_t
),
402 assert (client
->duid_len
);
403 r
= dhcp6_option_append(&opt
, &optlen
, 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
, 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 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 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 DHCP6_OPTION_PREFERENCE
:
713 r
= dhcp6_lease_set_preference(lease
, *optval
);
719 case 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 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 DHCP6_OPTION_RAPID_COMMIT
:
758 r
= dhcp6_lease_set_rapid_commit(lease
);
764 case DHCP6_OPTION_DNS_SERVERS
:
765 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
771 case DHCP6_OPTION_DOMAIN_LIST
:
772 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
778 case DHCP6_OPTION_NTP_SERVER
:
779 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
785 case 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_dhcp6_lease_free_ 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_dhcp6_lease_free_ 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
;
887 assert(client
->event
);
889 r
= ioctl(fd
, FIONREAD
, &buflen
);
890 if (r
< 0 || buflen
<= 0)
891 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
893 message
= malloc0(buflen
);
897 len
= read(fd
, message
, buflen
);
898 if ((size_t)len
< sizeof(DHCP6Message
)) {
899 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
903 switch(message
->type
) {
911 case DHCP6_INFORMATION_REQUEST
:
912 case DHCP6_RELAY_FORW
:
913 case DHCP6_RELAY_REPL
:
916 case DHCP6_ADVERTISE
:
918 case DHCP6_RECONFIGURE
:
922 log_dhcp6_client(client
, "unknown message type %d",
927 if (client
->transaction_id
!= (message
->transaction_id
&
928 htobe32(0x00ffffff)))
931 switch (client
->state
) {
932 case DHCP6_STATE_INFORMATION_REQUEST
:
933 r
= client_receive_reply(client
, message
, len
);
937 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
939 client_start(client
, DHCP6_STATE_STOPPED
);
943 case DHCP6_STATE_SOLICITATION
:
944 r
= client_receive_advertise(client
, message
, len
);
946 if (r
== DHCP6_STATE_REQUEST
) {
947 client_start(client
, r
);
952 /* fall through for Soliciation Rapid Commit option check */
953 case DHCP6_STATE_REQUEST
:
954 case DHCP6_STATE_RENEW
:
955 case DHCP6_STATE_REBIND
:
957 r
= client_receive_reply(client
, message
, len
);
961 if (r
== DHCP6_STATE_BOUND
) {
963 r
= client_start(client
, DHCP6_STATE_BOUND
);
965 client_stop(client
, r
);
969 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
974 case DHCP6_STATE_BOUND
:
978 case DHCP6_STATE_STOPPED
:
983 log_dhcp6_client(client
, "Recv %s",
984 dhcp6_message_type_to_string(message
->type
));
990 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
992 usec_t timeout
, time_now
;
993 char time_string
[FORMAT_TIMESPAN_MAX
];
995 assert_return(client
, -EINVAL
);
996 assert_return(client
->event
, -EINVAL
);
997 assert_return(client
->index
> 0, -EINVAL
);
998 assert_return(client
->state
!= state
, -EINVAL
);
1000 client
->timeout_resend_expire
=
1001 sd_event_source_unref(client
->timeout_resend_expire
);
1002 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1003 client
->retransmit_time
= 0;
1004 client
->retransmit_count
= 0;
1006 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1011 case DHCP6_STATE_STOPPED
:
1012 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1013 client
->state
= DHCP6_STATE_STOPPED
;
1019 case DHCP6_STATE_SOLICITATION
:
1020 client
->state
= DHCP6_STATE_SOLICITATION
;
1024 case DHCP6_STATE_INFORMATION_REQUEST
:
1025 case DHCP6_STATE_REQUEST
:
1026 case DHCP6_STATE_RENEW
:
1027 case DHCP6_STATE_REBIND
:
1029 client
->state
= state
;
1033 case DHCP6_STATE_BOUND
:
1035 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1036 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1038 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1039 be32toh(client
->lease
->ia
.lifetime_t1
),
1040 be32toh(client
->lease
->ia
.lifetime_t2
));
1045 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1047 log_dhcp6_client(client
, "T1 expires in %s",
1048 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1050 r
= sd_event_add_time(client
->event
,
1051 &client
->lease
->ia
.timeout_t1
,
1052 clock_boottime_or_monotonic(), time_now
+ timeout
,
1053 10 * USEC_PER_SEC
, client_timeout_t1
,
1058 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1059 client
->event_priority
);
1063 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1067 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1069 log_dhcp6_client(client
, "T2 expires in %s",
1070 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1072 r
= sd_event_add_time(client
->event
,
1073 &client
->lease
->ia
.timeout_t2
,
1074 clock_boottime_or_monotonic(), time_now
+ timeout
,
1075 10 * USEC_PER_SEC
, client_timeout_t2
,
1080 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1081 client
->event_priority
);
1085 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1089 client
->state
= state
;
1094 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1095 client
->transaction_start
= time_now
;
1097 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1098 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1103 r
= sd_event_source_set_priority(client
->timeout_resend
,
1104 client
->event_priority
);
1108 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1115 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1116 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1121 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1123 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1125 assert_return(client
, -EINVAL
);
1126 assert_return(client
->event
, -EINVAL
);
1127 assert_return(client
->index
> 0, -EINVAL
);
1129 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1132 r
= client_reset(client
);
1136 r
= client_ensure_iaid(client
);
1140 r
= client_ensure_duid(client
);
1144 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1150 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1151 client
->fd
, EPOLLIN
, client_receive_message
,
1156 r
= sd_event_source_set_priority(client
->receive_message
,
1157 client
->event_priority
);
1161 r
= sd_event_source_set_description(client
->receive_message
,
1162 "dhcp6-receive-message");
1166 if (client
->information_request
)
1167 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1169 log_dhcp6_client(client
, "Started in %s mode",
1170 client
->information_request
? "Information request":
1173 return client_start(client
, state
);
1176 client_reset(client
);
1180 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int priority
) {
1183 assert_return(client
, -EINVAL
);
1184 assert_return(!client
->event
, -EBUSY
);
1187 client
->event
= sd_event_ref(event
);
1189 r
= sd_event_default(&client
->event
);
1194 client
->event_priority
= priority
;
1199 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1200 assert_return(client
, -EINVAL
);
1202 client
->event
= sd_event_unref(client
->event
);
1207 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1211 return client
->event
;
1214 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1219 assert(client
->n_ref
>= 1);
1225 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1230 assert(client
->n_ref
>= 1);
1233 if (client
->n_ref
> 0)
1236 client_reset(client
);
1238 sd_dhcp6_client_detach_event(client
);
1240 free(client
->req_opts
);
1246 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1247 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1250 assert_return(ret
, -EINVAL
);
1252 client
= new0(sd_dhcp6_client
, 1);
1258 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1264 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1266 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1267 if (!client
->req_opts
)
1270 for (t
= 0; t
< client
->req_opts_len
; t
++)
1271 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);