1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/ioctl.h>
25 #include <linux/if_infiniband.h>
28 #include "udev-util.h"
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
,
76 DHCP6_OPTION_SNTP_SERVERS
,
79 const char * dhcp6_message_type_table
[_DHCP6_MESSAGE_MAX
] = {
80 [DHCP6_SOLICIT
] = "SOLICIT",
81 [DHCP6_ADVERTISE
] = "ADVERTISE",
82 [DHCP6_REQUEST
] = "REQUEST",
83 [DHCP6_CONFIRM
] = "CONFIRM",
84 [DHCP6_RENEW
] = "RENEW",
85 [DHCP6_REBIND
] = "REBIND",
86 [DHCP6_REPLY
] = "REPLY",
87 [DHCP6_RELEASE
] = "RELEASE",
88 [DHCP6_DECLINE
] = "DECLINE",
89 [DHCP6_RECONFIGURE
] = "RECONFIGURE",
90 [DHCP6_INFORMATION_REQUEST
] = "INFORMATION-REQUEST",
91 [DHCP6_RELAY_FORW
] = "RELAY-FORW",
92 [DHCP6_RELAY_REPL
] = "RELAY-REPL",
95 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type
, int);
97 const char * dhcp6_message_status_table
[_DHCP6_STATUS_MAX
] = {
98 [DHCP6_STATUS_SUCCESS
] = "Success",
99 [DHCP6_STATUS_UNSPEC_FAIL
] = "Unspecified failure",
100 [DHCP6_STATUS_NO_ADDRS_AVAIL
] = "No addresses available",
101 [DHCP6_STATUS_NO_BINDING
] = "Binding unavailable",
102 [DHCP6_STATUS_NOT_ON_LINK
] = "Not on link",
103 [DHCP6_STATUS_USE_MULTICAST
] = "Use multicast",
106 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status
, int);
108 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client
*, sd_dhcp6_client_unref
);
109 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
111 #define DHCP6_CLIENT_DONT_DESTROY(client) \
112 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
114 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
);
116 int sd_dhcp6_client_set_callback(sd_dhcp6_client
*client
,
117 sd_dhcp6_client_cb_t cb
, void *userdata
)
119 assert_return(client
, -EINVAL
);
122 client
->userdata
= userdata
;
127 int sd_dhcp6_client_set_index(sd_dhcp6_client
*client
, int interface_index
)
129 assert_return(client
, -EINVAL
);
130 assert_return(interface_index
>= -1, -EINVAL
);
132 client
->index
= interface_index
;
137 int sd_dhcp6_client_set_mac(sd_dhcp6_client
*client
, const uint8_t *addr
,
138 size_t addr_len
, uint16_t arp_type
)
140 assert_return(client
, -EINVAL
);
141 assert_return(addr
, -EINVAL
);
142 assert_return(addr_len
> 0 && addr_len
<= MAX_MAC_ADDR_LEN
, -EINVAL
);
143 assert_return(arp_type
> 0, -EINVAL
);
145 if (arp_type
== ARPHRD_ETHER
)
146 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
147 else if (arp_type
== ARPHRD_INFINIBAND
)
148 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
152 if (client
->mac_addr_len
== addr_len
&&
153 memcmp(&client
->mac_addr
, addr
, addr_len
) == 0)
156 memcpy(&client
->mac_addr
, addr
, addr_len
);
157 client
->mac_addr_len
= addr_len
;
158 client
->arp_type
= arp_type
;
163 static int client_ensure_duid(sd_dhcp6_client
*client
)
165 if (client
->duid_len
!= 0)
167 return dhcp_identifier_set_duid_en(&client
->duid
, &client
->duid_len
);
170 int sd_dhcp6_client_set_duid(sd_dhcp6_client
*client
, uint16_t type
, uint8_t *duid
,
173 assert_return(client
, -EINVAL
);
174 assert_return(duid
, -EINVAL
);
175 assert_return(duid_len
> 0 && duid_len
<= MAX_DUID_LEN
, -EINVAL
);
179 if (duid_len
<= sizeof(client
->duid
.llt
))
183 if (duid_len
!= sizeof(client
->duid
.en
))
187 if (duid_len
<= sizeof(client
->duid
.ll
))
190 case DHCP6_DUID_UUID
:
191 if (duid_len
!= sizeof(client
->duid
.uuid
))
195 /* accept unknown type in order to be forward compatible */
199 client
->duid
.type
= htobe16(type
);
200 memcpy(&client
->duid
.raw
.data
, duid
, duid_len
);
201 client
->duid_len
= duid_len
+ sizeof(client
->duid
.type
);
206 int sd_dhcp6_client_set_information_request(sd_dhcp6_client
*client
,
208 assert_return(client
, -EINVAL
);
210 client
->information_request
= enabled
;
215 int sd_dhcp6_client_get_information_request(sd_dhcp6_client
*client
,
217 assert_return(client
, -EINVAL
);
218 assert_return(enabled
, -EINVAL
);
220 *enabled
= client
->information_request
;
225 int sd_dhcp6_client_set_request_option(sd_dhcp6_client
*client
,
229 assert_return(client
, -EINVAL
);
230 assert_return(client
->state
== DHCP6_STATE_STOPPED
, -EBUSY
);
233 case DHCP6_OPTION_DNS_SERVERS
:
234 case DHCP6_OPTION_DOMAIN_LIST
:
235 case DHCP6_OPTION_SNTP_SERVERS
:
236 case DHCP6_OPTION_NTP_SERVER
:
243 for (t
= 0; t
< client
->req_opts_len
; t
++)
244 if (client
->req_opts
[t
] == htobe16(option
))
247 if (!GREEDY_REALLOC(client
->req_opts
, client
->req_opts_allocated
,
248 client
->req_opts_len
+ 1))
251 client
->req_opts
[client
->req_opts_len
++] = htobe16(option
);
256 int sd_dhcp6_client_get_lease(sd_dhcp6_client
*client
, sd_dhcp6_lease
**ret
) {
257 assert_return(client
, -EINVAL
);
258 assert_return(ret
, -EINVAL
);
263 *ret
= sd_dhcp6_lease_ref(client
->lease
);
268 static void client_notify(sd_dhcp6_client
*client
, int event
) {
270 client
->cb(client
, event
, client
->userdata
);
273 static int client_reset(sd_dhcp6_client
*client
) {
274 assert_return(client
, -EINVAL
);
277 dhcp6_lease_clear_timers(&client
->lease
->ia
);
278 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
281 client
->receive_message
=
282 sd_event_source_unref(client
->receive_message
);
284 client
->fd
= safe_close(client
->fd
);
286 client
->transaction_id
= 0;
287 client
->transaction_start
= 0;
289 client
->ia_na
.timeout_t1
=
290 sd_event_source_unref(client
->ia_na
.timeout_t1
);
291 client
->ia_na
.timeout_t2
=
292 sd_event_source_unref(client
->ia_na
.timeout_t2
);
294 client
->retransmit_time
= 0;
295 client
->retransmit_count
= 0;
296 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
297 client
->timeout_resend_expire
=
298 sd_event_source_unref(client
->timeout_resend_expire
);
300 client
->state
= DHCP6_STATE_STOPPED
;
305 static void client_stop(sd_dhcp6_client
*client
, int error
) {
306 DHCP6_CLIENT_DONT_DESTROY(client
);
310 client_notify(client
, error
);
312 client_reset(client
);
315 static int client_send_message(sd_dhcp6_client
*client
, usec_t time_now
) {
316 _cleanup_free_ DHCP6Message
*message
= NULL
;
317 struct in6_addr all_servers
=
318 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT
;
319 size_t len
, optlen
= 512;
325 len
= sizeof(DHCP6Message
) + optlen
;
327 message
= malloc0(len
);
331 opt
= (uint8_t *)(message
+ 1);
333 message
->transaction_id
= client
->transaction_id
;
335 switch(client
->state
) {
336 case DHCP6_STATE_INFORMATION_REQUEST
:
337 message
->type
= DHCP6_INFORMATION_REQUEST
;
341 case DHCP6_STATE_SOLICITATION
:
342 message
->type
= DHCP6_SOLICIT
;
344 r
= dhcp6_option_append(&opt
, &optlen
,
345 DHCP6_OPTION_RAPID_COMMIT
, 0, NULL
);
349 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->ia_na
);
355 case DHCP6_STATE_REQUEST
:
356 case DHCP6_STATE_RENEW
:
358 if (client
->state
== DHCP6_STATE_REQUEST
)
359 message
->type
= DHCP6_REQUEST
;
361 message
->type
= DHCP6_RENEW
;
363 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_SERVERID
,
364 client
->lease
->serverid_len
,
365 client
->lease
->serverid
);
369 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
375 case DHCP6_STATE_REBIND
:
376 message
->type
= DHCP6_REBIND
;
378 r
= dhcp6_option_append_ia(&opt
, &optlen
, &client
->lease
->ia
);
384 case DHCP6_STATE_STOPPED
:
385 case DHCP6_STATE_BOUND
:
389 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ORO
,
390 client
->req_opts_len
* sizeof(be16_t
),
395 assert (client
->duid_len
);
396 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_CLIENTID
,
397 client
->duid_len
, &client
->duid
);
401 elapsed_usec
= time_now
- client
->transaction_start
;
402 if (elapsed_usec
< 0xffff * USEC_PER_MSEC
* 10)
403 elapsed_time
= htobe16(elapsed_usec
/ USEC_PER_MSEC
/ 10);
405 elapsed_time
= 0xffff;
407 r
= dhcp6_option_append(&opt
, &optlen
, DHCP6_OPTION_ELAPSED_TIME
,
408 sizeof(elapsed_time
), &elapsed_time
);
412 r
= dhcp6_network_send_udp_socket(client
->fd
, &all_servers
, message
,
417 log_dhcp6_client(client
, "Sent %s",
418 dhcp6_message_type_to_string(message
->type
));
423 static int client_timeout_t2(sd_event_source
*s
, uint64_t usec
,
425 sd_dhcp6_client
*client
= userdata
;
427 assert_return(s
, -EINVAL
);
428 assert_return(client
, -EINVAL
);
429 assert_return(client
->lease
, -EINVAL
);
431 client
->lease
->ia
.timeout_t2
=
432 sd_event_source_unref(client
->lease
->ia
.timeout_t2
);
434 log_dhcp6_client(client
, "Timeout T2");
436 client_start(client
, DHCP6_STATE_REBIND
);
441 static int client_timeout_t1(sd_event_source
*s
, uint64_t usec
,
443 sd_dhcp6_client
*client
= userdata
;
445 assert_return(s
, -EINVAL
);
446 assert_return(client
, -EINVAL
);
447 assert_return(client
->lease
, -EINVAL
);
449 client
->lease
->ia
.timeout_t1
=
450 sd_event_source_unref(client
->lease
->ia
.timeout_t1
);
452 log_dhcp6_client(client
, "Timeout T1");
454 client_start(client
, DHCP6_STATE_RENEW
);
459 static int client_timeout_resend_expire(sd_event_source
*s
, uint64_t usec
,
461 sd_dhcp6_client
*client
= userdata
;
462 DHCP6_CLIENT_DONT_DESTROY(client
);
463 enum DHCP6State state
;
467 assert(client
->event
);
469 state
= client
->state
;
471 client_stop(client
, DHCP6_EVENT_RESEND_EXPIRE
);
473 /* RFC 3315, section 18.1.4., says that "...the client may choose to
474 use a Solicit message to locate a new DHCP server..." */
475 if (state
== DHCP6_STATE_REBIND
)
476 client_start(client
, DHCP6_STATE_SOLICITATION
);
481 static usec_t
client_timeout_compute_random(usec_t val
) {
482 return val
- val
/ 10 +
483 (random_u32() % (2 * USEC_PER_SEC
)) * val
/ 10 / USEC_PER_SEC
;
486 static int client_timeout_resend(sd_event_source
*s
, uint64_t usec
,
489 sd_dhcp6_client
*client
= userdata
;
490 usec_t time_now
, init_retransmit_time
= 0, max_retransmit_time
= 0;
491 usec_t max_retransmit_duration
= 0;
492 uint8_t max_retransmit_count
= 0;
493 char time_string
[FORMAT_TIMESPAN_MAX
];
498 assert(client
->event
);
500 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
502 switch (client
->state
) {
503 case DHCP6_STATE_INFORMATION_REQUEST
:
504 init_retransmit_time
= DHCP6_INF_TIMEOUT
;
505 max_retransmit_time
= DHCP6_INF_MAX_RT
;
509 case DHCP6_STATE_SOLICITATION
:
511 if (client
->retransmit_count
&& client
->lease
) {
512 client_start(client
, DHCP6_STATE_REQUEST
);
516 init_retransmit_time
= DHCP6_SOL_TIMEOUT
;
517 max_retransmit_time
= DHCP6_SOL_MAX_RT
;
521 case DHCP6_STATE_REQUEST
:
522 init_retransmit_time
= DHCP6_REQ_TIMEOUT
;
523 max_retransmit_time
= DHCP6_REQ_MAX_RT
;
524 max_retransmit_count
= DHCP6_REQ_MAX_RC
;
528 case DHCP6_STATE_RENEW
:
529 init_retransmit_time
= DHCP6_REN_TIMEOUT
;
530 max_retransmit_time
= DHCP6_REN_MAX_RT
;
532 /* RFC 3315, section 18.1.3. says max retransmit duration will
533 be the remaining time until T2. Instead of setting MRD,
534 wait for T2 to trigger with the same end result */
538 case DHCP6_STATE_REBIND
:
539 init_retransmit_time
= DHCP6_REB_TIMEOUT
;
540 max_retransmit_time
= DHCP6_REB_MAX_RT
;
542 if (!client
->timeout_resend_expire
) {
543 r
= dhcp6_lease_ia_rebind_expire(&client
->lease
->ia
,
546 client_stop(client
, r
);
549 max_retransmit_duration
= expire
* USEC_PER_SEC
;
554 case DHCP6_STATE_STOPPED
:
555 case DHCP6_STATE_BOUND
:
559 if (max_retransmit_count
&&
560 client
->retransmit_count
>= max_retransmit_count
) {
561 client_stop(client
, DHCP6_EVENT_RETRANS_MAX
);
565 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
569 r
= client_send_message(client
, time_now
);
571 client
->retransmit_count
++;
573 if (!client
->retransmit_time
) {
574 client
->retransmit_time
=
575 client_timeout_compute_random(init_retransmit_time
);
577 if (client
->state
== DHCP6_STATE_SOLICITATION
)
578 client
->retransmit_time
+= init_retransmit_time
/ 10;
581 if (max_retransmit_time
&&
582 client
->retransmit_time
> max_retransmit_time
/ 2)
583 client
->retransmit_time
= client_timeout_compute_random(max_retransmit_time
);
585 client
->retransmit_time
+= client_timeout_compute_random(client
->retransmit_time
);
588 log_dhcp6_client(client
, "Next retransmission in %s",
589 format_timespan(time_string
, FORMAT_TIMESPAN_MAX
,
590 client
->retransmit_time
, 0));
592 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
593 clock_boottime_or_monotonic(),
594 time_now
+ client
->retransmit_time
,
595 10 * USEC_PER_MSEC
, client_timeout_resend
,
600 r
= sd_event_source_set_priority(client
->timeout_resend
,
601 client
->event_priority
);
605 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timer");
609 if (max_retransmit_duration
&& !client
->timeout_resend_expire
) {
611 log_dhcp6_client(client
, "Max retransmission duration %"PRIu64
" secs",
612 max_retransmit_duration
/ USEC_PER_SEC
);
614 r
= sd_event_add_time(client
->event
,
615 &client
->timeout_resend_expire
,
616 clock_boottime_or_monotonic(),
617 time_now
+ max_retransmit_duration
,
619 client_timeout_resend_expire
, client
);
623 r
= sd_event_source_set_priority(client
->timeout_resend_expire
,
624 client
->event_priority
);
628 r
= sd_event_source_set_description(client
->timeout_resend_expire
, "dhcp6-resend-expire-timer");
635 client_stop(client
, r
);
640 static int client_ensure_iaid(sd_dhcp6_client
*client
) {
645 if (client
->ia_na
.id
)
648 r
= dhcp_identifier_set_iaid(client
->index
, client
->mac_addr
, client
->mac_addr_len
, &client
->ia_na
.id
);
655 static int client_parse_message(sd_dhcp6_client
*client
,
656 DHCP6Message
*message
, size_t len
,
657 sd_dhcp6_lease
*lease
) {
659 uint8_t *optval
, *option
, *id
= NULL
;
660 uint16_t optcode
, status
;
661 size_t optlen
, id_len
;
662 bool clientid
= false;
665 option
= (uint8_t *)message
+ sizeof(DHCP6Message
);
666 len
-= sizeof(DHCP6Message
);
668 while ((r
= dhcp6_option_parse(&option
, &len
, &optcode
, &optlen
,
671 case DHCP6_OPTION_CLIENTID
:
673 log_dhcp6_client(client
, "%s contains multiple clientids",
674 dhcp6_message_type_to_string(message
->type
));
678 if (optlen
!= client
->duid_len
||
679 memcmp(&client
->duid
, optval
, optlen
) != 0) {
680 log_dhcp6_client(client
, "%s DUID does not match",
681 dhcp6_message_type_to_string(message
->type
));
689 case DHCP6_OPTION_SERVERID
:
690 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
692 log_dhcp6_client(client
, "%s contains multiple serverids",
693 dhcp6_message_type_to_string(message
->type
));
697 r
= dhcp6_lease_set_serverid(lease
, optval
, optlen
);
703 case DHCP6_OPTION_PREFERENCE
:
707 r
= dhcp6_lease_set_preference(lease
, *optval
);
713 case DHCP6_OPTION_STATUS_CODE
:
717 status
= optval
[0] << 8 | optval
[1];
719 log_dhcp6_client(client
, "%s Status %s",
720 dhcp6_message_type_to_string(message
->type
),
721 dhcp6_message_status_to_string(status
));
727 case DHCP6_OPTION_IA_NA
:
728 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
729 log_dhcp6_client(client
, "Information request ignoring IA NA option");
734 r
= dhcp6_option_parse_ia(&optval
, &optlen
, optcode
,
736 if (r
< 0 && r
!= -ENOMSG
)
739 r
= dhcp6_lease_get_iaid(lease
, &iaid_lease
);
743 if (client
->ia_na
.id
!= iaid_lease
) {
744 log_dhcp6_client(client
, "%s has wrong IAID",
745 dhcp6_message_type_to_string(message
->type
));
751 case DHCP6_OPTION_RAPID_COMMIT
:
752 r
= dhcp6_lease_set_rapid_commit(lease
);
758 case DHCP6_OPTION_DNS_SERVERS
:
759 r
= dhcp6_lease_set_dns(lease
, optval
, optlen
);
765 case DHCP6_OPTION_DOMAIN_LIST
:
766 r
= dhcp6_lease_set_domains(lease
, optval
, optlen
);
772 case DHCP6_OPTION_NTP_SERVER
:
773 r
= dhcp6_lease_set_ntp(lease
, optval
, optlen
);
779 case DHCP6_OPTION_SNTP_SERVERS
:
780 r
= dhcp6_lease_set_sntp(lease
, optval
, optlen
);
792 if (r
< 0 || !clientid
) {
793 log_dhcp6_client(client
, "%s has incomplete options",
794 dhcp6_message_type_to_string(message
->type
));
798 if (client
->state
!= DHCP6_STATE_INFORMATION_REQUEST
) {
799 r
= dhcp6_lease_get_serverid(lease
, &id
, &id_len
);
801 log_dhcp6_client(client
, "%s has no server id",
802 dhcp6_message_type_to_string(message
->type
));
808 static int client_receive_reply(sd_dhcp6_client
*client
, DHCP6Message
*reply
,
812 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
815 if (reply
->type
!= DHCP6_REPLY
)
818 r
= dhcp6_lease_new(&lease
);
822 r
= client_parse_message(client
, reply
, len
, lease
);
826 if (client
->state
== DHCP6_STATE_SOLICITATION
) {
827 r
= dhcp6_lease_get_rapid_commit(lease
, &rapid_commit
);
836 dhcp6_lease_clear_timers(&client
->lease
->ia
);
837 client
->lease
= sd_dhcp6_lease_unref(client
->lease
);
840 client
->lease
= lease
;
843 return DHCP6_STATE_BOUND
;
846 static int client_receive_advertise(sd_dhcp6_client
*client
,
847 DHCP6Message
*advertise
, size_t len
) {
849 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease
*lease
= NULL
;
850 uint8_t pref_advertise
= 0, pref_lease
= 0;
852 if (advertise
->type
!= DHCP6_ADVERTISE
)
855 r
= dhcp6_lease_new(&lease
);
859 r
= client_parse_message(client
, advertise
, len
, lease
);
863 r
= dhcp6_lease_get_preference(lease
, &pref_advertise
);
867 r
= dhcp6_lease_get_preference(client
->lease
, &pref_lease
);
869 if (r
< 0 || pref_advertise
> pref_lease
) {
870 sd_dhcp6_lease_unref(client
->lease
);
871 client
->lease
= lease
;
876 if (pref_advertise
== 255 || client
->retransmit_count
> 1)
877 r
= DHCP6_STATE_REQUEST
;
882 static int client_receive_message(sd_event_source
*s
, int fd
, uint32_t revents
,
884 sd_dhcp6_client
*client
= userdata
;
885 DHCP6_CLIENT_DONT_DESTROY(client
);
886 _cleanup_free_ DHCP6Message
*message
;
891 assert(client
->event
);
893 r
= ioctl(fd
, FIONREAD
, &buflen
);
894 if (r
< 0 || buflen
<= 0)
895 buflen
= DHCP6_MIN_OPTIONS_SIZE
;
897 message
= malloc0(buflen
);
901 len
= read(fd
, message
, buflen
);
902 if ((size_t)len
< sizeof(DHCP6Message
)) {
903 log_dhcp6_client(client
, "could not receive message from UDP socket: %m");
907 switch(message
->type
) {
915 case DHCP6_INFORMATION_REQUEST
:
916 case DHCP6_RELAY_FORW
:
917 case DHCP6_RELAY_REPL
:
920 case DHCP6_ADVERTISE
:
922 case DHCP6_RECONFIGURE
:
926 log_dhcp6_client(client
, "unknown message type %d",
931 if (client
->transaction_id
!= (message
->transaction_id
&
932 htobe32(0x00ffffff)))
935 switch (client
->state
) {
936 case DHCP6_STATE_INFORMATION_REQUEST
:
937 r
= client_receive_reply(client
, message
, len
);
941 client_notify(client
, DHCP6_EVENT_INFORMATION_REQUEST
);
943 client_start(client
, DHCP6_STATE_STOPPED
);
947 case DHCP6_STATE_SOLICITATION
:
948 r
= client_receive_advertise(client
, message
, len
);
950 if (r
== DHCP6_STATE_REQUEST
) {
951 client_start(client
, r
);
956 /* fall through for Soliciation Rapid Commit option check */
957 case DHCP6_STATE_REQUEST
:
958 case DHCP6_STATE_RENEW
:
959 case DHCP6_STATE_REBIND
:
961 r
= client_receive_reply(client
, message
, len
);
965 if (r
== DHCP6_STATE_BOUND
) {
967 r
= client_start(client
, DHCP6_STATE_BOUND
);
969 client_stop(client
, r
);
973 client_notify(client
, DHCP6_EVENT_IP_ACQUIRE
);
978 case DHCP6_STATE_BOUND
:
982 case DHCP6_STATE_STOPPED
:
987 log_dhcp6_client(client
, "Recv %s",
988 dhcp6_message_type_to_string(message
->type
));
994 static int client_start(sd_dhcp6_client
*client
, enum DHCP6State state
)
997 usec_t timeout
, time_now
;
998 char time_string
[FORMAT_TIMESPAN_MAX
];
1000 assert_return(client
, -EINVAL
);
1001 assert_return(client
->event
, -EINVAL
);
1002 assert_return(client
->index
> 0, -EINVAL
);
1003 assert_return(client
->state
!= state
, -EINVAL
);
1005 client
->timeout_resend_expire
=
1006 sd_event_source_unref(client
->timeout_resend_expire
);
1007 client
->timeout_resend
= sd_event_source_unref(client
->timeout_resend
);
1008 client
->retransmit_time
= 0;
1009 client
->retransmit_count
= 0;
1011 r
= sd_event_now(client
->event
, clock_boottime_or_monotonic(), &time_now
);
1016 case DHCP6_STATE_STOPPED
:
1017 if (client
->state
== DHCP6_STATE_INFORMATION_REQUEST
) {
1018 client
->state
= DHCP6_STATE_STOPPED
;
1024 case DHCP6_STATE_SOLICITATION
:
1025 client
->state
= DHCP6_STATE_SOLICITATION
;
1029 case DHCP6_STATE_INFORMATION_REQUEST
:
1030 case DHCP6_STATE_REQUEST
:
1031 case DHCP6_STATE_RENEW
:
1032 case DHCP6_STATE_REBIND
:
1034 client
->state
= state
;
1038 case DHCP6_STATE_BOUND
:
1040 if (client
->lease
->ia
.lifetime_t1
== 0xffffffff ||
1041 client
->lease
->ia
.lifetime_t2
== 0xffffffff) {
1043 log_dhcp6_client(client
, "infinite T1 0x%08x or T2 0x%08x",
1044 be32toh(client
->lease
->ia
.lifetime_t1
),
1045 be32toh(client
->lease
->ia
.lifetime_t2
));
1050 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t1
) * USEC_PER_SEC
);
1052 log_dhcp6_client(client
, "T1 expires in %s",
1053 format_timespan(time_string
,
1054 FORMAT_TIMESPAN_MAX
,
1057 r
= sd_event_add_time(client
->event
,
1058 &client
->lease
->ia
.timeout_t1
,
1059 clock_boottime_or_monotonic(), time_now
+ timeout
,
1060 10 * USEC_PER_SEC
, client_timeout_t1
,
1065 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t1
,
1066 client
->event_priority
);
1070 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t1
, "dhcp6-t1-timeout");
1074 timeout
= client_timeout_compute_random(be32toh(client
->lease
->ia
.lifetime_t2
) * USEC_PER_SEC
);
1076 log_dhcp6_client(client
, "T2 expires in %s",
1077 format_timespan(time_string
,
1078 FORMAT_TIMESPAN_MAX
,
1081 r
= sd_event_add_time(client
->event
,
1082 &client
->lease
->ia
.timeout_t2
,
1083 clock_boottime_or_monotonic(), time_now
+ timeout
,
1084 10 * USEC_PER_SEC
, client_timeout_t2
,
1089 r
= sd_event_source_set_priority(client
->lease
->ia
.timeout_t2
,
1090 client
->event_priority
);
1094 r
= sd_event_source_set_description(client
->lease
->ia
.timeout_t2
, "dhcp6-t2-timeout");
1098 client
->state
= state
;
1103 client
->transaction_id
= random_u32() & htobe32(0x00ffffff);
1104 client
->transaction_start
= time_now
;
1106 r
= sd_event_add_time(client
->event
, &client
->timeout_resend
,
1107 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend
,
1112 r
= sd_event_source_set_priority(client
->timeout_resend
,
1113 client
->event_priority
);
1117 r
= sd_event_source_set_description(client
->timeout_resend
, "dhcp6-resend-timeout");
1124 int sd_dhcp6_client_stop(sd_dhcp6_client
*client
)
1126 client_stop(client
, DHCP6_EVENT_STOP
);
1131 int sd_dhcp6_client_start(sd_dhcp6_client
*client
)
1134 enum DHCP6State state
= DHCP6_STATE_SOLICITATION
;
1136 assert_return(client
, -EINVAL
);
1137 assert_return(client
->event
, -EINVAL
);
1138 assert_return(client
->index
> 0, -EINVAL
);
1140 r
= client_reset(client
);
1144 r
= client_ensure_iaid(client
);
1148 r
= client_ensure_duid(client
);
1152 r
= dhcp6_network_bind_udp_socket(client
->index
, NULL
);
1158 r
= sd_event_add_io(client
->event
, &client
->receive_message
,
1159 client
->fd
, EPOLLIN
, client_receive_message
,
1164 r
= sd_event_source_set_priority(client
->receive_message
,
1165 client
->event_priority
);
1169 r
= sd_event_source_set_description(client
->receive_message
,
1170 "dhcp6-receive-message");
1174 if (client
->information_request
)
1175 state
= DHCP6_STATE_INFORMATION_REQUEST
;
1177 log_dhcp6_client(client
, "Started in %s mode",
1178 client
->information_request
? "Information request":
1181 return client_start(client
, state
);
1184 client_reset(client
);
1188 int sd_dhcp6_client_attach_event(sd_dhcp6_client
*client
, sd_event
*event
,
1193 assert_return(client
, -EINVAL
);
1194 assert_return(!client
->event
, -EBUSY
);
1197 client
->event
= sd_event_ref(event
);
1199 r
= sd_event_default(&client
->event
);
1204 client
->event_priority
= priority
;
1209 int sd_dhcp6_client_detach_event(sd_dhcp6_client
*client
) {
1210 assert_return(client
, -EINVAL
);
1212 client
->event
= sd_event_unref(client
->event
);
1217 sd_event
*sd_dhcp6_client_get_event(sd_dhcp6_client
*client
) {
1221 return client
->event
;
1224 sd_dhcp6_client
*sd_dhcp6_client_ref(sd_dhcp6_client
*client
) {
1226 assert_se(REFCNT_INC(client
->n_ref
) >= 2);
1231 sd_dhcp6_client
*sd_dhcp6_client_unref(sd_dhcp6_client
*client
) {
1232 if (client
&& REFCNT_DEC(client
->n_ref
) == 0) {
1233 client_reset(client
);
1235 sd_dhcp6_client_detach_event(client
);
1236 sd_dhcp6_lease_unref(client
->lease
);
1238 free(client
->req_opts
);
1247 int sd_dhcp6_client_new(sd_dhcp6_client
**ret
)
1249 _cleanup_dhcp6_client_unref_ sd_dhcp6_client
*client
= NULL
;
1252 assert_return(ret
, -EINVAL
);
1254 client
= new0(sd_dhcp6_client
, 1);
1258 client
->n_ref
= REFCNT_INIT
;
1260 client
->ia_na
.type
= DHCP6_OPTION_IA_NA
;
1266 client
->req_opts_len
= ELEMENTSOF(default_req_opts
);
1268 client
->req_opts
= new0(be16_t
, client
->req_opts_len
);
1269 if (!client
->req_opts
)
1272 for (t
= 0; t
< client
->req_opts_len
; t
++)
1273 client
->req_opts
[t
] = htobe16(default_req_opts
[t
]);