2 This file is part of systemd.
4 Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/ioctl.h>
23 #include <linux/if_infiniband.h>
25 #include "sd-dhcp6-client.h"
27 #include "alloc-util.h"
28 #include "dhcp-identifier.h"
29 #include "dhcp6-internal.h"
30 #include "dhcp6-lease-internal.h"
31 #include "dhcp6-protocol.h"
33 #include "in-addr-util.h"
34 #include "network-internal.h"
35 #include "random-util.h"
36 #include "socket-util.h"
37 #include "string-table.h"
40 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
42 struct sd_dhcp6_client
{
45 enum DHCP6State state
;
49 struct in6_addr local_address
;
50 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
54 be32_t transaction_id
;
55 usec_t transaction_start
;
56 struct sd_dhcp6_lease
*lease
;
58 bool information_request
;
60 size_t req_opts_allocated
;
62 sd_event_source
*receive_message
;
63 usec_t retransmit_time
;
64 uint8_t retransmit_count
;
65 sd_event_source
*timeout_resend
;
66 sd_event_source
*timeout_resend_expire
;
67 sd_dhcp6_client_callback_t cb
;
73 static const uint16_t default_req_opts
[] = {
74 SD_DHCP6_OPTION_DNS_SERVERS
,
75 SD_DHCP6_OPTION_DOMAIN_LIST
,
76 SD_DHCP6_OPTION_NTP_SERVER
,
77 SD_DHCP6_OPTION_SNTP_SERVERS
,
80 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
81 [DHCP6_SOLICIT
] = "SOLICIT",
82 [DHCP6_ADVERTISE
] = "ADVERTISE",
83 [DHCP6_REQUEST
] = "REQUEST",
84 [DHCP6_CONFIRM
] = "CONFIRM",
85 [DHCP6_RENEW
] = "RENEW",
86 [DHCP6_REBIND
] = "REBIND",
87 [DHCP6_REPLY
] = "REPLY",
88 [DHCP6_RELEASE
] = "RELEASE",
89 [DHCP6_DECLINE
] = "DECLINE",
90 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
91 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
92 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
93 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
96 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
98 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
99 [DHCP6_STATUS_SUCCESS
] = "Success",
100 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
101 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
102 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
103 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
104 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
107 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
109 #define DHCP6_CLIENT_DONT_DESTROY(client) \
110 _cleanup_(sd_dhcp6_client_unrefp) _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_callback_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_local_address(sd_dhcp6_client
*client
, const struct in6_addr
*local_address
) {
135 assert_return(client
, -EINVAL
);
136 assert_return(local_address
, -EINVAL
);
137 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
139 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
141 client
->local_address
= *local_address
;
146 int sd_dhcp6_client_set_mac(
147 sd_dhcp6_client
*client
,
148 const uint8_t *addr
, size_t addr_len
,
151 assert_return(client
, -EINVAL
);
152 assert_return(addr
, -EINVAL
);
153 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
154 assert_return(arp_type
> 0, -EINVAL
);
156 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
158 if (arp_type
== ARPHRD_ETHER
)
159 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
160 else if (arp_type
== ARPHRD_INFINIBAND
)
161 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
165 if (client
->mac_addr_len
== addr_len
&&
166 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
169 memcpy(&client
->mac_addr
, addr
, addr_len
);
170 client
->mac_addr_len
= addr_len
;
171 client
->arp_type
= arp_type
;
176 static int client_ensure_duid(sd_dhcp6_client
*client
) {
177 if (client
->duid_len
!= 0)
180 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
183 int sd_dhcp6_client_set_duid(
184 sd_dhcp6_client
*client
,
186 uint8_t *duid
, size_t duid_len
) {
187 assert_return(client
, -EINVAL
);
188 assert_return(duid
, -EINVAL
);
189 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
191 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
195 if (duid_len
<= sizeof(client
->duid
.llt
))
199 if (duid_len
!= sizeof(client
->duid
.en
))
203 if (duid_len
<= sizeof(client
->duid
.ll
))
206 case DHCP6_DUID_UUID
:
207 if (duid_len
!= sizeof(client
->duid
.uuid
))
211 /* accept unknown type in order to be forward compatible */
215 client
->duid
.type
= htobe16(type
);
216 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
217 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
222 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
223 assert_return(client
, -EINVAL
);
224 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
226 client
->information_request
= enabled
;
231 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
232 assert_return(client
, -EINVAL
);
233 assert_return(enabled
, -EINVAL
);
235 *enabled
= client
->information_request
;
240 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
243 assert_return(client
, -EINVAL
);
244 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
247 case SD_DHCP6_OPTION_DNS_SERVERS
:
248 case SD_DHCP6_OPTION_DOMAIN_LIST
:
249 case SD_DHCP6_OPTION_SNTP_SERVERS
:
250 case SD_DHCP6_OPTION_NTP_SERVER
:
257 for (t
= 0; t
< client
->req_opts_len
; t
++)
258 if (client
->req_opts
[t
] == htobe16(option
))
261 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
262 client
->req_opts_len
+ 1))
265 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
270 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
271 assert_return(client
, -EINVAL
);
277 *ret
= client
->lease
;
282 static void client_notify(sd_dhcp6_client
*client
, int event
) {
284 client
->cb(client
, event
, client
->userdata
);
287 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
289 dhcp6_lease_clear_timers(&client
->lease
->ia
);
290 sd_dhcp6_lease_unref(client
->lease
);
292 client
->lease
= lease
;
295 static int client_reset(sd_dhcp6_client
*client
) {
296 assert_return(client
, -EINVAL
);
298 client_set_lease(client
, NULL
);
300 client
->receive_message
=
301 sd_event_source_unref(client
->receive_message
);
303 client
->fd
= safe_close(client
->fd
);
305 client
->transaction_id
= 0;
306 client
->transaction_start
= 0;
308 client
->ia_na
.timeout_t1
=
309 sd_event_source_unref(client
->ia_na
.timeout_t1
);
310 client
->ia_na
.timeout_t2
=
311 sd_event_source_unref(client
->ia_na
.timeout_t2
);
313 client
->retransmit_time
= 0;
314 client
->retransmit_count
= 0;
315 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
316 client
->timeout_resend_expire
=
317 sd_event_source_unref(client
->timeout_resend_expire
);
319 client
->state
= DHCP6_STATE_STOPPED
;
324 static void client_stop(sd_dhcp6_client
*client
, int error
) {
325 DHCP6_CLIENT_DONT_DESTROY(client
);
329 client_notify(client
, error
);
331 client_reset(client
);
334 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
335 _cleanup_free_ DHCP6Message
*message
= NULL
;
336 struct in6_addr all_servers
=
337 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
338 size_t len
, optlen
= 512;
344 len
= sizeof(DHCP6Message
) + optlen
;
346 message
= malloc0(len
);
350 opt
= (uint8_t *)(message
+ 1);
352 message
->transaction_id
= client
->transaction_id
;
354 switch(client
->state
) {
355 case DHCP6_STATE_INFORMATION_REQUEST
:
356 message
->type
= DHCP6_INFORMATION_REQUEST
;
360 case DHCP6_STATE_SOLICITATION
:
361 message
->type
= DHCP6_SOLICIT
;
363 r
= dhcp6_option_append(&opt
, &optlen
,
364 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
368 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
374 case DHCP6_STATE_REQUEST
:
375 case DHCP6_STATE_RENEW
:
377 if (client
->state
== DHCP6_STATE_REQUEST
)
378 message
->type
= DHCP6_REQUEST
;
380 message
->type
= DHCP6_RENEW
;
382 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
383 client
->lease
->serverid_len
,
384 client
->lease
->serverid
);
388 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
394 case DHCP6_STATE_REBIND
:
395 message
->type
= DHCP6_REBIND
;
397 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
403 case DHCP6_STATE_STOPPED
:
404 case DHCP6_STATE_BOUND
:
408 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
409 client
->req_opts_len
* sizeof(be16_t
),
414 assert (client
->duid_len
);
415 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
416 client
->duid_len
, &client
->duid
);
420 elapsed_usec
= time_now
- client
->transaction_start
;
421 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
422 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
424 elapsed_time
= 0xffff;
426 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
427 sizeof(elapsed_time
), &elapsed_time
);
431 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
436 log_dhcp6_client(client
, "Sent %s",
437 dhcp6_message_type_to_string(message
->type
));
442 static int client_timeout_t2(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_t2
=
451 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
453 log_dhcp6_client(client
, "Timeout T2");
455 client_start(client
, DHCP6_STATE_REBIND
);
460 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
462 sd_dhcp6_client
*client
= userdata
;
464 assert_return(s
, -EINVAL
);
465 assert_return(client
, -EINVAL
);
466 assert_return(client
->lease
, -EINVAL
);
468 client
->lease
->ia
.timeout_t1
=
469 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
471 log_dhcp6_client(client
, "Timeout T1");
473 client_start(client
, DHCP6_STATE_RENEW
);
478 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
480 sd_dhcp6_client
*client
= userdata
;
481 DHCP6_CLIENT_DONT_DESTROY(client
);
482 enum DHCP6State state
;
486 assert(client
->event
);
488 state
= client
->state
;
490 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
492 /* RFC 3315, section 18.1.4., says that "...the client may choose to
493 use a Solicit message to locate a new DHCP server..." */
494 if (state
== DHCP6_STATE_REBIND
)
495 client_start(client
, DHCP6_STATE_SOLICITATION
);
500 static usec_t
client_timeout_compute_random(usec_t val
) {
501 return val
- val
/ 10 +
502 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
505 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
508 sd_dhcp6_client
*client
= userdata
;
509 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
510 usec_t max_retransmit_duration
= 0;
511 uint8_t max_retransmit_count
= 0;
512 char time_string
[FORMAT_TIMESPAN_MAX
];
517 assert(client
->event
);
519 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
521 switch (client
->state
) {
522 case DHCP6_STATE_INFORMATION_REQUEST
:
523 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
524 max_retransmit_time
= DHCP6_INF_MAX_RT
;
528 case DHCP6_STATE_SOLICITATION
:
530 if (client
->retransmit_count
&& client
->lease
) {
531 client_start(client
, DHCP6_STATE_REQUEST
);
535 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
536 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
540 case DHCP6_STATE_REQUEST
:
541 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
542 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
543 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
547 case DHCP6_STATE_RENEW
:
548 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
549 max_retransmit_time
= DHCP6_REN_MAX_RT
;
551 /* RFC 3315, section 18.1.3. says max retransmit duration will
552 be the remaining time until T2. Instead of setting MRD,
553 wait for T2 to trigger with the same end result */
557 case DHCP6_STATE_REBIND
:
558 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
559 max_retransmit_time
= DHCP6_REB_MAX_RT
;
561 if (!client
->timeout_resend_expire
) {
562 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
565 client_stop(client
, r
);
568 max_retransmit_duration
= expire
* USEC_PER_SEC
;
573 case DHCP6_STATE_STOPPED
:
574 case DHCP6_STATE_BOUND
:
578 if (max_retransmit_count
&&
579 client
->retransmit_count
>= max_retransmit_count
) {
580 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
584 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
588 r
= client_send_message(client
, time_now
);
590 client
->retransmit_count
++;
592 if (!client
->retransmit_time
) {
593 client
->retransmit_time
=
594 client_timeout_compute_random(init_retransmit_time
);
596 if (client
->state
== DHCP6_STATE_SOLICITATION
)
597 client
->retransmit_time
+= init_retransmit_time
/ 10;
600 if (max_retransmit_time
&&
601 client
->retransmit_time
> max_retransmit_time
/ 2)
602 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
604 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
607 log_dhcp6_client(client
, "Next retransmission in %s",
608 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
610 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
611 clock_boottime_or_monotonic(),
612 time_now
+ client
->retransmit_time
,
613 10 * USEC_PER_MSEC
, client_timeout_resend
,
618 r
= sd_event_source_set_priority(client
->timeout_resend
,
619 client
->event_priority
);
623 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
627 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
629 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
630 max_retransmit_duration
/ USEC_PER_SEC
);
632 r
= sd_event_add_time(client
->event
,
633 &client
->timeout_resend_expire
,
634 clock_boottime_or_monotonic(),
635 time_now
+ max_retransmit_duration
,
637 client_timeout_resend_expire
, client
);
641 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
642 client
->event_priority
);
646 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
653 client_stop(client
, r
);
658 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
663 if (client
->ia_na
.id
)
666 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
673 static int client_parse_message(sd_dhcp6_client
*client
,
674 DHCP6Message
*message
, size_t len
,
675 sd_dhcp6_lease
*lease
) {
677 uint8_t *optval
, *option
, *id
= NULL
;
678 uint16_t optcode
, status
;
679 size_t optlen
, id_len
;
680 bool clientid
= false;
683 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
684 len
-= sizeof(DHCP6Message
);
686 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
689 case SD_DHCP6_OPTION_CLIENTID
:
691 log_dhcp6_client(client
, "%s contains multiple clientids",
692 dhcp6_message_type_to_string(message
->type
));
696 if (optlen
!= client
->duid_len
||
697 memcmp(&client
->duid
, optval
, optlen
) != 0) {
698 log_dhcp6_client(client
, "%s DUID does not match",
699 dhcp6_message_type_to_string(message
->type
));
707 case SD_DHCP6_OPTION_SERVERID
:
708 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
710 log_dhcp6_client(client
, "%s contains multiple serverids",
711 dhcp6_message_type_to_string(message
->type
));
715 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
721 case SD_DHCP6_OPTION_PREFERENCE
:
725 r
= dhcp6_lease_set_preference(lease
, *optval
);
731 case SD_DHCP6_OPTION_STATUS_CODE
:
735 status
= optval
[0] << 8 | optval
[1];
737 log_dhcp6_client(client
, "%s Status %s",
738 dhcp6_message_type_to_string(message
->type
),
739 dhcp6_message_status_to_string(status
));
745 case SD_DHCP6_OPTION_IA_NA
:
746 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
747 log_dhcp6_client(client
, "Information request ignoring IA NA option");
752 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
754 if (r
< 0 && r
!= -ENOMSG
)
757 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
761 if (client
->ia_na
.id
!= iaid_lease
) {
762 log_dhcp6_client(client
, "%s has wrong IAID",
763 dhcp6_message_type_to_string(message
->type
));
769 case SD_DHCP6_OPTION_RAPID_COMMIT
:
770 r
= dhcp6_lease_set_rapid_commit(lease
);
776 case SD_DHCP6_OPTION_DNS_SERVERS
:
777 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
783 case SD_DHCP6_OPTION_DOMAIN_LIST
:
784 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
790 case SD_DHCP6_OPTION_NTP_SERVER
:
791 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
797 case SD_DHCP6_OPTION_SNTP_SERVERS
:
798 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
810 if (r
< 0 || !clientid
) {
811 log_dhcp6_client(client
, "%s has incomplete options",
812 dhcp6_message_type_to_string(message
->type
));
816 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
817 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
819 log_dhcp6_client(client
, "%s has no server id",
820 dhcp6_message_type_to_string(message
->type
));
826 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
828 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
831 if (reply
->type
!= DHCP6_REPLY
)
834 r
= dhcp6_lease_new(&lease
);
838 r
= client_parse_message(client
, reply
, len
, lease
);
842 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
843 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
851 client_set_lease(client
, lease
);
854 return DHCP6_STATE_BOUND
;
857 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
859 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
860 uint8_t pref_advertise
= 0, pref_lease
= 0;
862 if (advertise
->type
!= DHCP6_ADVERTISE
)
865 r
= dhcp6_lease_new(&lease
);
869 r
= client_parse_message(client
, advertise
, len
, lease
);
873 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
877 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
879 if (r
< 0 || pref_advertise
> pref_lease
) {
880 client_set_lease(client
, lease
);
885 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
886 r
= DHCP6_STATE_REQUEST
;
891 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
892 sd_dhcp6_client
*client
= userdata
;
893 DHCP6_CLIENT_DONT_DESTROY(client
);
894 _cleanup_free_ DHCP6Message
*message
= NULL
;
900 assert(client
->event
);
902 buflen
= next_datagram_size_fd(fd
);
906 message
= malloc(buflen
);
910 len
= read(fd
, message
, buflen
);
912 if (errno
== EAGAIN
|| errno
== EINTR
)
915 log_dhcp6_client(client
, "Could not receive message from UDP socket: %m");
918 } else if ((size_t)len
< sizeof(DHCP6Message
))
921 switch(message
->type
) {
929 case DHCP6_INFORMATION_REQUEST
:
930 case DHCP6_RELAY_FORW
:
931 case DHCP6_RELAY_REPL
:
934 case DHCP6_ADVERTISE
:
936 case DHCP6_RECONFIGURE
:
940 log_dhcp6_client(client
, "unknown message type %d",
945 if (client
->transaction_id
!= (message
->transaction_id
&
946 htobe32(0x00ffffff)))
949 switch (client
->state
) {
950 case DHCP6_STATE_INFORMATION_REQUEST
:
951 r
= client_receive_reply(client
, message
, len
);
955 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
957 client_start(client
, DHCP6_STATE_STOPPED
);
961 case DHCP6_STATE_SOLICITATION
:
962 r
= client_receive_advertise(client
, message
, len
);
964 if (r
== DHCP6_STATE_REQUEST
) {
965 client_start(client
, r
);
970 /* fall through for Soliciation Rapid Commit option check */
971 case DHCP6_STATE_REQUEST
:
972 case DHCP6_STATE_RENEW
:
973 case DHCP6_STATE_REBIND
:
975 r
= client_receive_reply(client
, message
, len
);
979 if (r
== DHCP6_STATE_BOUND
) {
981 r
= client_start(client
, DHCP6_STATE_BOUND
);
983 client_stop(client
, r
);
987 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
992 case DHCP6_STATE_BOUND
:
996 case DHCP6_STATE_STOPPED
:
1001 log_dhcp6_client(client
, "Recv %s",
1002 dhcp6_message_type_to_string(message
->type
));
1008 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1010 usec_t timeout
, time_now
;
1011 char time_string
[FORMAT_TIMESPAN_MAX
];
1013 assert_return(client
, -EINVAL
);
1014 assert_return(client
->event
, -EINVAL
);
1015 assert_return(client
->index
> 0, -EINVAL
);
1016 assert_return(client
->state
!= state
, -EINVAL
);
1018 client
->timeout_resend_expire
=
1019 sd_event_source_unref(client
->timeout_resend_expire
);
1020 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1021 client
->retransmit_time
= 0;
1022 client
->retransmit_count
= 0;
1024 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1029 case DHCP6_STATE_STOPPED
:
1030 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1031 client
->state
= DHCP6_STATE_STOPPED
;
1037 case DHCP6_STATE_SOLICITATION
:
1038 client
->state
= DHCP6_STATE_SOLICITATION
;
1042 case DHCP6_STATE_INFORMATION_REQUEST
:
1043 case DHCP6_STATE_REQUEST
:
1044 case DHCP6_STATE_RENEW
:
1045 case DHCP6_STATE_REBIND
:
1047 client
->state
= state
;
1051 case DHCP6_STATE_BOUND
:
1053 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1054 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1056 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1057 be32toh(client
->lease
->ia
.lifetime_t1
),
1058 be32toh(client
->lease
->ia
.lifetime_t2
));
1063 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1065 log_dhcp6_client(client
, "T1 expires in %s",
1066 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1068 r
= sd_event_add_time(client
->event
,
1069 &client
->lease
->ia
.timeout_t1
,
1070 clock_boottime_or_monotonic(), time_now
+ timeout
,
1071 10 * USEC_PER_SEC
, client_timeout_t1
,
1076 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1077 client
->event_priority
);
1081 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1085 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1087 log_dhcp6_client(client
, "T2 expires in %s",
1088 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1090 r
= sd_event_add_time(client
->event
,
1091 &client
->lease
->ia
.timeout_t2
,
1092 clock_boottime_or_monotonic(), time_now
+ timeout
,
1093 10 * USEC_PER_SEC
, client_timeout_t2
,
1098 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1099 client
->event_priority
);
1103 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1107 client
->state
= state
;
1112 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1113 client
->transaction_start
= time_now
;
1115 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1116 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1121 r
= sd_event_source_set_priority(client
->timeout_resend
,
1122 client
->event_priority
);
1126 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1133 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1134 assert_return(client
, -EINVAL
);
1136 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1141 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1142 assert_return(client
, -EINVAL
);
1144 return client
->state
!= DHCP6_STATE_STOPPED
;
1147 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1149 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1151 assert_return(client
, -EINVAL
);
1152 assert_return(client
->event
, -EINVAL
);
1153 assert_return(client
->index
> 0, -EINVAL
);
1154 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1156 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1159 r
= client_reset(client
);
1163 r
= client_ensure_iaid(client
);
1167 r
= client_ensure_duid(client
);
1171 r
= dhcp6_network_bind_udp_socket(client
->index
, &client
->local_address
);
1177 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1178 client
->fd
, EPOLLIN
, client_receive_message
,
1183 r
= sd_event_source_set_priority(client
->receive_message
,
1184 client
->event_priority
);
1188 r
= sd_event_source_set_description(client
->receive_message
,
1189 "dhcp6-receive-message");
1193 if (client
->information_request
)
1194 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1196 log_dhcp6_client(client
, "Started in %s mode",
1197 client
->information_request
? "Information request":
1200 return client_start(client
, state
);
1203 client_reset(client
);
1207 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int64_t priority
) {
1210 assert_return(client
, -EINVAL
);
1211 assert_return(!client
->event
, -EBUSY
);
1214 client
->event
= sd_event_ref(event
);
1216 r
= sd_event_default(&client
->event
);
1221 client
->event_priority
= priority
;
1226 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1227 assert_return(client
, -EINVAL
);
1229 client
->event
= sd_event_unref(client
->event
);
1234 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1238 return client
->event
;
1241 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1246 assert(client
->n_ref
>= 1);
1252 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1257 assert(client
->n_ref
>= 1);
1260 if (client
->n_ref
> 0)
1263 client_reset(client
);
1265 sd_dhcp6_client_detach_event(client
);
1267 free(client
->req_opts
);
1273 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1274 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1277 assert_return(ret
, -EINVAL
);
1279 client
= new0(sd_dhcp6_client
, 1);
1285 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1291 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1293 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1294 if (!client
->req_opts
)
1297 for (t
= 0; t
< client
->req_opts_len
; t
++)
1298 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);