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 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
130 client
->index
= interface_index
;
135 int sd_dhcp6_client_set_mac(
136 sd_dhcp6_client
*client
,
137 const uint8_t *addr
, size_t addr_len
,
140 assert_return(client
, -EINVAL
);
141 assert_return(addr
, -EINVAL
);
142 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
143 assert_return(arp_type
> 0, -EINVAL
);
145 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
147 if (arp_type
== ARPHRD_ETHER
)
148 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
149 else if (arp_type
== ARPHRD_INFINIBAND
)
150 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
154 if (client
->mac_addr_len
== addr_len
&&
155 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
158 memcpy(&client
->mac_addr
, addr
, addr_len
);
159 client
->mac_addr_len
= addr_len
;
160 client
->arp_type
= arp_type
;
165 static int client_ensure_duid(sd_dhcp6_client
*client
) {
166 if (client
->duid_len
!= 0)
169 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
172 int sd_dhcp6_client_set_duid(
173 sd_dhcp6_client
*client
,
175 uint8_t *duid
, size_t duid_len
) {
176 assert_return(client
, -EINVAL
);
177 assert_return(duid
, -EINVAL
);
178 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
180 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
184 if (duid_len
<= sizeof(client
->duid
.llt
))
188 if (duid_len
!= sizeof(client
->duid
.en
))
192 if (duid_len
<= sizeof(client
->duid
.ll
))
195 case DHCP6_DUID_UUID
:
196 if (duid_len
!= sizeof(client
->duid
.uuid
))
200 /* accept unknown type in order to be forward compatible */
204 client
->duid
.type
= htobe16(type
);
205 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
206 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
211 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, bool enabled
) {
212 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
, bool *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
);
262 assert_return(ret
, -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
,
599 client
->retransmit_time
, 0));
601 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
602 clock_boottime_or_monotonic(),
603 time_now
+ client
->retransmit_time
,
604 10 * USEC_PER_MSEC
, client_timeout_resend
,
609 r
= sd_event_source_set_priority(client
->timeout_resend
,
610 client
->event_priority
);
614 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
618 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
620 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
621 max_retransmit_duration
/ USEC_PER_SEC
);
623 r
= sd_event_add_time(client
->event
,
624 &client
->timeout_resend_expire
,
625 clock_boottime_or_monotonic(),
626 time_now
+ max_retransmit_duration
,
628 client_timeout_resend_expire
, client
);
632 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
633 client
->event_priority
);
637 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
644 client_stop(client
, r
);
649 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
654 if (client
->ia_na
.id
)
657 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
664 static int client_parse_message(sd_dhcp6_client
*client
,
665 DHCP6Message
*message
, size_t len
,
666 sd_dhcp6_lease
*lease
) {
668 uint8_t *optval
, *option
, *id
= NULL
;
669 uint16_t optcode
, status
;
670 size_t optlen
, id_len
;
671 bool clientid
= false;
674 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
675 len
-= sizeof(DHCP6Message
);
677 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
680 case DHCP6_OPTION_CLIENTID
:
682 log_dhcp6_client(client
, "%s contains multiple clientids",
683 dhcp6_message_type_to_string(message
->type
));
687 if (optlen
!= client
->duid_len
||
688 memcmp(&client
->duid
, optval
, optlen
) != 0) {
689 log_dhcp6_client(client
, "%s DUID does not match",
690 dhcp6_message_type_to_string(message
->type
));
698 case DHCP6_OPTION_SERVERID
:
699 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
701 log_dhcp6_client(client
, "%s contains multiple serverids",
702 dhcp6_message_type_to_string(message
->type
));
706 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
712 case DHCP6_OPTION_PREFERENCE
:
716 r
= dhcp6_lease_set_preference(lease
, *optval
);
722 case DHCP6_OPTION_STATUS_CODE
:
726 status
= optval
[0] << 8 | optval
[1];
728 log_dhcp6_client(client
, "%s Status %s",
729 dhcp6_message_type_to_string(message
->type
),
730 dhcp6_message_status_to_string(status
));
736 case DHCP6_OPTION_IA_NA
:
737 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
738 log_dhcp6_client(client
, "Information request ignoring IA NA option");
743 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
745 if (r
< 0 && r
!= -ENOMSG
)
748 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
752 if (client
->ia_na
.id
!= iaid_lease
) {
753 log_dhcp6_client(client
, "%s has wrong IAID",
754 dhcp6_message_type_to_string(message
->type
));
760 case DHCP6_OPTION_RAPID_COMMIT
:
761 r
= dhcp6_lease_set_rapid_commit(lease
);
767 case DHCP6_OPTION_DNS_SERVERS
:
768 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
774 case DHCP6_OPTION_DOMAIN_LIST
:
775 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
781 case DHCP6_OPTION_NTP_SERVER
:
782 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
788 case DHCP6_OPTION_SNTP_SERVERS
:
789 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
801 if (r
< 0 || !clientid
) {
802 log_dhcp6_client(client
, "%s has incomplete options",
803 dhcp6_message_type_to_string(message
->type
));
807 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
808 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
810 log_dhcp6_client(client
, "%s has no server id",
811 dhcp6_message_type_to_string(message
->type
));
817 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
819 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
822 if (reply
->type
!= DHCP6_REPLY
)
825 r
= dhcp6_lease_new(&lease
);
829 r
= client_parse_message(client
, reply
, len
, lease
);
833 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
834 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
842 client_set_lease(client
, lease
);
845 return DHCP6_STATE_BOUND
;
848 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
850 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
851 uint8_t pref_advertise
= 0, pref_lease
= 0;
853 if (advertise
->type
!= DHCP6_ADVERTISE
)
856 r
= dhcp6_lease_new(&lease
);
860 r
= client_parse_message(client
, advertise
, len
, lease
);
864 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
868 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
870 if (r
< 0 || pref_advertise
> pref_lease
) {
871 client_set_lease(client
, lease
);
876 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
877 r
= DHCP6_STATE_REQUEST
;
882 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
883 sd_dhcp6_client
*client
= userdata
;
884 DHCP6_CLIENT_DONT_DESTROY(client
);
885 _cleanup_free_ DHCP6Message
*message
;
890 assert(client
->event
);
892 r
= ioctl(fd
, FIONREAD
, &buflen
);
893 if (r
< 0 || buflen
<= 0)
894 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
896 message
= malloc0(buflen
);
900 len
= read(fd
, message
, buflen
);
901 if ((size_t)len
< sizeof(DHCP6Message
)) {
902 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
906 switch(message
->type
) {
914 case DHCP6_INFORMATION_REQUEST
:
915 case DHCP6_RELAY_FORW
:
916 case DHCP6_RELAY_REPL
:
919 case DHCP6_ADVERTISE
:
921 case DHCP6_RECONFIGURE
:
925 log_dhcp6_client(client
, "unknown message type %d",
930 if (client
->transaction_id
!= (message
->transaction_id
&
931 htobe32(0x00ffffff)))
934 switch (client
->state
) {
935 case DHCP6_STATE_INFORMATION_REQUEST
:
936 r
= client_receive_reply(client
, message
, len
);
940 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
942 client_start(client
, DHCP6_STATE_STOPPED
);
946 case DHCP6_STATE_SOLICITATION
:
947 r
= client_receive_advertise(client
, message
, len
);
949 if (r
== DHCP6_STATE_REQUEST
) {
950 client_start(client
, r
);
955 /* fall through for Soliciation Rapid Commit option check */
956 case DHCP6_STATE_REQUEST
:
957 case DHCP6_STATE_RENEW
:
958 case DHCP6_STATE_REBIND
:
960 r
= client_receive_reply(client
, message
, len
);
964 if (r
== DHCP6_STATE_BOUND
) {
966 r
= client_start(client
, DHCP6_STATE_BOUND
);
968 client_stop(client
, r
);
972 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
977 case DHCP6_STATE_BOUND
:
981 case DHCP6_STATE_STOPPED
:
986 log_dhcp6_client(client
, "Recv %s",
987 dhcp6_message_type_to_string(message
->type
));
993 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
995 usec_t timeout
, time_now
;
996 char time_string
[FORMAT_TIMESPAN_MAX
];
998 assert_return(client
, -EINVAL
);
999 assert_return(client
->event
, -EINVAL
);
1000 assert_return(client
->index
> 0, -EINVAL
);
1001 assert_return(client
->state
!= state
, -EINVAL
);
1003 client
->timeout_resend_expire
=
1004 sd_event_source_unref(client
->timeout_resend_expire
);
1005 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1006 client
->retransmit_time
= 0;
1007 client
->retransmit_count
= 0;
1009 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1014 case DHCP6_STATE_STOPPED
:
1015 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1016 client
->state
= DHCP6_STATE_STOPPED
;
1022 case DHCP6_STATE_SOLICITATION
:
1023 client
->state
= DHCP6_STATE_SOLICITATION
;
1027 case DHCP6_STATE_INFORMATION_REQUEST
:
1028 case DHCP6_STATE_REQUEST
:
1029 case DHCP6_STATE_RENEW
:
1030 case DHCP6_STATE_REBIND
:
1032 client
->state
= state
;
1036 case DHCP6_STATE_BOUND
:
1038 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1039 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1041 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1042 be32toh(client
->lease
->ia
.lifetime_t1
),
1043 be32toh(client
->lease
->ia
.lifetime_t2
));
1048 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1050 log_dhcp6_client(client
, "T1 expires in %s",
1051 format_timespan(time_string
,
1052 FORMAT_TIMESPAN_MAX
,
1055 r
= sd_event_add_time(client
->event
,
1056 &client
->lease
->ia
.timeout_t1
,
1057 clock_boottime_or_monotonic(), time_now
+ timeout
,
1058 10 * USEC_PER_SEC
, client_timeout_t1
,
1063 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1064 client
->event_priority
);
1068 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1072 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1074 log_dhcp6_client(client
, "T2 expires in %s",
1075 format_timespan(time_string
,
1076 FORMAT_TIMESPAN_MAX
,
1079 r
= sd_event_add_time(client
->event
,
1080 &client
->lease
->ia
.timeout_t2
,
1081 clock_boottime_or_monotonic(), time_now
+ timeout
,
1082 10 * USEC_PER_SEC
, client_timeout_t2
,
1087 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1088 client
->event_priority
);
1092 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1096 client
->state
= state
;
1101 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1102 client
->transaction_start
= time_now
;
1104 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1105 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1110 r
= sd_event_source_set_priority(client
->timeout_resend
,
1111 client
->event_priority
);
1115 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1122 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1123 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1128 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1130 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1132 assert_return(client
, -EINVAL
);
1133 assert_return(client
->event
, -EINVAL
);
1134 assert_return(client
->index
> 0, -EINVAL
);
1136 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1139 r
= client_reset(client
);
1143 r
= client_ensure_iaid(client
);
1147 r
= client_ensure_duid(client
);
1151 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1157 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1158 client
->fd
, EPOLLIN
, client_receive_message
,
1163 r
= sd_event_source_set_priority(client
->receive_message
,
1164 client
->event_priority
);
1168 r
= sd_event_source_set_description(client
->receive_message
,
1169 "dhcp6-receive-message");
1173 if (client
->information_request
)
1174 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1176 log_dhcp6_client(client
, "Started in %s mode",
1177 client
->information_request
? "Information request":
1180 return client_start(client
, state
);
1183 client_reset(client
);
1187 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int priority
) {
1190 assert_return(client
, -EINVAL
);
1191 assert_return(!client
->event
, -EBUSY
);
1194 client
->event
= sd_event_ref(event
);
1196 r
= sd_event_default(&client
->event
);
1201 client
->event_priority
= priority
;
1206 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1207 assert_return(client
, -EINVAL
);
1209 client
->event
= sd_event_unref(client
->event
);
1214 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1218 return client
->event
;
1221 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1226 assert(client
->n_ref
>= 1);
1232 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1237 assert(client
->n_ref
>= 1);
1240 if (client
->n_ref
> 0)
1243 client_reset(client
);
1245 sd_dhcp6_client_detach_event(client
);
1247 free(client
->req_opts
);
1253 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1254 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1257 assert_return(ret
, -EINVAL
);
1259 client
= new0(sd_dhcp6_client
, 1);
1265 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1271 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1273 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1274 if (!client
->req_opts
)
1277 for (t
= 0; t
< client
->req_opts_len
; t
++)
1278 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);