1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 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"
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
,
77 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
78 [DHCP6_SOLICIT
] = "SOLICIT",
79 [DHCP6_ADVERTISE
] = "ADVERTISE",
80 [DHCP6_REQUEST
] = "REQUEST",
81 [DHCP6_CONFIRM
] = "CONFIRM",
82 [DHCP6_RENEW
] = "RENEW",
83 [DHCP6_REBIND
] = "REBIND",
84 [DHCP6_REPLY
] = "REPLY",
85 [DHCP6_RELEASE
] = "RELEASE",
86 [DHCP6_DECLINE
] = "DECLINE",
87 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
88 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
89 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
90 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
93 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
95 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
96 [DHCP6_STATUS_SUCCESS
] = "Success",
97 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
98 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
99 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
100 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
101 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
104 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
106 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client
*, sd_dhcp6_client_unref
);
107 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
109 #define DHCP6_CLIENT_DONT_DESTROY(client) \
110 _cleanup_dhcp6_client_unref_ _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
,
115 sd_dhcp6_client_cb_t cb
, void *userdata
)
117 assert_return(client
, -EINVAL
);
120 client
->userdata
= userdata
;
125 int sd_dhcp6_client_set_index(sd_dhcp6_client
*client
, int interface_index
)
127 assert_return(client
, -EINVAL
);
128 assert_return(interface_index
>= -1, -EINVAL
);
130 client
->index
= interface_index
;
135 int sd_dhcp6_client_set_mac(sd_dhcp6_client
*client
, const uint8_t *addr
,
136 size_t addr_len
, uint16_t arp_type
)
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 int sd_dhcp6_client_set_duid(sd_dhcp6_client
*client
, uint16_t type
, uint8_t *duid
,
164 assert_return(client
, -EINVAL
);
165 assert_return(duid
, -EINVAL
);
166 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
170 if (duid_len
<= sizeof(client
->duid
.llt
))
174 if (duid_len
!= sizeof(client
->duid
.en
))
178 if (duid_len
<= sizeof(client
->duid
.ll
))
181 case DHCP6_DUID_UUID
:
182 if (duid_len
!= sizeof(client
->duid
.uuid
))
186 /* accept unknown type in order to be forward compatible */
190 client
->duid
.type
= htobe16(type
);
191 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
192 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
197 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
,
199 assert_return(client
, -EINVAL
);
201 client
->information_request
= enabled
;
206 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
,
208 assert_return(client
, -EINVAL
);
209 assert_return(enabled
, -EINVAL
);
211 *enabled
= client
->information_request
;
216 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
,
220 assert_return(client
, -EINVAL
);
221 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
224 case DHCP6_OPTION_DNS_SERVERS
:
225 case DHCP6_OPTION_DOMAIN_LIST
:
226 case DHCP6_OPTION_SNTP_SERVERS
:
227 case DHCP6_OPTION_NTP_SERVER
:
234 for (t
= 0; t
< client
->req_opts_len
; t
++)
235 if (client
->req_opts
[t
] == htobe16(option
))
238 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
239 client
->req_opts_len
+ 1))
242 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
247 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
248 assert_return(client
, -EINVAL
);
249 assert_return(ret
, -EINVAL
);
254 *ret
= sd_dhcp6_lease_ref(client
->lease
);
259 static void client_notify(sd_dhcp6_client
*client
, int event
) {
261 client
->cb(client
, event
, client
->userdata
);
264 static int client_reset(sd_dhcp6_client
*client
) {
265 assert_return(client
, -EINVAL
);
267 client
->receive_message
=
268 sd_event_source_unref(client
->receive_message
);
270 client
->fd
= safe_close(client
->fd
);
272 client
->transaction_id
= 0;
273 client
->transaction_start
= 0;
275 client
->ia_na
.timeout_t1
=
276 sd_event_source_unref(client
->ia_na
.timeout_t1
);
277 client
->ia_na
.timeout_t2
=
278 sd_event_source_unref(client
->ia_na
.timeout_t2
);
280 client
->retransmit_time
= 0;
281 client
->retransmit_count
= 0;
282 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
283 client
->timeout_resend_expire
=
284 sd_event_source_unref(client
->timeout_resend_expire
);
286 client
->state
= DHCP6_STATE_STOPPED
;
291 static void client_stop(sd_dhcp6_client
*client
, int error
) {
292 DHCP6_CLIENT_DONT_DESTROY(client
);
296 client_notify(client
, error
);
298 client_reset(client
);
301 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
302 _cleanup_free_ DHCP6Message
*message
= NULL
;
303 struct in6_addr all_servers
=
304 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
305 size_t len
, optlen
= 512;
311 len
= sizeof(DHCP6Message
) + optlen
;
313 message
= malloc0(len
);
317 opt
= (uint8_t *)(message
+ 1);
319 message
->transaction_id
= client
->transaction_id
;
321 switch(client
->state
) {
322 case DHCP6_STATE_INFORMATION_REQUEST
:
323 message
->type
= DHCP6_INFORMATION_REQUEST
;
327 case DHCP6_STATE_SOLICITATION
:
328 message
->type
= DHCP6_SOLICIT
;
330 r
= dhcp6_option_append(&opt
, &optlen
,
331 DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
335 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
341 case DHCP6_STATE_REQUEST
:
342 case DHCP6_STATE_RENEW
:
344 if (client
->state
== DHCP6_STATE_REQUEST
)
345 message
->type
= DHCP6_REQUEST
;
347 message
->type
= DHCP6_RENEW
;
349 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_SERVERID
,
350 client
->lease
->serverid_len
,
351 client
->lease
->serverid
);
355 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
361 case DHCP6_STATE_REBIND
:
362 message
->type
= DHCP6_REBIND
;
364 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
370 case DHCP6_STATE_STOPPED
:
371 case DHCP6_STATE_BOUND
:
375 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ORO
,
376 client
->req_opts_len
* sizeof(be16_t
),
381 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_CLIENTID
,
382 client
->duid_len
, &client
->duid
);
386 elapsed_usec
= time_now
- client
->transaction_start
;
387 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
388 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
390 elapsed_time
= 0xffff;
392 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ELAPSED_TIME
,
393 sizeof(elapsed_time
), &elapsed_time
);
397 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
402 log_dhcp6_client(client
, "Sent %s",
403 dhcp6_message_type_to_string(message
->type
));
408 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
410 sd_dhcp6_client
*client
= userdata
;
412 assert_return(s
, -EINVAL
);
413 assert_return(client
, -EINVAL
);
414 assert_return(client
->lease
, -EINVAL
);
416 client
->lease
->ia
.timeout_t2
=
417 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
419 log_dhcp6_client(client
, "Timeout T2");
421 client_start(client
, DHCP6_STATE_REBIND
);
426 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
428 sd_dhcp6_client
*client
= userdata
;
430 assert_return(s
, -EINVAL
);
431 assert_return(client
, -EINVAL
);
432 assert_return(client
->lease
, -EINVAL
);
434 client
->lease
->ia
.timeout_t1
=
435 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
437 log_dhcp6_client(client
, "Timeout T1");
439 client_start(client
, DHCP6_STATE_RENEW
);
444 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
446 sd_dhcp6_client
*client
= userdata
;
447 DHCP6_CLIENT_DONT_DESTROY(client
);
448 enum DHCP6State state
;
452 assert(client
->event
);
454 state
= client
->state
;
456 client_stop(client
, DHCP6_EVENT_RESEND_EXPIRE
);
458 /* RFC 3315, section 18.1.4., says that "...the client may choose to
459 use a Solicit message to locate a new DHCP server..." */
460 if (state
== DHCP6_STATE_REBIND
)
461 client_start(client
, DHCP6_STATE_SOLICITATION
);
466 static usec_t
client_timeout_compute_random(usec_t val
) {
467 return val
- val
/ 10 +
468 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
471 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
474 sd_dhcp6_client
*client
= userdata
;
475 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
476 usec_t max_retransmit_duration
= 0;
477 uint8_t max_retransmit_count
= 0;
478 char time_string
[FORMAT_TIMESPAN_MAX
];
483 assert(client
->event
);
485 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
487 switch (client
->state
) {
488 case DHCP6_STATE_INFORMATION_REQUEST
:
489 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
490 max_retransmit_time
= DHCP6_INF_MAX_RT
;
494 case DHCP6_STATE_SOLICITATION
:
496 if (client
->retransmit_count
&& client
->lease
) {
497 client_start(client
, DHCP6_STATE_REQUEST
);
501 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
502 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
506 case DHCP6_STATE_REQUEST
:
507 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
508 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
509 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
513 case DHCP6_STATE_RENEW
:
514 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
515 max_retransmit_time
= DHCP6_REN_MAX_RT
;
517 /* RFC 3315, section 18.1.3. says max retransmit duration will
518 be the remaining time until T2. Instead of setting MRD,
519 wait for T2 to trigger with the same end result */
523 case DHCP6_STATE_REBIND
:
524 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
525 max_retransmit_time
= DHCP6_REB_MAX_RT
;
527 if (!client
->timeout_resend_expire
) {
528 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
531 client_stop(client
, r
);
534 max_retransmit_duration
= expire
* USEC_PER_SEC
;
539 case DHCP6_STATE_STOPPED
:
540 case DHCP6_STATE_BOUND
:
544 if (max_retransmit_count
&&
545 client
->retransmit_count
>= max_retransmit_count
) {
546 client_stop(client
, DHCP6_EVENT_RETRANS_MAX
);
550 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
554 r
= client_send_message(client
, time_now
);
556 client
->retransmit_count
++;
558 if (!client
->retransmit_time
) {
559 client
->retransmit_time
=
560 client_timeout_compute_random(init_retransmit_time
);
562 if (client
->state
== DHCP6_STATE_SOLICITATION
)
563 client
->retransmit_time
+= init_retransmit_time
/ 10;
566 if (max_retransmit_time
&&
567 client
->retransmit_time
> max_retransmit_time
/ 2)
568 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
570 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
573 log_dhcp6_client(client
, "Next retransmission in %s",
574 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
,
575 client
->retransmit_time
, 0));
577 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
578 clock_boottime_or_monotonic(),
579 time_now
+ client
->retransmit_time
,
580 10 * USEC_PER_MSEC
, client_timeout_resend
,
585 r
= sd_event_source_set_priority(client
->timeout_resend
,
586 client
->event_priority
);
590 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
594 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
596 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
597 max_retransmit_duration
/ USEC_PER_SEC
);
599 r
= sd_event_add_time(client
->event
,
600 &client
->timeout_resend_expire
,
601 clock_boottime_or_monotonic(),
602 time_now
+ max_retransmit_duration
,
604 client_timeout_resend_expire
, client
);
608 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
609 client
->event_priority
);
613 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
620 client_stop(client
, r
);
625 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
630 if (client
->ia_na
.id
)
633 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
640 static int client_parse_message(sd_dhcp6_client
*client
,
641 DHCP6Message
*message
, size_t len
,
642 sd_dhcp6_lease
*lease
) {
644 uint8_t *optval
, *option
, *id
= NULL
;
645 uint16_t optcode
, status
;
646 size_t optlen
, id_len
;
647 bool clientid
= false;
650 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
651 len
-= sizeof(DHCP6Message
);
653 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
656 case DHCP6_OPTION_CLIENTID
:
658 log_dhcp6_client(client
, "%s contains multiple clientids",
659 dhcp6_message_type_to_string(message
->type
));
663 if (optlen
!= client
->duid_len
||
664 memcmp(&client
->duid
, optval
, optlen
) != 0) {
665 log_dhcp6_client(client
, "%s DUID does not match",
666 dhcp6_message_type_to_string(message
->type
));
674 case DHCP6_OPTION_SERVERID
:
675 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
677 log_dhcp6_client(client
, "%s contains multiple serverids",
678 dhcp6_message_type_to_string(message
->type
));
682 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
688 case DHCP6_OPTION_PREFERENCE
:
692 r
= dhcp6_lease_set_preference(lease
, *optval
);
698 case DHCP6_OPTION_STATUS_CODE
:
702 status
= optval
[0] << 8 | optval
[1];
704 log_dhcp6_client(client
, "%s Status %s",
705 dhcp6_message_type_to_string(message
->type
),
706 dhcp6_message_status_to_string(status
));
712 case DHCP6_OPTION_IA_NA
:
713 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
714 log_dhcp6_client(client
, "Information request ignoring IA NA option");
719 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
721 if (r
< 0 && r
!= -ENOMSG
)
724 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
728 if (client
->ia_na
.id
!= iaid_lease
) {
729 log_dhcp6_client(client
, "%s has wrong IAID",
730 dhcp6_message_type_to_string(message
->type
));
736 case DHCP6_OPTION_RAPID_COMMIT
:
737 r
= dhcp6_lease_set_rapid_commit(lease
);
748 if (r
< 0 || !clientid
) {
749 log_dhcp6_client(client
, "%s has incomplete options",
750 dhcp6_message_type_to_string(message
->type
));
754 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
755 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
757 log_dhcp6_client(client
, "%s has no server id",
758 dhcp6_message_type_to_string(message
->type
));
764 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
,
768 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
771 if (reply
->type
!= DHCP6_REPLY
)
774 r
= dhcp6_lease_new(&lease
);
778 r
= client_parse_message(client
, reply
, len
, lease
);
782 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
783 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
792 dhcp6_lease_clear_timers(&client
->lease
->ia
);
793 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
796 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
797 client
->lease
= lease
;
801 return DHCP6_STATE_BOUND
;
804 static int client_receive_advertise(sd_dhcp6_client
*client
,
805 DHCP6Message
*advertise
, size_t len
) {
807 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
808 uint8_t pref_advertise
= 0, pref_lease
= 0;
810 if (advertise
->type
!= DHCP6_ADVERTISE
)
813 r
= dhcp6_lease_new(&lease
);
817 r
= client_parse_message(client
, advertise
, len
, lease
);
821 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
825 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
827 if (r
< 0 || pref_advertise
> pref_lease
) {
828 sd_dhcp6_lease_unref(client
->lease
);
829 client
->lease
= lease
;
834 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
835 r
= DHCP6_STATE_REQUEST
;
840 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
,
842 sd_dhcp6_client
*client
= userdata
;
843 DHCP6_CLIENT_DONT_DESTROY(client
);
844 _cleanup_free_ DHCP6Message
*message
;
849 assert(client
->event
);
851 r
= ioctl(fd
, FIONREAD
, &buflen
);
852 if (r
< 0 || buflen
<= 0)
853 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
855 message
= malloc0(buflen
);
859 len
= read(fd
, message
, buflen
);
860 if ((size_t)len
< sizeof(DHCP6Message
)) {
861 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
865 switch(message
->type
) {
873 case DHCP6_INFORMATION_REQUEST
:
874 case DHCP6_RELAY_FORW
:
875 case DHCP6_RELAY_REPL
:
878 case DHCP6_ADVERTISE
:
880 case DHCP6_RECONFIGURE
:
884 log_dhcp6_client(client
, "unknown message type %d",
889 if (client
->transaction_id
!= (message
->transaction_id
&
890 htobe32(0x00ffffff)))
893 switch (client
->state
) {
894 case DHCP6_STATE_INFORMATION_REQUEST
:
895 r
= client_receive_reply(client
, message
, len
);
899 client_notify(client
, DHCP6_EVENT_INFORMATION_REQUEST
);
901 client_start(client
, DHCP6_STATE_STOPPED
);
905 case DHCP6_STATE_SOLICITATION
:
906 r
= client_receive_advertise(client
, message
, len
);
908 if (r
== DHCP6_STATE_REQUEST
) {
909 client_start(client
, r
);
914 /* fall through for Soliciation Rapid Commit option check */
915 case DHCP6_STATE_REQUEST
:
916 case DHCP6_STATE_RENEW
:
917 case DHCP6_STATE_REBIND
:
919 r
= client_receive_reply(client
, message
, len
);
923 if (r
== DHCP6_STATE_BOUND
) {
925 r
= client_start(client
, DHCP6_STATE_BOUND
);
927 client_stop(client
, r
);
931 client_notify(client
, DHCP6_EVENT_IP_ACQUIRE
);
936 case DHCP6_STATE_BOUND
:
940 case DHCP6_STATE_STOPPED
:
945 log_dhcp6_client(client
, "Recv %s",
946 dhcp6_message_type_to_string(message
->type
));
952 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
)
955 usec_t timeout
, time_now
;
956 char time_string
[FORMAT_TIMESPAN_MAX
];
958 assert_return(client
, -EINVAL
);
959 assert_return(client
->event
, -EINVAL
);
960 assert_return(client
->index
> 0, -EINVAL
);
961 assert_return(client
->state
!= state
, -EINVAL
);
963 client
->timeout_resend_expire
=
964 sd_event_source_unref(client
->timeout_resend_expire
);
965 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
966 client
->retransmit_time
= 0;
967 client
->retransmit_count
= 0;
969 if (client
->state
== DHCP6_STATE_STOPPED
) {
970 time_now
= now(clock_boottime_or_monotonic());
972 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(),
979 case DHCP6_STATE_STOPPED
:
980 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
981 client
->state
= DHCP6_STATE_STOPPED
;
987 case DHCP6_STATE_SOLICITATION
:
988 client
->state
= DHCP6_STATE_SOLICITATION
;
992 case DHCP6_STATE_INFORMATION_REQUEST
:
993 case DHCP6_STATE_REQUEST
:
994 case DHCP6_STATE_RENEW
:
995 case DHCP6_STATE_REBIND
:
997 client
->state
= state
;
1001 case DHCP6_STATE_BOUND
:
1003 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1004 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1006 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1007 be32toh(client
->lease
->ia
.lifetime_t1
),
1008 be32toh(client
->lease
->ia
.lifetime_t2
));
1013 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1015 log_dhcp6_client(client
, "T1 expires in %s",
1016 format_timespan(time_string
,
1017 FORMAT_TIMESPAN_MAX
,
1020 r
= sd_event_add_time(client
->event
,
1021 &client
->lease
->ia
.timeout_t1
,
1022 clock_boottime_or_monotonic(), time_now
+ timeout
,
1023 10 * USEC_PER_SEC
, client_timeout_t1
,
1028 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1029 client
->event_priority
);
1033 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1037 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1039 log_dhcp6_client(client
, "T2 expires in %s",
1040 format_timespan(time_string
,
1041 FORMAT_TIMESPAN_MAX
,
1044 r
= sd_event_add_time(client
->event
,
1045 &client
->lease
->ia
.timeout_t2
,
1046 clock_boottime_or_monotonic(), time_now
+ timeout
,
1047 10 * USEC_PER_SEC
, client_timeout_t2
,
1052 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1053 client
->event_priority
);
1057 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1061 client
->state
= state
;
1066 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1067 client
->transaction_start
= time_now
;
1069 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1070 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1075 r
= sd_event_source_set_priority(client
->timeout_resend
,
1076 client
->event_priority
);
1080 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1087 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
)
1089 client_stop(client
, DHCP6_EVENT_STOP
);
1094 int sd_dhcp6_client_start(sd_dhcp6_client
*client
)
1097 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1099 assert_return(client
, -EINVAL
);
1100 assert_return(client
->event
, -EINVAL
);
1101 assert_return(client
->index
> 0, -EINVAL
);
1103 r
= client_reset(client
);
1107 r
= client_ensure_iaid(client
);
1111 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1117 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1118 client
->fd
, EPOLLIN
, client_receive_message
,
1123 r
= sd_event_source_set_priority(client
->receive_message
,
1124 client
->event_priority
);
1128 r
= sd_event_source_set_description(client
->receive_message
,
1129 "dhcp6-receive-message");
1133 if (client
->information_request
)
1134 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1136 log_dhcp6_client(client
, "Started in %s mode",
1137 client
->information_request
? "Information request":
1140 return client_start(client
, state
);
1143 client_reset(client
);
1147 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
,
1152 assert_return(client
, -EINVAL
);
1153 assert_return(!client
->event
, -EBUSY
);
1156 client
->event
= sd_event_ref(event
);
1158 r
= sd_event_default(&client
->event
);
1163 client
->event_priority
= priority
;
1168 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1169 assert_return(client
, -EINVAL
);
1171 client
->event
= sd_event_unref(client
->event
);
1176 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1180 return client
->event
;
1183 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1185 assert_se(REFCNT_INC(client
->n_ref
) >= 2);
1190 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1191 if (client
&& REFCNT_DEC(client
->n_ref
) == 0) {
1192 client_reset(client
);
1194 sd_dhcp6_client_detach_event(client
);
1196 free(client
->req_opts
);
1205 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
)
1207 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1211 assert_return(ret
, -EINVAL
);
1213 client
= new0(sd_dhcp6_client
, 1);
1217 client
->n_ref
= REFCNT_INIT
;
1219 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1225 /* initialize DUID */
1226 r
= dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
1230 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1232 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1233 if (!client
->req_opts
)
1236 for (t
= 0; t
< client
->req_opts_len
; t
++)
1237 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);