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>
28 #include "udev-util.h"
30 #include "random-util.h"
32 #include "network-internal.h"
33 #include "sd-dhcp6-client.h"
34 #include "dhcp6-protocol.h"
35 #include "dhcp6-internal.h"
36 #include "dhcp6-lease-internal.h"
37 #include "dhcp-identifier.h"
39 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
41 struct sd_dhcp6_client
{
44 enum DHCP6State state
;
48 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
52 be32_t transaction_id
;
53 usec_t transaction_start
;
54 struct sd_dhcp6_lease
*lease
;
56 bool information_request
;
58 size_t req_opts_allocated
;
60 sd_event_source
*receive_message
;
61 usec_t retransmit_time
;
62 uint8_t retransmit_count
;
63 sd_event_source
*timeout_resend
;
64 sd_event_source
*timeout_resend_expire
;
65 sd_dhcp6_client_cb_t cb
;
71 static const uint16_t default_req_opts
[] = {
72 DHCP6_OPTION_DNS_SERVERS
,
73 DHCP6_OPTION_DOMAIN_LIST
,
74 DHCP6_OPTION_NTP_SERVER
,
75 DHCP6_OPTION_SNTP_SERVERS
,
78 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
79 [DHCP6_SOLICIT
] = "SOLICIT",
80 [DHCP6_ADVERTISE
] = "ADVERTISE",
81 [DHCP6_REQUEST
] = "REQUEST",
82 [DHCP6_CONFIRM
] = "CONFIRM",
83 [DHCP6_RENEW
] = "RENEW",
84 [DHCP6_REBIND
] = "REBIND",
85 [DHCP6_REPLY
] = "REPLY",
86 [DHCP6_RELEASE
] = "RELEASE",
87 [DHCP6_DECLINE
] = "DECLINE",
88 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
89 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
90 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
91 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
94 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
96 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
97 [DHCP6_STATUS_SUCCESS
] = "Success",
98 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
99 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
100 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
101 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
102 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
105 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
107 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client
*, sd_dhcp6_client_unref
);
108 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
110 #define DHCP6_CLIENT_DONT_DESTROY(client) \
111 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
113 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
115 int sd_dhcp6_client_set_callback(sd_dhcp6_client
*client
, sd_dhcp6_client_cb_t cb
, void *userdata
) {
116 assert_return(client
, -EINVAL
);
119 client
->userdata
= userdata
;
124 int sd_dhcp6_client_set_index(sd_dhcp6_client
*client
, int interface_index
) {
125 assert_return(client
, -EINVAL
);
126 assert_return(interface_index
>= -1, -EINVAL
);
128 client
->index
= interface_index
;
133 int sd_dhcp6_client_set_mac(
134 sd_dhcp6_client
*client
,
135 const uint8_t *addr
, size_t addr_len
,
138 assert_return(client
, -EINVAL
);
139 assert_return(addr
, -EINVAL
);
140 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
141 assert_return(arp_type
> 0, -EINVAL
);
143 if (arp_type
== ARPHRD_ETHER
)
144 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
145 else if (arp_type
== ARPHRD_INFINIBAND
)
146 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
150 if (client
->mac_addr_len
== addr_len
&&
151 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
154 memcpy(&client
->mac_addr
, addr
, addr_len
);
155 client
->mac_addr_len
= addr_len
;
156 client
->arp_type
= arp_type
;
161 static int client_ensure_duid(sd_dhcp6_client
*client
) {
162 if (client
->duid_len
!= 0)
165 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
168 int sd_dhcp6_client_set_duid(
169 sd_dhcp6_client
*client
,
171 uint8_t *duid
, size_t duid_len
) {
172 assert_return(client
, -EINVAL
);
173 assert_return(duid
, -EINVAL
);
174 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
178 if (duid_len
<= sizeof(client
->duid
.llt
))
182 if (duid_len
!= sizeof(client
->duid
.en
))
186 if (duid_len
<= sizeof(client
->duid
.ll
))
189 case DHCP6_DUID_UUID
:
190 if (duid_len
!= sizeof(client
->duid
.uuid
))
194 /* accept unknown type in order to be forward compatible */
198 client
->duid
.type
= htobe16(type
);
199 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
200 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
205 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, bool enabled
) {
206 assert_return(client
, -EINVAL
);
208 client
->information_request
= enabled
;
213 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, bool *enabled
) {
214 assert_return(client
, -EINVAL
);
215 assert_return(enabled
, -EINVAL
);
217 *enabled
= client
->information_request
;
222 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
225 assert_return(client
, -EINVAL
);
226 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
229 case DHCP6_OPTION_DNS_SERVERS
:
230 case DHCP6_OPTION_DOMAIN_LIST
:
231 case DHCP6_OPTION_SNTP_SERVERS
:
232 case DHCP6_OPTION_NTP_SERVER
:
239 for (t
= 0; t
< client
->req_opts_len
; t
++)
240 if (client
->req_opts
[t
] == htobe16(option
))
243 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
244 client
->req_opts_len
+ 1))
247 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
252 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
253 assert_return(client
, -EINVAL
);
254 assert_return(ret
, -EINVAL
);
259 *ret
= client
->lease
;
264 static void client_notify(sd_dhcp6_client
*client
, int event
) {
266 client
->cb(client
, event
, client
->userdata
);
269 static int client_reset(sd_dhcp6_client
*client
) {
270 assert_return(client
, -EINVAL
);
273 dhcp6_lease_clear_timers(&client
->lease
->ia
);
274 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
277 client
->receive_message
=
278 sd_event_source_unref(client
->receive_message
);
280 client
->fd
= safe_close(client
->fd
);
282 client
->transaction_id
= 0;
283 client
->transaction_start
= 0;
285 client
->ia_na
.timeout_t1
=
286 sd_event_source_unref(client
->ia_na
.timeout_t1
);
287 client
->ia_na
.timeout_t2
=
288 sd_event_source_unref(client
->ia_na
.timeout_t2
);
290 client
->retransmit_time
= 0;
291 client
->retransmit_count
= 0;
292 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
293 client
->timeout_resend_expire
=
294 sd_event_source_unref(client
->timeout_resend_expire
);
296 client
->state
= DHCP6_STATE_STOPPED
;
301 static void client_stop(sd_dhcp6_client
*client
, int error
) {
302 DHCP6_CLIENT_DONT_DESTROY(client
);
306 client_notify(client
, error
);
308 client_reset(client
);
311 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
312 _cleanup_free_ DHCP6Message
*message
= NULL
;
313 struct in6_addr all_servers
=
314 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
315 size_t len
, optlen
= 512;
321 len
= sizeof(DHCP6Message
) + optlen
;
323 message
= malloc0(len
);
327 opt
= (uint8_t *)(message
+ 1);
329 message
->transaction_id
= client
->transaction_id
;
331 switch(client
->state
) {
332 case DHCP6_STATE_INFORMATION_REQUEST
:
333 message
->type
= DHCP6_INFORMATION_REQUEST
;
337 case DHCP6_STATE_SOLICITATION
:
338 message
->type
= DHCP6_SOLICIT
;
340 r
= dhcp6_option_append(&opt
, &optlen
,
341 DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
345 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
351 case DHCP6_STATE_REQUEST
:
352 case DHCP6_STATE_RENEW
:
354 if (client
->state
== DHCP6_STATE_REQUEST
)
355 message
->type
= DHCP6_REQUEST
;
357 message
->type
= DHCP6_RENEW
;
359 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_SERVERID
,
360 client
->lease
->serverid_len
,
361 client
->lease
->serverid
);
365 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
371 case DHCP6_STATE_REBIND
:
372 message
->type
= DHCP6_REBIND
;
374 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
380 case DHCP6_STATE_STOPPED
:
381 case DHCP6_STATE_BOUND
:
385 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ORO
,
386 client
->req_opts_len
* sizeof(be16_t
),
391 assert (client
->duid_len
);
392 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_CLIENTID
,
393 client
->duid_len
, &client
->duid
);
397 elapsed_usec
= time_now
- client
->transaction_start
;
398 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
399 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
401 elapsed_time
= 0xffff;
403 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ELAPSED_TIME
,
404 sizeof(elapsed_time
), &elapsed_time
);
408 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
413 log_dhcp6_client(client
, "Sent %s",
414 dhcp6_message_type_to_string(message
->type
));
419 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
421 sd_dhcp6_client
*client
= userdata
;
423 assert_return(s
, -EINVAL
);
424 assert_return(client
, -EINVAL
);
425 assert_return(client
->lease
, -EINVAL
);
427 client
->lease
->ia
.timeout_t2
=
428 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
430 log_dhcp6_client(client
, "Timeout T2");
432 client_start(client
, DHCP6_STATE_REBIND
);
437 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
439 sd_dhcp6_client
*client
= userdata
;
441 assert_return(s
, -EINVAL
);
442 assert_return(client
, -EINVAL
);
443 assert_return(client
->lease
, -EINVAL
);
445 client
->lease
->ia
.timeout_t1
=
446 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
448 log_dhcp6_client(client
, "Timeout T1");
450 client_start(client
, DHCP6_STATE_RENEW
);
455 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
457 sd_dhcp6_client
*client
= userdata
;
458 DHCP6_CLIENT_DONT_DESTROY(client
);
459 enum DHCP6State state
;
463 assert(client
->event
);
465 state
= client
->state
;
467 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
469 /* RFC 3315, section 18.1.4., says that "...the client may choose to
470 use a Solicit message to locate a new DHCP server..." */
471 if (state
== DHCP6_STATE_REBIND
)
472 client_start(client
, DHCP6_STATE_SOLICITATION
);
477 static usec_t
client_timeout_compute_random(usec_t val
) {
478 return val
- val
/ 10 +
479 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
482 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
485 sd_dhcp6_client
*client
= userdata
;
486 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
487 usec_t max_retransmit_duration
= 0;
488 uint8_t max_retransmit_count
= 0;
489 char time_string
[FORMAT_TIMESPAN_MAX
];
494 assert(client
->event
);
496 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
498 switch (client
->state
) {
499 case DHCP6_STATE_INFORMATION_REQUEST
:
500 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
501 max_retransmit_time
= DHCP6_INF_MAX_RT
;
505 case DHCP6_STATE_SOLICITATION
:
507 if (client
->retransmit_count
&& client
->lease
) {
508 client_start(client
, DHCP6_STATE_REQUEST
);
512 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
513 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
517 case DHCP6_STATE_REQUEST
:
518 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
519 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
520 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
524 case DHCP6_STATE_RENEW
:
525 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
526 max_retransmit_time
= DHCP6_REN_MAX_RT
;
528 /* RFC 3315, section 18.1.3. says max retransmit duration will
529 be the remaining time until T2. Instead of setting MRD,
530 wait for T2 to trigger with the same end result */
534 case DHCP6_STATE_REBIND
:
535 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
536 max_retransmit_time
= DHCP6_REB_MAX_RT
;
538 if (!client
->timeout_resend_expire
) {
539 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
542 client_stop(client
, r
);
545 max_retransmit_duration
= expire
* USEC_PER_SEC
;
550 case DHCP6_STATE_STOPPED
:
551 case DHCP6_STATE_BOUND
:
555 if (max_retransmit_count
&&
556 client
->retransmit_count
>= max_retransmit_count
) {
557 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
561 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
565 r
= client_send_message(client
, time_now
);
567 client
->retransmit_count
++;
569 if (!client
->retransmit_time
) {
570 client
->retransmit_time
=
571 client_timeout_compute_random(init_retransmit_time
);
573 if (client
->state
== DHCP6_STATE_SOLICITATION
)
574 client
->retransmit_time
+= init_retransmit_time
/ 10;
577 if (max_retransmit_time
&&
578 client
->retransmit_time
> max_retransmit_time
/ 2)
579 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
581 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
584 log_dhcp6_client(client
, "Next retransmission in %s",
585 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
,
586 client
->retransmit_time
, 0));
588 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
589 clock_boottime_or_monotonic(),
590 time_now
+ client
->retransmit_time
,
591 10 * USEC_PER_MSEC
, client_timeout_resend
,
596 r
= sd_event_source_set_priority(client
->timeout_resend
,
597 client
->event_priority
);
601 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
605 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
607 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
608 max_retransmit_duration
/ USEC_PER_SEC
);
610 r
= sd_event_add_time(client
->event
,
611 &client
->timeout_resend_expire
,
612 clock_boottime_or_monotonic(),
613 time_now
+ max_retransmit_duration
,
615 client_timeout_resend_expire
, client
);
619 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
620 client
->event_priority
);
624 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
631 client_stop(client
, r
);
636 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
641 if (client
->ia_na
.id
)
644 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
651 static int client_parse_message(sd_dhcp6_client
*client
,
652 DHCP6Message
*message
, size_t len
,
653 sd_dhcp6_lease
*lease
) {
655 uint8_t *optval
, *option
, *id
= NULL
;
656 uint16_t optcode
, status
;
657 size_t optlen
, id_len
;
658 bool clientid
= false;
661 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
662 len
-= sizeof(DHCP6Message
);
664 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
667 case DHCP6_OPTION_CLIENTID
:
669 log_dhcp6_client(client
, "%s contains multiple clientids",
670 dhcp6_message_type_to_string(message
->type
));
674 if (optlen
!= client
->duid_len
||
675 memcmp(&client
->duid
, optval
, optlen
) != 0) {
676 log_dhcp6_client(client
, "%s DUID does not match",
677 dhcp6_message_type_to_string(message
->type
));
685 case DHCP6_OPTION_SERVERID
:
686 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
688 log_dhcp6_client(client
, "%s contains multiple serverids",
689 dhcp6_message_type_to_string(message
->type
));
693 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
699 case DHCP6_OPTION_PREFERENCE
:
703 r
= dhcp6_lease_set_preference(lease
, *optval
);
709 case DHCP6_OPTION_STATUS_CODE
:
713 status
= optval
[0] << 8 | optval
[1];
715 log_dhcp6_client(client
, "%s Status %s",
716 dhcp6_message_type_to_string(message
->type
),
717 dhcp6_message_status_to_string(status
));
723 case DHCP6_OPTION_IA_NA
:
724 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
725 log_dhcp6_client(client
, "Information request ignoring IA NA option");
730 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
732 if (r
< 0 && r
!= -ENOMSG
)
735 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
739 if (client
->ia_na
.id
!= iaid_lease
) {
740 log_dhcp6_client(client
, "%s has wrong IAID",
741 dhcp6_message_type_to_string(message
->type
));
747 case DHCP6_OPTION_RAPID_COMMIT
:
748 r
= dhcp6_lease_set_rapid_commit(lease
);
754 case DHCP6_OPTION_DNS_SERVERS
:
755 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
761 case DHCP6_OPTION_DOMAIN_LIST
:
762 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
768 case DHCP6_OPTION_NTP_SERVER
:
769 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
775 case DHCP6_OPTION_SNTP_SERVERS
:
776 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
788 if (r
< 0 || !clientid
) {
789 log_dhcp6_client(client
, "%s has incomplete options",
790 dhcp6_message_type_to_string(message
->type
));
794 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
795 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
797 log_dhcp6_client(client
, "%s has no server id",
798 dhcp6_message_type_to_string(message
->type
));
804 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
806 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
809 if (reply
->type
!= DHCP6_REPLY
)
812 r
= dhcp6_lease_new(&lease
);
816 r
= client_parse_message(client
, reply
, len
, lease
);
820 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
821 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
830 dhcp6_lease_clear_timers(&client
->lease
->ia
);
831 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
834 client
->lease
= lease
;
837 return DHCP6_STATE_BOUND
;
840 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
842 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
843 uint8_t pref_advertise
= 0, pref_lease
= 0;
845 if (advertise
->type
!= DHCP6_ADVERTISE
)
848 r
= dhcp6_lease_new(&lease
);
852 r
= client_parse_message(client
, advertise
, len
, lease
);
856 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
860 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
862 if (r
< 0 || pref_advertise
> pref_lease
) {
863 sd_dhcp6_lease_unref(client
->lease
);
864 client
->lease
= lease
;
869 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
870 r
= DHCP6_STATE_REQUEST
;
875 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
876 sd_dhcp6_client
*client
= userdata
;
877 DHCP6_CLIENT_DONT_DESTROY(client
);
878 _cleanup_free_ DHCP6Message
*message
;
883 assert(client
->event
);
885 r
= ioctl(fd
, FIONREAD
, &buflen
);
886 if (r
< 0 || buflen
<= 0)
887 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
889 message
= malloc0(buflen
);
893 len
= read(fd
, message
, buflen
);
894 if ((size_t)len
< sizeof(DHCP6Message
)) {
895 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
899 switch(message
->type
) {
907 case DHCP6_INFORMATION_REQUEST
:
908 case DHCP6_RELAY_FORW
:
909 case DHCP6_RELAY_REPL
:
912 case DHCP6_ADVERTISE
:
914 case DHCP6_RECONFIGURE
:
918 log_dhcp6_client(client
, "unknown message type %d",
923 if (client
->transaction_id
!= (message
->transaction_id
&
924 htobe32(0x00ffffff)))
927 switch (client
->state
) {
928 case DHCP6_STATE_INFORMATION_REQUEST
:
929 r
= client_receive_reply(client
, message
, len
);
933 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
935 client_start(client
, DHCP6_STATE_STOPPED
);
939 case DHCP6_STATE_SOLICITATION
:
940 r
= client_receive_advertise(client
, message
, len
);
942 if (r
== DHCP6_STATE_REQUEST
) {
943 client_start(client
, r
);
948 /* fall through for Soliciation Rapid Commit option check */
949 case DHCP6_STATE_REQUEST
:
950 case DHCP6_STATE_RENEW
:
951 case DHCP6_STATE_REBIND
:
953 r
= client_receive_reply(client
, message
, len
);
957 if (r
== DHCP6_STATE_BOUND
) {
959 r
= client_start(client
, DHCP6_STATE_BOUND
);
961 client_stop(client
, r
);
965 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
970 case DHCP6_STATE_BOUND
:
974 case DHCP6_STATE_STOPPED
:
979 log_dhcp6_client(client
, "Recv %s",
980 dhcp6_message_type_to_string(message
->type
));
986 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
988 usec_t timeout
, time_now
;
989 char time_string
[FORMAT_TIMESPAN_MAX
];
991 assert_return(client
, -EINVAL
);
992 assert_return(client
->event
, -EINVAL
);
993 assert_return(client
->index
> 0, -EINVAL
);
994 assert_return(client
->state
!= state
, -EINVAL
);
996 client
->timeout_resend_expire
=
997 sd_event_source_unref(client
->timeout_resend_expire
);
998 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
999 client
->retransmit_time
= 0;
1000 client
->retransmit_count
= 0;
1002 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1007 case DHCP6_STATE_STOPPED
:
1008 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1009 client
->state
= DHCP6_STATE_STOPPED
;
1015 case DHCP6_STATE_SOLICITATION
:
1016 client
->state
= DHCP6_STATE_SOLICITATION
;
1020 case DHCP6_STATE_INFORMATION_REQUEST
:
1021 case DHCP6_STATE_REQUEST
:
1022 case DHCP6_STATE_RENEW
:
1023 case DHCP6_STATE_REBIND
:
1025 client
->state
= state
;
1029 case DHCP6_STATE_BOUND
:
1031 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1032 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1034 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1035 be32toh(client
->lease
->ia
.lifetime_t1
),
1036 be32toh(client
->lease
->ia
.lifetime_t2
));
1041 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1043 log_dhcp6_client(client
, "T1 expires in %s",
1044 format_timespan(time_string
,
1045 FORMAT_TIMESPAN_MAX
,
1048 r
= sd_event_add_time(client
->event
,
1049 &client
->lease
->ia
.timeout_t1
,
1050 clock_boottime_or_monotonic(), time_now
+ timeout
,
1051 10 * USEC_PER_SEC
, client_timeout_t1
,
1056 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1057 client
->event_priority
);
1061 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1065 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1067 log_dhcp6_client(client
, "T2 expires in %s",
1068 format_timespan(time_string
,
1069 FORMAT_TIMESPAN_MAX
,
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 r
= client_reset(client
);
1133 r
= client_ensure_iaid(client
);
1137 r
= client_ensure_duid(client
);
1141 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1147 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1148 client
->fd
, EPOLLIN
, client_receive_message
,
1153 r
= sd_event_source_set_priority(client
->receive_message
,
1154 client
->event_priority
);
1158 r
= sd_event_source_set_description(client
->receive_message
,
1159 "dhcp6-receive-message");
1163 if (client
->information_request
)
1164 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1166 log_dhcp6_client(client
, "Started in %s mode",
1167 client
->information_request
? "Information request":
1170 return client_start(client
, state
);
1173 client_reset(client
);
1177 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int priority
) {
1180 assert_return(client
, -EINVAL
);
1181 assert_return(!client
->event
, -EBUSY
);
1184 client
->event
= sd_event_ref(event
);
1186 r
= sd_event_default(&client
->event
);
1191 client
->event_priority
= priority
;
1196 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1197 assert_return(client
, -EINVAL
);
1199 client
->event
= sd_event_unref(client
->event
);
1204 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1208 return client
->event
;
1211 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1216 assert(client
->n_ref
>= 1);
1222 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1227 assert(client
->n_ref
>= 1);
1230 if (client
->n_ref
> 0)
1233 client_reset(client
);
1235 sd_dhcp6_client_detach_event(client
);
1236 sd_dhcp6_lease_unref(client
->lease
);
1238 free(client
->req_opts
);
1244 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1245 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1248 assert_return(ret
, -EINVAL
);
1250 client
= new0(sd_dhcp6_client
, 1);
1256 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1262 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1264 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1265 if (!client
->req_opts
)
1268 for (t
= 0; t
< client
->req_opts_len
; t
++)
1269 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);