1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/ioctl.h>
25 #include <linux/if_infiniband.h>
27 #include "sd-dhcp6-client.h"
29 #include "alloc-util.h"
30 #include "dhcp-identifier.h"
31 #include "dhcp6-internal.h"
32 #include "dhcp6-lease-internal.h"
33 #include "dhcp6-protocol.h"
35 #include "in-addr-util.h"
36 #include "network-internal.h"
37 #include "random-util.h"
38 #include "string-table.h"
41 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
43 struct sd_dhcp6_client
{
46 enum DHCP6State state
;
50 struct in6_addr local_address
;
51 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
55 be32_t transaction_id
;
56 usec_t transaction_start
;
57 struct sd_dhcp6_lease
*lease
;
59 bool information_request
;
61 size_t req_opts_allocated
;
63 sd_event_source
*receive_message
;
64 usec_t retransmit_time
;
65 uint8_t retransmit_count
;
66 sd_event_source
*timeout_resend
;
67 sd_event_source
*timeout_resend_expire
;
68 sd_dhcp6_client_cb_t cb
;
74 static const uint16_t default_req_opts
[] = {
75 SD_DHCP6_OPTION_DNS_SERVERS
,
76 SD_DHCP6_OPTION_DOMAIN_LIST
,
77 SD_DHCP6_OPTION_NTP_SERVER
,
78 SD_DHCP6_OPTION_SNTP_SERVERS
,
81 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
82 [DHCP6_SOLICIT
] = "SOLICIT",
83 [DHCP6_ADVERTISE
] = "ADVERTISE",
84 [DHCP6_REQUEST
] = "REQUEST",
85 [DHCP6_CONFIRM
] = "CONFIRM",
86 [DHCP6_RENEW
] = "RENEW",
87 [DHCP6_REBIND
] = "REBIND",
88 [DHCP6_REPLY
] = "REPLY",
89 [DHCP6_RELEASE
] = "RELEASE",
90 [DHCP6_DECLINE
] = "DECLINE",
91 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
92 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
93 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
94 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
97 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
99 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
100 [DHCP6_STATUS_SUCCESS
] = "Success",
101 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
102 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
103 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
104 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
105 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
108 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
110 #define DHCP6_CLIENT_DONT_DESTROY(client) \
111 _cleanup_(sd_dhcp6_client_unrefp) _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_local_address(sd_dhcp6_client
*client
, const struct in6_addr
*local_address
) {
136 assert_return(client
, -EINVAL
);
137 assert_return(local_address
, -EINVAL
);
138 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) local_address
) > 0, -EINVAL
);
140 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
142 client
->local_address
= *local_address
;
147 int sd_dhcp6_client_set_mac(
148 sd_dhcp6_client
*client
,
149 const uint8_t *addr
, size_t addr_len
,
152 assert_return(client
, -EINVAL
);
153 assert_return(addr
, -EINVAL
);
154 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
155 assert_return(arp_type
> 0, -EINVAL
);
157 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
159 if (arp_type
== ARPHRD_ETHER
)
160 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
161 else if (arp_type
== ARPHRD_INFINIBAND
)
162 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
166 if (client
->mac_addr_len
== addr_len
&&
167 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
170 memcpy(&client
->mac_addr
, addr
, addr_len
);
171 client
->mac_addr_len
= addr_len
;
172 client
->arp_type
= arp_type
;
177 static int client_ensure_duid(sd_dhcp6_client
*client
) {
178 if (client
->duid_len
!= 0)
181 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
184 int sd_dhcp6_client_set_duid(
185 sd_dhcp6_client
*client
,
187 uint8_t *duid
, size_t duid_len
) {
188 assert_return(client
, -EINVAL
);
189 assert_return(duid
, -EINVAL
);
190 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
192 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
196 if (duid_len
<= sizeof(client
->duid
.llt
))
200 if (duid_len
!= sizeof(client
->duid
.en
))
204 if (duid_len
<= sizeof(client
->duid
.ll
))
207 case DHCP6_DUID_UUID
:
208 if (duid_len
!= sizeof(client
->duid
.uuid
))
212 /* accept unknown type in order to be forward compatible */
216 client
->duid
.type
= htobe16(type
);
217 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
218 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
223 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
, int enabled
) {
224 assert_return(client
, -EINVAL
);
225 assert_return(IN_SET(client
->state
, DHCP6_STATE_STOPPED
), -EBUSY
);
227 client
->information_request
= enabled
;
232 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
, int *enabled
) {
233 assert_return(client
, -EINVAL
);
234 assert_return(enabled
, -EINVAL
);
236 *enabled
= client
->information_request
;
241 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
, uint16_t option
) {
244 assert_return(client
, -EINVAL
);
245 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
248 case SD_DHCP6_OPTION_DNS_SERVERS
:
249 case SD_DHCP6_OPTION_DOMAIN_LIST
:
250 case SD_DHCP6_OPTION_SNTP_SERVERS
:
251 case SD_DHCP6_OPTION_NTP_SERVER
:
258 for (t
= 0; t
< client
->req_opts_len
; t
++)
259 if (client
->req_opts
[t
] == htobe16(option
))
262 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
263 client
->req_opts_len
+ 1))
266 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
271 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
272 assert_return(client
, -EINVAL
);
278 *ret
= client
->lease
;
283 static void client_notify(sd_dhcp6_client
*client
, int event
) {
285 client
->cb(client
, event
, client
->userdata
);
288 static void client_set_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
*lease
) {
290 dhcp6_lease_clear_timers(&client
->lease
->ia
);
291 sd_dhcp6_lease_unref(client
->lease
);
293 client
->lease
= lease
;
296 static int client_reset(sd_dhcp6_client
*client
) {
297 assert_return(client
, -EINVAL
);
299 client_set_lease(client
, NULL
);
301 client
->receive_message
=
302 sd_event_source_unref(client
->receive_message
);
304 client
->fd
= safe_close(client
->fd
);
306 client
->transaction_id
= 0;
307 client
->transaction_start
= 0;
309 client
->ia_na
.timeout_t1
=
310 sd_event_source_unref(client
->ia_na
.timeout_t1
);
311 client
->ia_na
.timeout_t2
=
312 sd_event_source_unref(client
->ia_na
.timeout_t2
);
314 client
->retransmit_time
= 0;
315 client
->retransmit_count
= 0;
316 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
317 client
->timeout_resend_expire
=
318 sd_event_source_unref(client
->timeout_resend_expire
);
320 client
->state
= DHCP6_STATE_STOPPED
;
325 static void client_stop(sd_dhcp6_client
*client
, int error
) {
326 DHCP6_CLIENT_DONT_DESTROY(client
);
330 client_notify(client
, error
);
332 client_reset(client
);
335 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
336 _cleanup_free_ DHCP6Message
*message
= NULL
;
337 struct in6_addr all_servers
=
338 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
339 size_t len
, optlen
= 512;
345 len
= sizeof(DHCP6Message
) + optlen
;
347 message
= malloc0(len
);
351 opt
= (uint8_t *)(message
+ 1);
353 message
->transaction_id
= client
->transaction_id
;
355 switch(client
->state
) {
356 case DHCP6_STATE_INFORMATION_REQUEST
:
357 message
->type
= DHCP6_INFORMATION_REQUEST
;
361 case DHCP6_STATE_SOLICITATION
:
362 message
->type
= DHCP6_SOLICIT
;
364 r
= dhcp6_option_append(&opt
, &optlen
,
365 SD_DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
369 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
375 case DHCP6_STATE_REQUEST
:
376 case DHCP6_STATE_RENEW
:
378 if (client
->state
== DHCP6_STATE_REQUEST
)
379 message
->type
= DHCP6_REQUEST
;
381 message
->type
= DHCP6_RENEW
;
383 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_SERVERID
,
384 client
->lease
->serverid_len
,
385 client
->lease
->serverid
);
389 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
395 case DHCP6_STATE_REBIND
:
396 message
->type
= DHCP6_REBIND
;
398 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
404 case DHCP6_STATE_STOPPED
:
405 case DHCP6_STATE_BOUND
:
409 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ORO
,
410 client
->req_opts_len
* sizeof(be16_t
),
415 assert (client
->duid_len
);
416 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_CLIENTID
,
417 client
->duid_len
, &client
->duid
);
421 elapsed_usec
= time_now
- client
->transaction_start
;
422 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
423 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
425 elapsed_time
= 0xffff;
427 r
= dhcp6_option_append(&opt
, &optlen
, SD_DHCP6_OPTION_ELAPSED_TIME
,
428 sizeof(elapsed_time
), &elapsed_time
);
432 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
437 log_dhcp6_client(client
, "Sent %s",
438 dhcp6_message_type_to_string(message
->type
));
443 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
445 sd_dhcp6_client
*client
= userdata
;
447 assert_return(s
, -EINVAL
);
448 assert_return(client
, -EINVAL
);
449 assert_return(client
->lease
, -EINVAL
);
451 client
->lease
->ia
.timeout_t2
=
452 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
454 log_dhcp6_client(client
, "Timeout T2");
456 client_start(client
, DHCP6_STATE_REBIND
);
461 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
463 sd_dhcp6_client
*client
= userdata
;
465 assert_return(s
, -EINVAL
);
466 assert_return(client
, -EINVAL
);
467 assert_return(client
->lease
, -EINVAL
);
469 client
->lease
->ia
.timeout_t1
=
470 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
472 log_dhcp6_client(client
, "Timeout T1");
474 client_start(client
, DHCP6_STATE_RENEW
);
479 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
481 sd_dhcp6_client
*client
= userdata
;
482 DHCP6_CLIENT_DONT_DESTROY(client
);
483 enum DHCP6State state
;
487 assert(client
->event
);
489 state
= client
->state
;
491 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE
);
493 /* RFC 3315, section 18.1.4., says that "...the client may choose to
494 use a Solicit message to locate a new DHCP server..." */
495 if (state
== DHCP6_STATE_REBIND
)
496 client_start(client
, DHCP6_STATE_SOLICITATION
);
501 static usec_t
client_timeout_compute_random(usec_t val
) {
502 return val
- val
/ 10 +
503 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
506 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
509 sd_dhcp6_client
*client
= userdata
;
510 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
511 usec_t max_retransmit_duration
= 0;
512 uint8_t max_retransmit_count
= 0;
513 char time_string
[FORMAT_TIMESPAN_MAX
];
518 assert(client
->event
);
520 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
522 switch (client
->state
) {
523 case DHCP6_STATE_INFORMATION_REQUEST
:
524 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
525 max_retransmit_time
= DHCP6_INF_MAX_RT
;
529 case DHCP6_STATE_SOLICITATION
:
531 if (client
->retransmit_count
&& client
->lease
) {
532 client_start(client
, DHCP6_STATE_REQUEST
);
536 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
537 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
541 case DHCP6_STATE_REQUEST
:
542 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
543 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
544 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
548 case DHCP6_STATE_RENEW
:
549 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
550 max_retransmit_time
= DHCP6_REN_MAX_RT
;
552 /* RFC 3315, section 18.1.3. says max retransmit duration will
553 be the remaining time until T2. Instead of setting MRD,
554 wait for T2 to trigger with the same end result */
558 case DHCP6_STATE_REBIND
:
559 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
560 max_retransmit_time
= DHCP6_REB_MAX_RT
;
562 if (!client
->timeout_resend_expire
) {
563 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
566 client_stop(client
, r
);
569 max_retransmit_duration
= expire
* USEC_PER_SEC
;
574 case DHCP6_STATE_STOPPED
:
575 case DHCP6_STATE_BOUND
:
579 if (max_retransmit_count
&&
580 client
->retransmit_count
>= max_retransmit_count
) {
581 client_stop(client
, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX
);
585 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
589 r
= client_send_message(client
, time_now
);
591 client
->retransmit_count
++;
593 if (!client
->retransmit_time
) {
594 client
->retransmit_time
=
595 client_timeout_compute_random(init_retransmit_time
);
597 if (client
->state
== DHCP6_STATE_SOLICITATION
)
598 client
->retransmit_time
+= init_retransmit_time
/ 10;
601 if (max_retransmit_time
&&
602 client
->retransmit_time
> max_retransmit_time
/ 2)
603 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
605 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
608 log_dhcp6_client(client
, "Next retransmission in %s",
609 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, client
->retransmit_time
, USEC_PER_SEC
));
611 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
612 clock_boottime_or_monotonic(),
613 time_now
+ client
->retransmit_time
,
614 10 * USEC_PER_MSEC
, client_timeout_resend
,
619 r
= sd_event_source_set_priority(client
->timeout_resend
,
620 client
->event_priority
);
624 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
628 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
630 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
631 max_retransmit_duration
/ USEC_PER_SEC
);
633 r
= sd_event_add_time(client
->event
,
634 &client
->timeout_resend_expire
,
635 clock_boottime_or_monotonic(),
636 time_now
+ max_retransmit_duration
,
638 client_timeout_resend_expire
, client
);
642 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
643 client
->event_priority
);
647 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
654 client_stop(client
, r
);
659 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
664 if (client
->ia_na
.id
)
667 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
674 static int client_parse_message(sd_dhcp6_client
*client
,
675 DHCP6Message
*message
, size_t len
,
676 sd_dhcp6_lease
*lease
) {
678 uint8_t *optval
, *option
, *id
= NULL
;
679 uint16_t optcode
, status
;
680 size_t optlen
, id_len
;
681 bool clientid
= false;
684 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
685 len
-= sizeof(DHCP6Message
);
687 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
690 case SD_DHCP6_OPTION_CLIENTID
:
692 log_dhcp6_client(client
, "%s contains multiple clientids",
693 dhcp6_message_type_to_string(message
->type
));
697 if (optlen
!= client
->duid_len
||
698 memcmp(&client
->duid
, optval
, optlen
) != 0) {
699 log_dhcp6_client(client
, "%s DUID does not match",
700 dhcp6_message_type_to_string(message
->type
));
708 case SD_DHCP6_OPTION_SERVERID
:
709 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
711 log_dhcp6_client(client
, "%s contains multiple serverids",
712 dhcp6_message_type_to_string(message
->type
));
716 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
722 case SD_DHCP6_OPTION_PREFERENCE
:
726 r
= dhcp6_lease_set_preference(lease
, *optval
);
732 case SD_DHCP6_OPTION_STATUS_CODE
:
736 status
= optval
[0] << 8 | optval
[1];
738 log_dhcp6_client(client
, "%s Status %s",
739 dhcp6_message_type_to_string(message
->type
),
740 dhcp6_message_status_to_string(status
));
746 case SD_DHCP6_OPTION_IA_NA
:
747 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
748 log_dhcp6_client(client
, "Information request ignoring IA NA option");
753 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
755 if (r
< 0 && r
!= -ENOMSG
)
758 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
762 if (client
->ia_na
.id
!= iaid_lease
) {
763 log_dhcp6_client(client
, "%s has wrong IAID",
764 dhcp6_message_type_to_string(message
->type
));
770 case SD_DHCP6_OPTION_RAPID_COMMIT
:
771 r
= dhcp6_lease_set_rapid_commit(lease
);
777 case SD_DHCP6_OPTION_DNS_SERVERS
:
778 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
784 case SD_DHCP6_OPTION_DOMAIN_LIST
:
785 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
791 case SD_DHCP6_OPTION_NTP_SERVER
:
792 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
798 case SD_DHCP6_OPTION_SNTP_SERVERS
:
799 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
811 if (r
< 0 || !clientid
) {
812 log_dhcp6_client(client
, "%s has incomplete options",
813 dhcp6_message_type_to_string(message
->type
));
817 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
818 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
820 log_dhcp6_client(client
, "%s has no server id",
821 dhcp6_message_type_to_string(message
->type
));
827 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
, size_t len
) {
829 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
832 if (reply
->type
!= DHCP6_REPLY
)
835 r
= dhcp6_lease_new(&lease
);
839 r
= client_parse_message(client
, reply
, len
, lease
);
843 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
844 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
852 client_set_lease(client
, lease
);
855 return DHCP6_STATE_BOUND
;
858 static int client_receive_advertise(sd_dhcp6_client
*client
, DHCP6Message
*advertise
, size_t len
) {
860 _cleanup_(sd_dhcp6_lease_unrefp
) sd_dhcp6_lease
*lease
= NULL
;
861 uint8_t pref_advertise
= 0, pref_lease
= 0;
863 if (advertise
->type
!= DHCP6_ADVERTISE
)
866 r
= dhcp6_lease_new(&lease
);
870 r
= client_parse_message(client
, advertise
, len
, lease
);
874 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
878 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
880 if (r
< 0 || pref_advertise
> pref_lease
) {
881 client_set_lease(client
, lease
);
886 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
887 r
= DHCP6_STATE_REQUEST
;
892 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
893 sd_dhcp6_client
*client
= userdata
;
894 DHCP6_CLIENT_DONT_DESTROY(client
);
895 _cleanup_free_ DHCP6Message
*message
= NULL
;
900 assert(client
->event
);
902 r
= ioctl(fd
, FIONREAD
, &buflen
);
906 /* This really should not happen */
909 message
= malloc(buflen
);
913 len
= read(fd
, message
, buflen
);
915 if (errno
== EAGAIN
|| errno
== EINTR
)
918 log_dhcp6_client(client
, "Could not receive message from UDP socket: %m");
921 } else if ((size_t)len
< sizeof(DHCP6Message
))
924 switch(message
->type
) {
932 case DHCP6_INFORMATION_REQUEST
:
933 case DHCP6_RELAY_FORW
:
934 case DHCP6_RELAY_REPL
:
937 case DHCP6_ADVERTISE
:
939 case DHCP6_RECONFIGURE
:
943 log_dhcp6_client(client
, "unknown message type %d",
948 if (client
->transaction_id
!= (message
->transaction_id
&
949 htobe32(0x00ffffff)))
952 switch (client
->state
) {
953 case DHCP6_STATE_INFORMATION_REQUEST
:
954 r
= client_receive_reply(client
, message
, len
);
958 client_notify(client
, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST
);
960 client_start(client
, DHCP6_STATE_STOPPED
);
964 case DHCP6_STATE_SOLICITATION
:
965 r
= client_receive_advertise(client
, message
, len
);
967 if (r
== DHCP6_STATE_REQUEST
) {
968 client_start(client
, r
);
973 /* fall through for Soliciation Rapid Commit option check */
974 case DHCP6_STATE_REQUEST
:
975 case DHCP6_STATE_RENEW
:
976 case DHCP6_STATE_REBIND
:
978 r
= client_receive_reply(client
, message
, len
);
982 if (r
== DHCP6_STATE_BOUND
) {
984 r
= client_start(client
, DHCP6_STATE_BOUND
);
986 client_stop(client
, r
);
990 client_notify(client
, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE
);
995 case DHCP6_STATE_BOUND
:
999 case DHCP6_STATE_STOPPED
:
1004 log_dhcp6_client(client
, "Recv %s",
1005 dhcp6_message_type_to_string(message
->type
));
1011 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
) {
1013 usec_t timeout
, time_now
;
1014 char time_string
[FORMAT_TIMESPAN_MAX
];
1016 assert_return(client
, -EINVAL
);
1017 assert_return(client
->event
, -EINVAL
);
1018 assert_return(client
->index
> 0, -EINVAL
);
1019 assert_return(client
->state
!= state
, -EINVAL
);
1021 client
->timeout_resend_expire
=
1022 sd_event_source_unref(client
->timeout_resend_expire
);
1023 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1024 client
->retransmit_time
= 0;
1025 client
->retransmit_count
= 0;
1027 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1032 case DHCP6_STATE_STOPPED
:
1033 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1034 client
->state
= DHCP6_STATE_STOPPED
;
1040 case DHCP6_STATE_SOLICITATION
:
1041 client
->state
= DHCP6_STATE_SOLICITATION
;
1045 case DHCP6_STATE_INFORMATION_REQUEST
:
1046 case DHCP6_STATE_REQUEST
:
1047 case DHCP6_STATE_RENEW
:
1048 case DHCP6_STATE_REBIND
:
1050 client
->state
= state
;
1054 case DHCP6_STATE_BOUND
:
1056 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1057 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1059 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1060 be32toh(client
->lease
->ia
.lifetime_t1
),
1061 be32toh(client
->lease
->ia
.lifetime_t2
));
1066 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1068 log_dhcp6_client(client
, "T1 expires in %s",
1069 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1071 r
= sd_event_add_time(client
->event
,
1072 &client
->lease
->ia
.timeout_t1
,
1073 clock_boottime_or_monotonic(), time_now
+ timeout
,
1074 10 * USEC_PER_SEC
, client_timeout_t1
,
1079 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1080 client
->event_priority
);
1084 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1088 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1090 log_dhcp6_client(client
, "T2 expires in %s",
1091 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
, timeout
, USEC_PER_SEC
));
1093 r
= sd_event_add_time(client
->event
,
1094 &client
->lease
->ia
.timeout_t2
,
1095 clock_boottime_or_monotonic(), time_now
+ timeout
,
1096 10 * USEC_PER_SEC
, client_timeout_t2
,
1101 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1102 client
->event_priority
);
1106 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1110 client
->state
= state
;
1115 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1116 client
->transaction_start
= time_now
;
1118 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1119 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1124 r
= sd_event_source_set_priority(client
->timeout_resend
,
1125 client
->event_priority
);
1129 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1136 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
) {
1137 assert_return(client
, -EINVAL
);
1139 client_stop(client
, SD_DHCP6_CLIENT_EVENT_STOP
);
1144 int sd_dhcp6_client_is_running(sd_dhcp6_client
*client
) {
1145 assert_return(client
, -EINVAL
);
1147 return client
->state
!= DHCP6_STATE_STOPPED
;
1150 int sd_dhcp6_client_start(sd_dhcp6_client
*client
) {
1152 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1154 assert_return(client
, -EINVAL
);
1155 assert_return(client
->event
, -EINVAL
);
1156 assert_return(client
->index
> 0, -EINVAL
);
1157 assert_return(in_addr_is_link_local(AF_INET6
, (const union in_addr_union
*) &client
->local_address
) > 0, -EINVAL
);
1159 if (!IN_SET(client
->state
, DHCP6_STATE_STOPPED
))
1162 r
= client_reset(client
);
1166 r
= client_ensure_iaid(client
);
1170 r
= client_ensure_duid(client
);
1174 r
= dhcp6_network_bind_udp_socket(client
->index
, &client
->local_address
);
1180 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1181 client
->fd
, EPOLLIN
, client_receive_message
,
1186 r
= sd_event_source_set_priority(client
->receive_message
,
1187 client
->event_priority
);
1191 r
= sd_event_source_set_description(client
->receive_message
,
1192 "dhcp6-receive-message");
1196 if (client
->information_request
)
1197 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1199 log_dhcp6_client(client
, "Started in %s mode",
1200 client
->information_request
? "Information request":
1203 return client_start(client
, state
);
1206 client_reset(client
);
1210 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
, int priority
) {
1213 assert_return(client
, -EINVAL
);
1214 assert_return(!client
->event
, -EBUSY
);
1217 client
->event
= sd_event_ref(event
);
1219 r
= sd_event_default(&client
->event
);
1224 client
->event_priority
= priority
;
1229 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1230 assert_return(client
, -EINVAL
);
1232 client
->event
= sd_event_unref(client
->event
);
1237 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1241 return client
->event
;
1244 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1249 assert(client
->n_ref
>= 1);
1255 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1260 assert(client
->n_ref
>= 1);
1263 if (client
->n_ref
> 0)
1266 client_reset(client
);
1268 sd_dhcp6_client_detach_event(client
);
1270 free(client
->req_opts
);
1276 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
) {
1277 _cleanup_(sd_dhcp6_client_unrefp
) sd_dhcp6_client
*client
= NULL
;
1280 assert_return(ret
, -EINVAL
);
1282 client
= new0(sd_dhcp6_client
, 1);
1288 client
->ia_na
.type
= SD_DHCP6_OPTION_IA_NA
;
1294 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1296 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1297 if (!client
->req_opts
)
1300 for (t
= 0; t
< client
->req_opts_len
; t
++)
1301 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);