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 void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
271 dhcp6_lease_clear_timers(&client
->lease
->ia
);
272 sd_dhcp6_lease_unref(client
->lease
);
274 client
->lease
= lease
;
277 static int client_reset(sd_dhcp6_client
*client
) {
278 assert_return(client
, -EINVAL
);
280 client_set_lease(client
, NULL
);
282 client
->receive_message
=
283 sd_event_source_unref(client
->receive_message
);
285 client
->fd
= safe_close(client
->fd
);
287 client
->transaction_id
= 0;
288 client
->transaction_start
= 0;
290 client
->ia_na
.timeout_t1
=
291 sd_event_source_unref(client
->ia_na
.timeout_t1
);
292 client
->ia_na
.timeout_t2
=
293 sd_event_source_unref(client
->ia_na
.timeout_t2
);
295 client
->retransmit_time
= 0;
296 client
->retransmit_count
= 0;
297 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
298 client
->timeout_resend_expire
=
299 sd_event_source_unref(client
->timeout_resend_expire
);
301 client
->state
= DHCP6_STATE_STOPPED
;
306 static void client_stop(sd_dhcp6_client
*client
, int error
) {
307 DHCP6_CLIENT_DONT_DESTROY(client
);
311 client_notify(client
, error
);
313 client_reset(client
);
316 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
317 _cleanup_free_ DHCP6Message
*message
= NULL
;
318 struct in6_addr all_servers
=
319 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
320 size_t len
, optlen
= 512;
326 len
= sizeof(DHCP6Message
) + optlen
;
328 message
= malloc0(len
);
332 opt
= (uint8_t *)(message
+ 1);
334 message
->transaction_id
= client
->transaction_id
;
336 switch(client
->state
) {
337 case DHCP6_STATE_INFORMATION_REQUEST
:
338 message
->type
= DHCP6_INFORMATION_REQUEST
;
342 case DHCP6_STATE_SOLICITATION
:
343 message
->type
= DHCP6_SOLICIT
;
345 r
= dhcp6_option_append(&opt
, &optlen
,
346 DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
350 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
356 case DHCP6_STATE_REQUEST
:
357 case DHCP6_STATE_RENEW
:
359 if (client
->state
== DHCP6_STATE_REQUEST
)
360 message
->type
= DHCP6_REQUEST
;
362 message
->type
= DHCP6_RENEW
;
364 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_SERVERID
,
365 client
->lease
->serverid_len
,
366 client
->lease
->serverid
);
370 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
376 case DHCP6_STATE_REBIND
:
377 message
->type
= DHCP6_REBIND
;
379 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
385 case DHCP6_STATE_STOPPED
:
386 case DHCP6_STATE_BOUND
:
390 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ORO
,
391 client
->req_opts_len
* sizeof(be16_t
),
396 assert (client
->duid_len
);
397 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_CLIENTID
,
398 client
->duid_len
, &client
->duid
);
402 elapsed_usec
= time_now
- client
->transaction_start
;
403 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
404 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
406 elapsed_time
= 0xffff;
408 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ELAPSED_TIME
,
409 sizeof(elapsed_time
), &elapsed_time
);
413 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
418 log_dhcp6_client(client
, "Sent %s",
419 dhcp6_message_type_to_string(message
->type
));
424 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
426 sd_dhcp6_client
*client
= userdata
;
428 assert_return(s
, -EINVAL
);
429 assert_return(client
, -EINVAL
);
430 assert_return(client
->lease
, -EINVAL
);
432 client
->lease
->ia
.timeout_t2
=
433 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
435 log_dhcp6_client(client
, "Timeout T2");
437 client_start(client
, DHCP6_STATE_REBIND
);
442 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
444 sd_dhcp6_client
*client
= userdata
;
446 assert_return(s
, -EINVAL
);
447 assert_return(client
, -EINVAL
);
448 assert_return(client
->lease
, -EINVAL
);
450 client
->lease
->ia
.timeout_t1
=
451 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
453 log_dhcp6_client(client
, "Timeout T1");
455 client_start(client
, DHCP6_STATE_RENEW
);
460 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
462 sd_dhcp6_client
*client
= userdata
;
463 DHCP6_CLIENT_DONT_DESTROY(client
);
464 enum DHCP6State state
;
468 assert(client
->event
);
470 state
= client
->state
;
472 client_stop(client
, DHCP6_EVENT_RESEND_EXPIRE
);
474 /* RFC 3315, section 18.1.4., says that "...the client may choose to
475 use a Solicit message to locate a new DHCP server..." */
476 if (state
== DHCP6_STATE_REBIND
)
477 client_start(client
, DHCP6_STATE_SOLICITATION
);
482 static usec_t
client_timeout_compute_random(usec_t val
) {
483 return val
- val
/ 10 +
484 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
487 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
490 sd_dhcp6_client
*client
= userdata
;
491 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
492 usec_t max_retransmit_duration
= 0;
493 uint8_t max_retransmit_count
= 0;
494 char time_string
[FORMAT_TIMESPAN_MAX
];
499 assert(client
->event
);
501 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
503 switch (client
->state
) {
504 case DHCP6_STATE_INFORMATION_REQUEST
:
505 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
506 max_retransmit_time
= DHCP6_INF_MAX_RT
;
510 case DHCP6_STATE_SOLICITATION
:
512 if (client
->retransmit_count
&& client
->lease
) {
513 client_start(client
, DHCP6_STATE_REQUEST
);
517 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
518 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
522 case DHCP6_STATE_REQUEST
:
523 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
524 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
525 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
529 case DHCP6_STATE_RENEW
:
530 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
531 max_retransmit_time
= DHCP6_REN_MAX_RT
;
533 /* RFC 3315, section 18.1.3. says max retransmit duration will
534 be the remaining time until T2. Instead of setting MRD,
535 wait for T2 to trigger with the same end result */
539 case DHCP6_STATE_REBIND
:
540 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
541 max_retransmit_time
= DHCP6_REB_MAX_RT
;
543 if (!client
->timeout_resend_expire
) {
544 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
547 client_stop(client
, r
);
550 max_retransmit_duration
= expire
* USEC_PER_SEC
;
555 case DHCP6_STATE_STOPPED
:
556 case DHCP6_STATE_BOUND
:
560 if (max_retransmit_count
&&
561 client
->retransmit_count
>= max_retransmit_count
) {
562 client_stop(client
, DHCP6_EVENT_RETRANS_MAX
);
566 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
570 r
= client_send_message(client
, time_now
);
572 client
->retransmit_count
++;
574 if (!client
->retransmit_time
) {
575 client
->retransmit_time
=
576 client_timeout_compute_random(init_retransmit_time
);
578 if (client
->state
== DHCP6_STATE_SOLICITATION
)
579 client
->retransmit_time
+= init_retransmit_time
/ 10;
582 if (max_retransmit_time
&&
583 client
->retransmit_time
> max_retransmit_time
/ 2)
584 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
586 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
589 log_dhcp6_client(client
, "Next retransmission in %s",
590 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
,
591 client
->retransmit_time
, 0));
593 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
594 clock_boottime_or_monotonic(),
595 time_now
+ client
->retransmit_time
,
596 10 * USEC_PER_MSEC
, client_timeout_resend
,
601 r
= sd_event_source_set_priority(client
->timeout_resend
,
602 client
->event_priority
);
606 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
610 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
612 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
613 max_retransmit_duration
/ USEC_PER_SEC
);
615 r
= sd_event_add_time(client
->event
,
616 &client
->timeout_resend_expire
,
617 clock_boottime_or_monotonic(),
618 time_now
+ max_retransmit_duration
,
620 client_timeout_resend_expire
, client
);
624 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
625 client
->event_priority
);
629 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
636 client_stop(client
, r
);
641 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
646 if (client
->ia_na
.id
)
649 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
656 static int client_parse_message(sd_dhcp6_client
*client
,
657 DHCP6Message
*message
, size_t len
,
658 sd_dhcp6_lease
*lease
) {
660 uint8_t *optval
, *option
, *id
= NULL
;
661 uint16_t optcode
, status
;
662 size_t optlen
, id_len
;
663 bool clientid
= false;
666 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
667 len
-= sizeof(DHCP6Message
);
669 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
672 case DHCP6_OPTION_CLIENTID
:
674 log_dhcp6_client(client
, "%s contains multiple clientids",
675 dhcp6_message_type_to_string(message
->type
));
679 if (optlen
!= client
->duid_len
||
680 memcmp(&client
->duid
, optval
, optlen
) != 0) {
681 log_dhcp6_client(client
, "%s DUID does not match",
682 dhcp6_message_type_to_string(message
->type
));
690 case DHCP6_OPTION_SERVERID
:
691 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
693 log_dhcp6_client(client
, "%s contains multiple serverids",
694 dhcp6_message_type_to_string(message
->type
));
698 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
704 case DHCP6_OPTION_PREFERENCE
:
708 r
= dhcp6_lease_set_preference(lease
, *optval
);
714 case DHCP6_OPTION_STATUS_CODE
:
718 status
= optval
[0] << 8 | optval
[1];
720 log_dhcp6_client(client
, "%s Status %s",
721 dhcp6_message_type_to_string(message
->type
),
722 dhcp6_message_status_to_string(status
));
728 case DHCP6_OPTION_IA_NA
:
729 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
730 log_dhcp6_client(client
, "Information request ignoring IA NA option");
735 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
737 if (r
< 0 && r
!= -ENOMSG
)
740 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
744 if (client
->ia_na
.id
!= iaid_lease
) {
745 log_dhcp6_client(client
, "%s has wrong IAID",
746 dhcp6_message_type_to_string(message
->type
));
752 case DHCP6_OPTION_RAPID_COMMIT
:
753 r
= dhcp6_lease_set_rapid_commit(lease
);
759 case DHCP6_OPTION_DNS_SERVERS
:
760 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
766 case DHCP6_OPTION_DOMAIN_LIST
:
767 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
773 case DHCP6_OPTION_NTP_SERVER
:
774 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
780 case DHCP6_OPTION_SNTP_SERVERS
:
781 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
793 if (r
< 0 || !clientid
) {
794 log_dhcp6_client(client
, "%s has incomplete options",
795 dhcp6_message_type_to_string(message
->type
));
799 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
800 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
802 log_dhcp6_client(client
, "%s has no server id",
803 dhcp6_message_type_to_string(message
->type
));
809 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
811 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
814 if (reply
->type
!= DHCP6_REPLY
)
817 r
= dhcp6_lease_new(&lease
);
821 r
= client_parse_message(client
, reply
, len
, lease
);
825 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
826 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
834 client_set_lease(client
, 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 client_set_lease(client
, lease
);
868 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
869 r
= DHCP6_STATE_REQUEST
;
874 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
875 sd_dhcp6_client
*client
= userdata
;
876 DHCP6_CLIENT_DONT_DESTROY(client
);
877 _cleanup_free_ DHCP6Message
*message
;
882 assert(client
->event
);
884 r
= ioctl(fd
, FIONREAD
, &buflen
);
885 if (r
< 0 || buflen
<= 0)
886 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
888 message
= malloc0(buflen
);
892 len
= read(fd
, message
, buflen
);
893 if ((size_t)len
< sizeof(DHCP6Message
)) {
894 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
898 switch(message
->type
) {
906 case DHCP6_INFORMATION_REQUEST
:
907 case DHCP6_RELAY_FORW
:
908 case DHCP6_RELAY_REPL
:
911 case DHCP6_ADVERTISE
:
913 case DHCP6_RECONFIGURE
:
917 log_dhcp6_client(client
, "unknown message type %d",
922 if (client
->transaction_id
!= (message
->transaction_id
&
923 htobe32(0x00ffffff)))
926 switch (client
->state
) {
927 case DHCP6_STATE_INFORMATION_REQUEST
:
928 r
= client_receive_reply(client
, message
, len
);
932 client_notify(client
, DHCP6_EVENT_INFORMATION_REQUEST
);
934 client_start(client
, DHCP6_STATE_STOPPED
);
938 case DHCP6_STATE_SOLICITATION
:
939 r
= client_receive_advertise(client
, message
, len
);
941 if (r
== DHCP6_STATE_REQUEST
) {
942 client_start(client
, r
);
947 /* fall through for Soliciation Rapid Commit option check */
948 case DHCP6_STATE_REQUEST
:
949 case DHCP6_STATE_RENEW
:
950 case DHCP6_STATE_REBIND
:
952 r
= client_receive_reply(client
, message
, len
);
956 if (r
== DHCP6_STATE_BOUND
) {
958 r
= client_start(client
, DHCP6_STATE_BOUND
);
960 client_stop(client
, r
);
964 client_notify(client
, DHCP6_EVENT_IP_ACQUIRE
);
969 case DHCP6_STATE_BOUND
:
973 case DHCP6_STATE_STOPPED
:
978 log_dhcp6_client(client
, "Recv %s",
979 dhcp6_message_type_to_string(message
->type
));
985 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
987 usec_t timeout
, time_now
;
988 char time_string
[FORMAT_TIMESPAN_MAX
];
990 assert_return(client
, -EINVAL
);
991 assert_return(client
->event
, -EINVAL
);
992 assert_return(client
->index
> 0, -EINVAL
);
993 assert_return(client
->state
!= state
, -EINVAL
);
995 client
->timeout_resend_expire
=
996 sd_event_source_unref(client
->timeout_resend_expire
);
997 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
998 client
->retransmit_time
= 0;
999 client
->retransmit_count
= 0;
1001 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1006 case DHCP6_STATE_STOPPED
:
1007 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1008 client
->state
= DHCP6_STATE_STOPPED
;
1014 case DHCP6_STATE_SOLICITATION
:
1015 client
->state
= DHCP6_STATE_SOLICITATION
;
1019 case DHCP6_STATE_INFORMATION_REQUEST
:
1020 case DHCP6_STATE_REQUEST
:
1021 case DHCP6_STATE_RENEW
:
1022 case DHCP6_STATE_REBIND
:
1024 client
->state
= state
;
1028 case DHCP6_STATE_BOUND
:
1030 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1031 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1033 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1034 be32toh(client
->lease
->ia
.lifetime_t1
),
1035 be32toh(client
->lease
->ia
.lifetime_t2
));
1040 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1042 log_dhcp6_client(client
, "T1 expires in %s",
1043 format_timespan(time_string
,
1044 FORMAT_TIMESPAN_MAX
,
1047 r
= sd_event_add_time(client
->event
,
1048 &client
->lease
->ia
.timeout_t1
,
1049 clock_boottime_or_monotonic(), time_now
+ timeout
,
1050 10 * USEC_PER_SEC
, client_timeout_t1
,
1055 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1056 client
->event_priority
);
1060 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1064 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1066 log_dhcp6_client(client
, "T2 expires in %s",
1067 format_timespan(time_string
,
1068 FORMAT_TIMESPAN_MAX
,
1071 r
= sd_event_add_time(client
->event
,
1072 &client
->lease
->ia
.timeout_t2
,
1073 clock_boottime_or_monotonic(), time_now
+ timeout
,
1074 10 * USEC_PER_SEC
, client_timeout_t2
,
1079 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1080 client
->event_priority
);
1084 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1088 client
->state
= state
;
1093 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1094 client
->transaction_start
= time_now
;
1096 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1097 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1102 r
= sd_event_source_set_priority(client
->timeout_resend
,
1103 client
->event_priority
);
1107 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1114 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1115 client_stop(client
, DHCP6_EVENT_STOP
);
1120 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1122 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1124 assert_return(client
, -EINVAL
);
1125 assert_return(client
->event
, -EINVAL
);
1126 assert_return(client
->index
> 0, -EINVAL
);
1128 r
= client_reset(client
);
1132 r
= client_ensure_iaid(client
);
1136 r
= client_ensure_duid(client
);
1140 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1146 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1147 client
->fd
, EPOLLIN
, client_receive_message
,
1152 r
= sd_event_source_set_priority(client
->receive_message
,
1153 client
->event_priority
);
1157 r
= sd_event_source_set_description(client
->receive_message
,
1158 "dhcp6-receive-message");
1162 if (client
->information_request
)
1163 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1165 log_dhcp6_client(client
, "Started in %s mode",
1166 client
->information_request
? "Information request":
1169 return client_start(client
, state
);
1172 client_reset(client
);
1176 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int priority
) {
1179 assert_return(client
, -EINVAL
);
1180 assert_return(!client
->event
, -EBUSY
);
1183 client
->event
= sd_event_ref(event
);
1185 r
= sd_event_default(&client
->event
);
1190 client
->event_priority
= priority
;
1195 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1196 assert_return(client
, -EINVAL
);
1198 client
->event
= sd_event_unref(client
->event
);
1203 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1207 return client
->event
;
1210 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1215 assert(client
->n_ref
>= 1);
1221 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1226 assert(client
->n_ref
>= 1);
1229 if (client
->n_ref
> 0)
1232 client_reset(client
);
1234 sd_dhcp6_client_detach_event(client
);
1236 free(client
->req_opts
);
1242 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1243 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1246 assert_return(ret
, -EINVAL
);
1248 client
= new0(sd_dhcp6_client
, 1);
1254 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1260 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1262 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1263 if (!client
->req_opts
)
1266 for (t
= 0; t
< client
->req_opts_len
; t
++)
1267 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);