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"
31 #include "random-util.h"
33 #include "network-internal.h"
34 #include "sd-dhcp6-client.h"
35 #include "dhcp6-protocol.h"
36 #include "dhcp6-internal.h"
37 #include "dhcp6-lease-internal.h"
38 #include "dhcp-identifier.h"
40 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
42 struct sd_dhcp6_client
{
45 enum DHCP6State state
;
49 uint8_t mac_addr
[MAX_MAC_ADDR_LEN
];
53 be32_t transaction_id
;
54 usec_t transaction_start
;
55 struct sd_dhcp6_lease
*lease
;
57 bool information_request
;
59 size_t req_opts_allocated
;
61 sd_event_source
*receive_message
;
62 usec_t retransmit_time
;
63 uint8_t retransmit_count
;
64 sd_event_source
*timeout_resend
;
65 sd_event_source
*timeout_resend_expire
;
66 sd_dhcp6_client_cb_t cb
;
72 static const uint16_t default_req_opts
[] = {
73 DHCP6_OPTION_DNS_SERVERS
,
74 DHCP6_OPTION_DOMAIN_LIST
,
75 DHCP6_OPTION_NTP_SERVER
,
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
,
116 sd_dhcp6_client_cb_t cb
, void *userdata
)
118 assert_return(client
, -EINVAL
);
121 client
->userdata
= userdata
;
126 int sd_dhcp6_client_set_index(sd_dhcp6_client
*client
, int interface_index
)
128 assert_return(client
, -EINVAL
);
129 assert_return(interface_index
>= -1, -EINVAL
);
131 client
->index
= interface_index
;
136 int sd_dhcp6_client_set_mac(sd_dhcp6_client
*client
, const uint8_t *addr
,
137 size_t addr_len
, uint16_t arp_type
)
139 assert_return(client
, -EINVAL
);
140 assert_return(addr
, -EINVAL
);
141 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
142 assert_return(arp_type
> 0, -EINVAL
);
144 if (arp_type
== ARPHRD_ETHER
)
145 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
146 else if (arp_type
== ARPHRD_INFINIBAND
)
147 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
151 if (client
->mac_addr_len
== addr_len
&&
152 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
155 memcpy(&client
->mac_addr
, addr
, addr_len
);
156 client
->mac_addr_len
= addr_len
;
157 client
->arp_type
= arp_type
;
162 static int client_ensure_duid(sd_dhcp6_client
*client
)
164 if (client
->duid_len
!= 0)
166 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
169 int sd_dhcp6_client_set_duid(sd_dhcp6_client
*client
, uint16_t type
, uint8_t *duid
,
172 assert_return(client
, -EINVAL
);
173 assert_return(duid
, -EINVAL
);
174 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
178 if (duid_len
<= sizeof(client
->duid
.llt
))
182 if (duid_len
!= sizeof(client
->duid
.en
))
186 if (duid_len
<= sizeof(client
->duid
.ll
))
189 case DHCP6_DUID_UUID
:
190 if (duid_len
!= sizeof(client
->duid
.uuid
))
194 /* accept unknown type in order to be forward compatible */
198 client
->duid
.type
= htobe16(type
);
199 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
200 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
205 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
,
207 assert_return(client
, -EINVAL
);
209 client
->information_request
= enabled
;
214 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
,
216 assert_return(client
, -EINVAL
);
217 assert_return(enabled
, -EINVAL
);
219 *enabled
= client
->information_request
;
224 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
,
228 assert_return(client
, -EINVAL
);
229 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
232 case DHCP6_OPTION_DNS_SERVERS
:
233 case DHCP6_OPTION_DOMAIN_LIST
:
234 case DHCP6_OPTION_SNTP_SERVERS
:
235 case DHCP6_OPTION_NTP_SERVER
:
242 for (t
= 0; t
< client
->req_opts_len
; t
++)
243 if (client
->req_opts
[t
] == htobe16(option
))
246 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
247 client
->req_opts_len
+ 1))
250 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
255 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
256 assert_return(client
, -EINVAL
);
257 assert_return(ret
, -EINVAL
);
262 *ret
= sd_dhcp6_lease_ref(client
->lease
);
267 static void client_notify(sd_dhcp6_client
*client
, int event
) {
269 client
->cb(client
, event
, client
->userdata
);
272 static int client_reset(sd_dhcp6_client
*client
) {
273 assert_return(client
, -EINVAL
);
275 client
->receive_message
=
276 sd_event_source_unref(client
->receive_message
);
278 client
->fd
= safe_close(client
->fd
);
280 client
->transaction_id
= 0;
281 client
->transaction_start
= 0;
283 client
->ia_na
.timeout_t1
=
284 sd_event_source_unref(client
->ia_na
.timeout_t1
);
285 client
->ia_na
.timeout_t2
=
286 sd_event_source_unref(client
->ia_na
.timeout_t2
);
288 client
->retransmit_time
= 0;
289 client
->retransmit_count
= 0;
290 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
291 client
->timeout_resend_expire
=
292 sd_event_source_unref(client
->timeout_resend_expire
);
294 client
->state
= DHCP6_STATE_STOPPED
;
299 static void client_stop(sd_dhcp6_client
*client
, int error
) {
300 DHCP6_CLIENT_DONT_DESTROY(client
);
304 client_notify(client
, error
);
306 client_reset(client
);
309 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
310 _cleanup_free_ DHCP6Message
*message
= NULL
;
311 struct in6_addr all_servers
=
312 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
313 size_t len
, optlen
= 512;
319 len
= sizeof(DHCP6Message
) + optlen
;
321 message
= malloc0(len
);
325 opt
= (uint8_t *)(message
+ 1);
327 message
->transaction_id
= client
->transaction_id
;
329 switch(client
->state
) {
330 case DHCP6_STATE_INFORMATION_REQUEST
:
331 message
->type
= DHCP6_INFORMATION_REQUEST
;
335 case DHCP6_STATE_SOLICITATION
:
336 message
->type
= DHCP6_SOLICIT
;
338 r
= dhcp6_option_append(&opt
, &optlen
,
339 DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
343 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
349 case DHCP6_STATE_REQUEST
:
350 case DHCP6_STATE_RENEW
:
352 if (client
->state
== DHCP6_STATE_REQUEST
)
353 message
->type
= DHCP6_REQUEST
;
355 message
->type
= DHCP6_RENEW
;
357 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_SERVERID
,
358 client
->lease
->serverid_len
,
359 client
->lease
->serverid
);
363 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
369 case DHCP6_STATE_REBIND
:
370 message
->type
= DHCP6_REBIND
;
372 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
378 case DHCP6_STATE_STOPPED
:
379 case DHCP6_STATE_BOUND
:
383 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ORO
,
384 client
->req_opts_len
* sizeof(be16_t
),
389 assert (client
->duid_len
);
390 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_CLIENTID
,
391 client
->duid_len
, &client
->duid
);
395 elapsed_usec
= time_now
- client
->transaction_start
;
396 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
397 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
399 elapsed_time
= 0xffff;
401 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ELAPSED_TIME
,
402 sizeof(elapsed_time
), &elapsed_time
);
406 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
411 log_dhcp6_client(client
, "Sent %s",
412 dhcp6_message_type_to_string(message
->type
));
417 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
419 sd_dhcp6_client
*client
= userdata
;
421 assert_return(s
, -EINVAL
);
422 assert_return(client
, -EINVAL
);
423 assert_return(client
->lease
, -EINVAL
);
425 client
->lease
->ia
.timeout_t2
=
426 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
428 log_dhcp6_client(client
, "Timeout T2");
430 client_start(client
, DHCP6_STATE_REBIND
);
435 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
437 sd_dhcp6_client
*client
= userdata
;
439 assert_return(s
, -EINVAL
);
440 assert_return(client
, -EINVAL
);
441 assert_return(client
->lease
, -EINVAL
);
443 client
->lease
->ia
.timeout_t1
=
444 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
446 log_dhcp6_client(client
, "Timeout T1");
448 client_start(client
, DHCP6_STATE_RENEW
);
453 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
455 sd_dhcp6_client
*client
= userdata
;
456 DHCP6_CLIENT_DONT_DESTROY(client
);
457 enum DHCP6State state
;
461 assert(client
->event
);
463 state
= client
->state
;
465 client_stop(client
, DHCP6_EVENT_RESEND_EXPIRE
);
467 /* RFC 3315, section 18.1.4., says that "...the client may choose to
468 use a Solicit message to locate a new DHCP server..." */
469 if (state
== DHCP6_STATE_REBIND
)
470 client_start(client
, DHCP6_STATE_SOLICITATION
);
475 static usec_t
client_timeout_compute_random(usec_t val
) {
476 return val
- val
/ 10 +
477 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
480 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
483 sd_dhcp6_client
*client
= userdata
;
484 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
485 usec_t max_retransmit_duration
= 0;
486 uint8_t max_retransmit_count
= 0;
487 char time_string
[FORMAT_TIMESPAN_MAX
];
492 assert(client
->event
);
494 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
496 switch (client
->state
) {
497 case DHCP6_STATE_INFORMATION_REQUEST
:
498 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
499 max_retransmit_time
= DHCP6_INF_MAX_RT
;
503 case DHCP6_STATE_SOLICITATION
:
505 if (client
->retransmit_count
&& client
->lease
) {
506 client_start(client
, DHCP6_STATE_REQUEST
);
510 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
511 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
515 case DHCP6_STATE_REQUEST
:
516 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
517 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
518 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
522 case DHCP6_STATE_RENEW
:
523 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
524 max_retransmit_time
= DHCP6_REN_MAX_RT
;
526 /* RFC 3315, section 18.1.3. says max retransmit duration will
527 be the remaining time until T2. Instead of setting MRD,
528 wait for T2 to trigger with the same end result */
532 case DHCP6_STATE_REBIND
:
533 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
534 max_retransmit_time
= DHCP6_REB_MAX_RT
;
536 if (!client
->timeout_resend_expire
) {
537 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
540 client_stop(client
, r
);
543 max_retransmit_duration
= expire
* USEC_PER_SEC
;
548 case DHCP6_STATE_STOPPED
:
549 case DHCP6_STATE_BOUND
:
553 if (max_retransmit_count
&&
554 client
->retransmit_count
>= max_retransmit_count
) {
555 client_stop(client
, DHCP6_EVENT_RETRANS_MAX
);
559 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
563 r
= client_send_message(client
, time_now
);
565 client
->retransmit_count
++;
567 if (!client
->retransmit_time
) {
568 client
->retransmit_time
=
569 client_timeout_compute_random(init_retransmit_time
);
571 if (client
->state
== DHCP6_STATE_SOLICITATION
)
572 client
->retransmit_time
+= init_retransmit_time
/ 10;
575 if (max_retransmit_time
&&
576 client
->retransmit_time
> max_retransmit_time
/ 2)
577 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
579 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
582 log_dhcp6_client(client
, "Next retransmission in %s",
583 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
,
584 client
->retransmit_time
, 0));
586 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
587 clock_boottime_or_monotonic(),
588 time_now
+ client
->retransmit_time
,
589 10 * USEC_PER_MSEC
, client_timeout_resend
,
594 r
= sd_event_source_set_priority(client
->timeout_resend
,
595 client
->event_priority
);
599 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
603 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
605 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
606 max_retransmit_duration
/ USEC_PER_SEC
);
608 r
= sd_event_add_time(client
->event
,
609 &client
->timeout_resend_expire
,
610 clock_boottime_or_monotonic(),
611 time_now
+ max_retransmit_duration
,
613 client_timeout_resend_expire
, client
);
617 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
618 client
->event_priority
);
622 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
629 client_stop(client
, r
);
634 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
639 if (client
->ia_na
.id
)
642 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
649 static int client_parse_message(sd_dhcp6_client
*client
,
650 DHCP6Message
*message
, size_t len
,
651 sd_dhcp6_lease
*lease
) {
653 uint8_t *optval
, *option
, *id
= NULL
;
654 uint16_t optcode
, status
;
655 size_t optlen
, id_len
;
656 bool clientid
= false;
659 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
660 len
-= sizeof(DHCP6Message
);
662 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
665 case DHCP6_OPTION_CLIENTID
:
667 log_dhcp6_client(client
, "%s contains multiple clientids",
668 dhcp6_message_type_to_string(message
->type
));
672 if (optlen
!= client
->duid_len
||
673 memcmp(&client
->duid
, optval
, optlen
) != 0) {
674 log_dhcp6_client(client
, "%s DUID does not match",
675 dhcp6_message_type_to_string(message
->type
));
683 case DHCP6_OPTION_SERVERID
:
684 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
686 log_dhcp6_client(client
, "%s contains multiple serverids",
687 dhcp6_message_type_to_string(message
->type
));
691 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
697 case DHCP6_OPTION_PREFERENCE
:
701 r
= dhcp6_lease_set_preference(lease
, *optval
);
707 case DHCP6_OPTION_STATUS_CODE
:
711 status
= optval
[0] << 8 | optval
[1];
713 log_dhcp6_client(client
, "%s Status %s",
714 dhcp6_message_type_to_string(message
->type
),
715 dhcp6_message_status_to_string(status
));
721 case DHCP6_OPTION_IA_NA
:
722 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
723 log_dhcp6_client(client
, "Information request ignoring IA NA option");
728 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
730 if (r
< 0 && r
!= -ENOMSG
)
733 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
737 if (client
->ia_na
.id
!= iaid_lease
) {
738 log_dhcp6_client(client
, "%s has wrong IAID",
739 dhcp6_message_type_to_string(message
->type
));
745 case DHCP6_OPTION_RAPID_COMMIT
:
746 r
= dhcp6_lease_set_rapid_commit(lease
);
757 if (r
< 0 || !clientid
) {
758 log_dhcp6_client(client
, "%s has incomplete options",
759 dhcp6_message_type_to_string(message
->type
));
763 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
764 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
766 log_dhcp6_client(client
, "%s has no server id",
767 dhcp6_message_type_to_string(message
->type
));
773 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
,
777 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
780 if (reply
->type
!= DHCP6_REPLY
)
783 r
= dhcp6_lease_new(&lease
);
787 r
= client_parse_message(client
, reply
, len
, lease
);
791 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
792 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
801 dhcp6_lease_clear_timers(&client
->lease
->ia
);
802 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
805 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
806 client
->lease
= lease
;
810 return DHCP6_STATE_BOUND
;
813 static int client_receive_advertise(sd_dhcp6_client
*client
,
814 DHCP6Message
*advertise
, size_t len
) {
816 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
817 uint8_t pref_advertise
= 0, pref_lease
= 0;
819 if (advertise
->type
!= DHCP6_ADVERTISE
)
822 r
= dhcp6_lease_new(&lease
);
826 r
= client_parse_message(client
, advertise
, len
, lease
);
830 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
834 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
836 if (r
< 0 || pref_advertise
> pref_lease
) {
837 sd_dhcp6_lease_unref(client
->lease
);
838 client
->lease
= lease
;
843 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
844 r
= DHCP6_STATE_REQUEST
;
849 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
,
851 sd_dhcp6_client
*client
= userdata
;
852 DHCP6_CLIENT_DONT_DESTROY(client
);
853 _cleanup_free_ DHCP6Message
*message
;
858 assert(client
->event
);
860 r
= ioctl(fd
, FIONREAD
, &buflen
);
861 if (r
< 0 || buflen
<= 0)
862 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
864 message
= malloc0(buflen
);
868 len
= read(fd
, message
, buflen
);
869 if ((size_t)len
< sizeof(DHCP6Message
)) {
870 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
874 switch(message
->type
) {
882 case DHCP6_INFORMATION_REQUEST
:
883 case DHCP6_RELAY_FORW
:
884 case DHCP6_RELAY_REPL
:
887 case DHCP6_ADVERTISE
:
889 case DHCP6_RECONFIGURE
:
893 log_dhcp6_client(client
, "unknown message type %d",
898 if (client
->transaction_id
!= (message
->transaction_id
&
899 htobe32(0x00ffffff)))
902 switch (client
->state
) {
903 case DHCP6_STATE_INFORMATION_REQUEST
:
904 r
= client_receive_reply(client
, message
, len
);
908 client_notify(client
, DHCP6_EVENT_INFORMATION_REQUEST
);
910 client_start(client
, DHCP6_STATE_STOPPED
);
914 case DHCP6_STATE_SOLICITATION
:
915 r
= client_receive_advertise(client
, message
, len
);
917 if (r
== DHCP6_STATE_REQUEST
) {
918 client_start(client
, r
);
923 /* fall through for Soliciation Rapid Commit option check */
924 case DHCP6_STATE_REQUEST
:
925 case DHCP6_STATE_RENEW
:
926 case DHCP6_STATE_REBIND
:
928 r
= client_receive_reply(client
, message
, len
);
932 if (r
== DHCP6_STATE_BOUND
) {
934 r
= client_start(client
, DHCP6_STATE_BOUND
);
936 client_stop(client
, r
);
940 client_notify(client
, DHCP6_EVENT_IP_ACQUIRE
);
945 case DHCP6_STATE_BOUND
:
949 case DHCP6_STATE_STOPPED
:
954 log_dhcp6_client(client
, "Recv %s",
955 dhcp6_message_type_to_string(message
->type
));
961 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
)
964 usec_t timeout
, time_now
;
965 char time_string
[FORMAT_TIMESPAN_MAX
];
967 assert_return(client
, -EINVAL
);
968 assert_return(client
->event
, -EINVAL
);
969 assert_return(client
->index
> 0, -EINVAL
);
970 assert_return(client
->state
!= state
, -EINVAL
);
972 client
->timeout_resend_expire
=
973 sd_event_source_unref(client
->timeout_resend_expire
);
974 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
975 client
->retransmit_time
= 0;
976 client
->retransmit_count
= 0;
978 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
983 case DHCP6_STATE_STOPPED
:
984 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
985 client
->state
= DHCP6_STATE_STOPPED
;
991 case DHCP6_STATE_SOLICITATION
:
992 client
->state
= DHCP6_STATE_SOLICITATION
;
996 case DHCP6_STATE_INFORMATION_REQUEST
:
997 case DHCP6_STATE_REQUEST
:
998 case DHCP6_STATE_RENEW
:
999 case DHCP6_STATE_REBIND
:
1001 client
->state
= state
;
1005 case DHCP6_STATE_BOUND
:
1007 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1008 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1010 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1011 be32toh(client
->lease
->ia
.lifetime_t1
),
1012 be32toh(client
->lease
->ia
.lifetime_t2
));
1017 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1019 log_dhcp6_client(client
, "T1 expires in %s",
1020 format_timespan(time_string
,
1021 FORMAT_TIMESPAN_MAX
,
1024 r
= sd_event_add_time(client
->event
,
1025 &client
->lease
->ia
.timeout_t1
,
1026 clock_boottime_or_monotonic(), time_now
+ timeout
,
1027 10 * USEC_PER_SEC
, client_timeout_t1
,
1032 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1033 client
->event_priority
);
1037 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1041 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1043 log_dhcp6_client(client
, "T2 expires in %s",
1044 format_timespan(time_string
,
1045 FORMAT_TIMESPAN_MAX
,
1048 r
= sd_event_add_time(client
->event
,
1049 &client
->lease
->ia
.timeout_t2
,
1050 clock_boottime_or_monotonic(), time_now
+ timeout
,
1051 10 * USEC_PER_SEC
, client_timeout_t2
,
1056 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1057 client
->event_priority
);
1061 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1065 client
->state
= state
;
1070 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1071 client
->transaction_start
= time_now
;
1073 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1074 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1079 r
= sd_event_source_set_priority(client
->timeout_resend
,
1080 client
->event_priority
);
1084 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1091 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
)
1093 client_stop(client
, DHCP6_EVENT_STOP
);
1098 int sd_dhcp6_client_start(sd_dhcp6_client
*client
)
1101 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1103 assert_return(client
, -EINVAL
);
1104 assert_return(client
->event
, -EINVAL
);
1105 assert_return(client
->index
> 0, -EINVAL
);
1107 r
= client_reset(client
);
1111 r
= client_ensure_iaid(client
);
1115 r
= client_ensure_duid(client
);
1119 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1125 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1126 client
->fd
, EPOLLIN
, client_receive_message
,
1131 r
= sd_event_source_set_priority(client
->receive_message
,
1132 client
->event_priority
);
1136 r
= sd_event_source_set_description(client
->receive_message
,
1137 "dhcp6-receive-message");
1141 if (client
->information_request
)
1142 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1144 log_dhcp6_client(client
, "Started in %s mode",
1145 client
->information_request
? "Information request":
1148 return client_start(client
, state
);
1151 client_reset(client
);
1155 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
,
1160 assert_return(client
, -EINVAL
);
1161 assert_return(!client
->event
, -EBUSY
);
1164 client
->event
= sd_event_ref(event
);
1166 r
= sd_event_default(&client
->event
);
1171 client
->event_priority
= priority
;
1176 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1177 assert_return(client
, -EINVAL
);
1179 client
->event
= sd_event_unref(client
->event
);
1184 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1188 return client
->event
;
1191 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1193 assert_se(REFCNT_INC(client
->n_ref
) >= 2);
1198 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1199 if (client
&& REFCNT_DEC(client
->n_ref
) == 0) {
1200 client_reset(client
);
1202 sd_dhcp6_client_detach_event(client
);
1203 sd_dhcp6_lease_unref(client
->lease
);
1205 free(client
->req_opts
);
1214 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
)
1216 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1219 assert_return(ret
, -EINVAL
);
1221 client
= new0(sd_dhcp6_client
, 1);
1225 client
->n_ref
= REFCNT_INIT
;
1227 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1233 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1235 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1236 if (!client
->req_opts
)
1239 for (t
= 0; t
< client
->req_opts_len
; t
++)
1240 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);