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 static int client_ensure_duid(sd_dhcp6_client *client)
163 if (client->duid_len != 0)
165 return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
168 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
171 assert_return(client, -EINVAL);
172 assert_return(duid, -EINVAL);
173 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
177 if (duid_len <= sizeof(client->duid.llt))
181 if (duid_len != sizeof(client->duid.en))
185 if (duid_len <= sizeof(client->duid.ll))
188 case DHCP6_DUID_UUID:
189 if (duid_len != sizeof(client->duid.uuid))
193 /* accept unknown type in order to be forward compatible */
197 client->duid.type = htobe16(type);
198 memcpy(&client->duid.raw.data, duid, duid_len);
199 client->duid_len = duid_len + sizeof(client->duid.type);
204 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
206 assert_return(client, -EINVAL);
208 client->information_request = enabled;
213 int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
215 assert_return(client, -EINVAL);
216 assert_return(enabled, -EINVAL);
218 *enabled = client->information_request;
223 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
227 assert_return(client, -EINVAL);
228 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
231 case DHCP6_OPTION_DNS_SERVERS:
232 case DHCP6_OPTION_DOMAIN_LIST:
233 case DHCP6_OPTION_SNTP_SERVERS:
234 case DHCP6_OPTION_NTP_SERVER:
241 for (t = 0; t < client->req_opts_len; t++)
242 if (client->req_opts[t] == htobe16(option))
245 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
246 client->req_opts_len + 1))
249 client->req_opts[client->req_opts_len++] = htobe16(option);
254 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
255 assert_return(client, -EINVAL);
256 assert_return(ret, -EINVAL);
261 *ret = sd_dhcp6_lease_ref(client->lease);
266 static void client_notify(sd_dhcp6_client *client, int event) {
268 client->cb(client, event, client->userdata);
271 static int client_reset(sd_dhcp6_client *client) {
272 assert_return(client, -EINVAL);
274 client->receive_message =
275 sd_event_source_unref(client->receive_message);
277 client->fd = safe_close(client->fd);
279 client->transaction_id = 0;
280 client->transaction_start = 0;
282 client->ia_na.timeout_t1 =
283 sd_event_source_unref(client->ia_na.timeout_t1);
284 client->ia_na.timeout_t2 =
285 sd_event_source_unref(client->ia_na.timeout_t2);
287 client->retransmit_time = 0;
288 client->retransmit_count = 0;
289 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
290 client->timeout_resend_expire =
291 sd_event_source_unref(client->timeout_resend_expire);
293 client->state = DHCP6_STATE_STOPPED;
298 static void client_stop(sd_dhcp6_client *client, int error) {
299 DHCP6_CLIENT_DONT_DESTROY(client);
303 client_notify(client, error);
305 client_reset(client);
308 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
309 _cleanup_free_ DHCP6Message *message = NULL;
310 struct in6_addr all_servers =
311 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
312 size_t len, optlen = 512;
318 len = sizeof(DHCP6Message) + optlen;
320 message = malloc0(len);
324 opt = (uint8_t *)(message + 1);
326 message->transaction_id = client->transaction_id;
328 switch(client->state) {
329 case DHCP6_STATE_INFORMATION_REQUEST:
330 message->type = DHCP6_INFORMATION_REQUEST;
334 case DHCP6_STATE_SOLICITATION:
335 message->type = DHCP6_SOLICIT;
337 r = dhcp6_option_append(&opt, &optlen,
338 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
342 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
348 case DHCP6_STATE_REQUEST:
349 case DHCP6_STATE_RENEW:
351 if (client->state == DHCP6_STATE_REQUEST)
352 message->type = DHCP6_REQUEST;
354 message->type = DHCP6_RENEW;
356 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
357 client->lease->serverid_len,
358 client->lease->serverid);
362 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
368 case DHCP6_STATE_REBIND:
369 message->type = DHCP6_REBIND;
371 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
377 case DHCP6_STATE_STOPPED:
378 case DHCP6_STATE_BOUND:
382 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
383 client->req_opts_len * sizeof(be16_t),
388 assert (client->duid_len);
389 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
390 client->duid_len, &client->duid);
394 elapsed_usec = time_now - client->transaction_start;
395 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
396 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
398 elapsed_time = 0xffff;
400 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
401 sizeof(elapsed_time), &elapsed_time);
405 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
410 log_dhcp6_client(client, "Sent %s",
411 dhcp6_message_type_to_string(message->type));
416 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
418 sd_dhcp6_client *client = userdata;
420 assert_return(s, -EINVAL);
421 assert_return(client, -EINVAL);
422 assert_return(client->lease, -EINVAL);
424 client->lease->ia.timeout_t2 =
425 sd_event_source_unref(client->lease->ia.timeout_t2);
427 log_dhcp6_client(client, "Timeout T2");
429 client_start(client, DHCP6_STATE_REBIND);
434 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
436 sd_dhcp6_client *client = userdata;
438 assert_return(s, -EINVAL);
439 assert_return(client, -EINVAL);
440 assert_return(client->lease, -EINVAL);
442 client->lease->ia.timeout_t1 =
443 sd_event_source_unref(client->lease->ia.timeout_t1);
445 log_dhcp6_client(client, "Timeout T1");
447 client_start(client, DHCP6_STATE_RENEW);
452 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
454 sd_dhcp6_client *client = userdata;
455 DHCP6_CLIENT_DONT_DESTROY(client);
456 enum DHCP6State state;
460 assert(client->event);
462 state = client->state;
464 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
466 /* RFC 3315, section 18.1.4., says that "...the client may choose to
467 use a Solicit message to locate a new DHCP server..." */
468 if (state == DHCP6_STATE_REBIND)
469 client_start(client, DHCP6_STATE_SOLICITATION);
474 static usec_t client_timeout_compute_random(usec_t val) {
475 return val - val / 10 +
476 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
479 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
482 sd_dhcp6_client *client = userdata;
483 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
484 usec_t max_retransmit_duration = 0;
485 uint8_t max_retransmit_count = 0;
486 char time_string[FORMAT_TIMESPAN_MAX];
491 assert(client->event);
493 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
495 switch (client->state) {
496 case DHCP6_STATE_INFORMATION_REQUEST:
497 init_retransmit_time = DHCP6_INF_TIMEOUT;
498 max_retransmit_time = DHCP6_INF_MAX_RT;
502 case DHCP6_STATE_SOLICITATION:
504 if (client->retransmit_count && client->lease) {
505 client_start(client, DHCP6_STATE_REQUEST);
509 init_retransmit_time = DHCP6_SOL_TIMEOUT;
510 max_retransmit_time = DHCP6_SOL_MAX_RT;
514 case DHCP6_STATE_REQUEST:
515 init_retransmit_time = DHCP6_REQ_TIMEOUT;
516 max_retransmit_time = DHCP6_REQ_MAX_RT;
517 max_retransmit_count = DHCP6_REQ_MAX_RC;
521 case DHCP6_STATE_RENEW:
522 init_retransmit_time = DHCP6_REN_TIMEOUT;
523 max_retransmit_time = DHCP6_REN_MAX_RT;
525 /* RFC 3315, section 18.1.3. says max retransmit duration will
526 be the remaining time until T2. Instead of setting MRD,
527 wait for T2 to trigger with the same end result */
531 case DHCP6_STATE_REBIND:
532 init_retransmit_time = DHCP6_REB_TIMEOUT;
533 max_retransmit_time = DHCP6_REB_MAX_RT;
535 if (!client->timeout_resend_expire) {
536 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
539 client_stop(client, r);
542 max_retransmit_duration = expire * USEC_PER_SEC;
547 case DHCP6_STATE_STOPPED:
548 case DHCP6_STATE_BOUND:
552 if (max_retransmit_count &&
553 client->retransmit_count >= max_retransmit_count) {
554 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
558 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
562 r = client_send_message(client, time_now);
564 client->retransmit_count++;
566 if (!client->retransmit_time) {
567 client->retransmit_time =
568 client_timeout_compute_random(init_retransmit_time);
570 if (client->state == DHCP6_STATE_SOLICITATION)
571 client->retransmit_time += init_retransmit_time / 10;
574 if (max_retransmit_time &&
575 client->retransmit_time > max_retransmit_time / 2)
576 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
578 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
581 log_dhcp6_client(client, "Next retransmission in %s",
582 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
583 client->retransmit_time, 0));
585 r = sd_event_add_time(client->event, &client->timeout_resend,
586 clock_boottime_or_monotonic(),
587 time_now + client->retransmit_time,
588 10 * USEC_PER_MSEC, client_timeout_resend,
593 r = sd_event_source_set_priority(client->timeout_resend,
594 client->event_priority);
598 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
602 if (max_retransmit_duration && !client->timeout_resend_expire) {
604 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
605 max_retransmit_duration / USEC_PER_SEC);
607 r = sd_event_add_time(client->event,
608 &client->timeout_resend_expire,
609 clock_boottime_or_monotonic(),
610 time_now + max_retransmit_duration,
612 client_timeout_resend_expire, client);
616 r = sd_event_source_set_priority(client->timeout_resend_expire,
617 client->event_priority);
621 r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
628 client_stop(client, r);
633 static int client_ensure_iaid(sd_dhcp6_client *client) {
638 if (client->ia_na.id)
641 r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
648 static int client_parse_message(sd_dhcp6_client *client,
649 DHCP6Message *message, size_t len,
650 sd_dhcp6_lease *lease) {
652 uint8_t *optval, *option, *id = NULL;
653 uint16_t optcode, status;
654 size_t optlen, id_len;
655 bool clientid = false;
658 option = (uint8_t *)message + sizeof(DHCP6Message);
659 len -= sizeof(DHCP6Message);
661 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
664 case DHCP6_OPTION_CLIENTID:
666 log_dhcp6_client(client, "%s contains multiple clientids",
667 dhcp6_message_type_to_string(message->type));
671 if (optlen != client->duid_len ||
672 memcmp(&client->duid, optval, optlen) != 0) {
673 log_dhcp6_client(client, "%s DUID does not match",
674 dhcp6_message_type_to_string(message->type));
682 case DHCP6_OPTION_SERVERID:
683 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
685 log_dhcp6_client(client, "%s contains multiple serverids",
686 dhcp6_message_type_to_string(message->type));
690 r = dhcp6_lease_set_serverid(lease, optval, optlen);
696 case DHCP6_OPTION_PREFERENCE:
700 r = dhcp6_lease_set_preference(lease, *optval);
706 case DHCP6_OPTION_STATUS_CODE:
710 status = optval[0] << 8 | optval[1];
712 log_dhcp6_client(client, "%s Status %s",
713 dhcp6_message_type_to_string(message->type),
714 dhcp6_message_status_to_string(status));
720 case DHCP6_OPTION_IA_NA:
721 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
722 log_dhcp6_client(client, "Information request ignoring IA NA option");
727 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
729 if (r < 0 && r != -ENOMSG)
732 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
736 if (client->ia_na.id != iaid_lease) {
737 log_dhcp6_client(client, "%s has wrong IAID",
738 dhcp6_message_type_to_string(message->type));
744 case DHCP6_OPTION_RAPID_COMMIT:
745 r = dhcp6_lease_set_rapid_commit(lease);
756 if (r < 0 || !clientid) {
757 log_dhcp6_client(client, "%s has incomplete options",
758 dhcp6_message_type_to_string(message->type));
762 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
763 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
765 log_dhcp6_client(client, "%s has no server id",
766 dhcp6_message_type_to_string(message->type));
772 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
776 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
779 if (reply->type != DHCP6_REPLY)
782 r = dhcp6_lease_new(&lease);
786 r = client_parse_message(client, reply, len, lease);
790 if (client->state == DHCP6_STATE_SOLICITATION) {
791 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
800 dhcp6_lease_clear_timers(&client->lease->ia);
801 client->lease = sd_dhcp6_lease_unref(client->lease);
804 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
805 client->lease = lease;
809 return DHCP6_STATE_BOUND;
812 static int client_receive_advertise(sd_dhcp6_client *client,
813 DHCP6Message *advertise, size_t len) {
815 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
816 uint8_t pref_advertise = 0, pref_lease = 0;
818 if (advertise->type != DHCP6_ADVERTISE)
821 r = dhcp6_lease_new(&lease);
825 r = client_parse_message(client, advertise, len, lease);
829 r = dhcp6_lease_get_preference(lease, &pref_advertise);
833 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
835 if (r < 0 || pref_advertise > pref_lease) {
836 sd_dhcp6_lease_unref(client->lease);
837 client->lease = lease;
842 if (pref_advertise == 255 || client->retransmit_count > 1)
843 r = DHCP6_STATE_REQUEST;
848 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
850 sd_dhcp6_client *client = userdata;
851 DHCP6_CLIENT_DONT_DESTROY(client);
852 _cleanup_free_ DHCP6Message *message;
857 assert(client->event);
859 r = ioctl(fd, FIONREAD, &buflen);
860 if (r < 0 || buflen <= 0)
861 buflen = DHCP6_MIN_OPTIONS_SIZE;
863 message = malloc0(buflen);
867 len = read(fd, message, buflen);
868 if ((size_t)len < sizeof(DHCP6Message)) {
869 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
873 switch(message->type) {
881 case DHCP6_INFORMATION_REQUEST:
882 case DHCP6_RELAY_FORW:
883 case DHCP6_RELAY_REPL:
886 case DHCP6_ADVERTISE:
888 case DHCP6_RECONFIGURE:
892 log_dhcp6_client(client, "unknown message type %d",
897 if (client->transaction_id != (message->transaction_id &
898 htobe32(0x00ffffff)))
901 switch (client->state) {
902 case DHCP6_STATE_INFORMATION_REQUEST:
903 r = client_receive_reply(client, message, len);
907 client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
909 client_start(client, DHCP6_STATE_STOPPED);
913 case DHCP6_STATE_SOLICITATION:
914 r = client_receive_advertise(client, message, len);
916 if (r == DHCP6_STATE_REQUEST) {
917 client_start(client, r);
922 /* fall through for Soliciation Rapid Commit option check */
923 case DHCP6_STATE_REQUEST:
924 case DHCP6_STATE_RENEW:
925 case DHCP6_STATE_REBIND:
927 r = client_receive_reply(client, message, len);
931 if (r == DHCP6_STATE_BOUND) {
933 r = client_start(client, DHCP6_STATE_BOUND);
935 client_stop(client, r);
939 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
944 case DHCP6_STATE_BOUND:
948 case DHCP6_STATE_STOPPED:
953 log_dhcp6_client(client, "Recv %s",
954 dhcp6_message_type_to_string(message->type));
960 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
963 usec_t timeout, time_now;
964 char time_string[FORMAT_TIMESPAN_MAX];
966 assert_return(client, -EINVAL);
967 assert_return(client->event, -EINVAL);
968 assert_return(client->index > 0, -EINVAL);
969 assert_return(client->state != state, -EINVAL);
971 client->timeout_resend_expire =
972 sd_event_source_unref(client->timeout_resend_expire);
973 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
974 client->retransmit_time = 0;
975 client->retransmit_count = 0;
977 if (client->state == DHCP6_STATE_STOPPED) {
978 time_now = now(clock_boottime_or_monotonic());
980 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
987 case DHCP6_STATE_STOPPED:
988 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
989 client->state = DHCP6_STATE_STOPPED;
995 case DHCP6_STATE_SOLICITATION:
996 client->state = DHCP6_STATE_SOLICITATION;
1000 case DHCP6_STATE_INFORMATION_REQUEST:
1001 case DHCP6_STATE_REQUEST:
1002 case DHCP6_STATE_RENEW:
1003 case DHCP6_STATE_REBIND:
1005 client->state = state;
1009 case DHCP6_STATE_BOUND:
1011 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1012 client->lease->ia.lifetime_t2 == 0xffffffff) {
1014 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1015 be32toh(client->lease->ia.lifetime_t1),
1016 be32toh(client->lease->ia.lifetime_t2));
1021 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1023 log_dhcp6_client(client, "T1 expires in %s",
1024 format_timespan(time_string,
1025 FORMAT_TIMESPAN_MAX,
1028 r = sd_event_add_time(client->event,
1029 &client->lease->ia.timeout_t1,
1030 clock_boottime_or_monotonic(), time_now + timeout,
1031 10 * USEC_PER_SEC, client_timeout_t1,
1036 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1037 client->event_priority);
1041 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
1045 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1047 log_dhcp6_client(client, "T2 expires in %s",
1048 format_timespan(time_string,
1049 FORMAT_TIMESPAN_MAX,
1052 r = sd_event_add_time(client->event,
1053 &client->lease->ia.timeout_t2,
1054 clock_boottime_or_monotonic(), time_now + timeout,
1055 10 * USEC_PER_SEC, client_timeout_t2,
1060 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1061 client->event_priority);
1065 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
1069 client->state = state;
1074 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1075 client->transaction_start = time_now;
1077 r = sd_event_add_time(client->event, &client->timeout_resend,
1078 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1083 r = sd_event_source_set_priority(client->timeout_resend,
1084 client->event_priority);
1088 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1095 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1097 client_stop(client, DHCP6_EVENT_STOP);
1102 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1105 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
1107 assert_return(client, -EINVAL);
1108 assert_return(client->event, -EINVAL);
1109 assert_return(client->index > 0, -EINVAL);
1111 r = client_reset(client);
1115 r = client_ensure_iaid(client);
1119 r = client_ensure_duid(client);
1123 r = dhcp6_network_bind_udp_socket(client->index, NULL);
1129 r = sd_event_add_io(client->event, &client->receive_message,
1130 client->fd, EPOLLIN, client_receive_message,
1135 r = sd_event_source_set_priority(client->receive_message,
1136 client->event_priority);
1140 r = sd_event_source_set_description(client->receive_message,
1141 "dhcp6-receive-message");
1145 if (client->information_request)
1146 state = DHCP6_STATE_INFORMATION_REQUEST;
1148 log_dhcp6_client(client, "Started in %s mode",
1149 client->information_request? "Information request":
1152 return client_start(client, state);
1155 client_reset(client);
1159 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1164 assert_return(client, -EINVAL);
1165 assert_return(!client->event, -EBUSY);
1168 client->event = sd_event_ref(event);
1170 r = sd_event_default(&client->event);
1175 client->event_priority = priority;
1180 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1181 assert_return(client, -EINVAL);
1183 client->event = sd_event_unref(client->event);
1188 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1192 return client->event;
1195 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1197 assert_se(REFCNT_INC(client->n_ref) >= 2);
1202 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1203 if (client && REFCNT_DEC(client->n_ref) == 0) {
1204 client_reset(client);
1206 sd_dhcp6_client_detach_event(client);
1208 free(client->req_opts);
1217 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1219 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1222 assert_return(ret, -EINVAL);
1224 client = new0(sd_dhcp6_client, 1);
1228 client->n_ref = REFCNT_INIT;
1230 client->ia_na.type = DHCP6_OPTION_IA_NA;
1236 client->req_opts_len = ELEMENTSOF(default_req_opts);
1238 client->req_opts = new0(be16_t, client->req_opts_len);
1239 if (!client->req_opts)
1242 for (t = 0; t < client->req_opts_len; t++)
1243 client->req_opts[t] = htobe16(default_req_opts[t]);