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"
30 #include "siphash24.h"
34 #include "network-internal.h"
35 #include "sd-dhcp6-client.h"
36 #include "dhcp6-protocol.h"
37 #include "dhcp6-internal.h"
38 #include "dhcp6-lease-internal.h"
40 #define SYSTEMD_PEN 43793
41 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
43 /* RFC 3315 section 9.1:
44 * A DUID can be no more than 128 octets long (not including the type code).
46 #define MAX_DUID_LEN 128
48 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
50 struct sd_dhcp6_client {
53 enum DHCP6State state;
57 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
61 be32_t transaction_id;
62 usec_t transaction_start;
63 struct sd_dhcp6_lease *lease;
65 bool information_request;
67 size_t req_opts_allocated;
69 sd_event_source *receive_message;
70 usec_t retransmit_time;
71 uint8_t retransmit_count;
72 sd_event_source *timeout_resend;
73 sd_event_source *timeout_resend_expire;
74 sd_dhcp6_client_cb_t cb;
78 uint16_t type; /* DHCP6_DUID_LLT */
84 uint16_t type; /* DHCP6_DUID_EN */
89 uint16_t type; /* DHCP6_DUID_LL */
94 uint16_t type; /* DHCP6_DUID_UUID */
99 uint8_t data[MAX_DUID_LEN];
105 static const uint16_t default_req_opts[] = {
106 DHCP6_OPTION_DNS_SERVERS,
107 DHCP6_OPTION_DOMAIN_LIST,
108 DHCP6_OPTION_NTP_SERVER,
111 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
112 [DHCP6_SOLICIT] = "SOLICIT",
113 [DHCP6_ADVERTISE] = "ADVERTISE",
114 [DHCP6_REQUEST] = "REQUEST",
115 [DHCP6_CONFIRM] = "CONFIRM",
116 [DHCP6_RENEW] = "RENEW",
117 [DHCP6_REBIND] = "REBIND",
118 [DHCP6_REPLY] = "REPLY",
119 [DHCP6_RELEASE] = "RELEASE",
120 [DHCP6_DECLINE] = "DECLINE",
121 [DHCP6_RECONFIGURE] = "RECONFIGURE",
122 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
123 [DHCP6_RELAY_FORW] = "RELAY-FORW",
124 [DHCP6_RELAY_REPL] = "RELAY-REPL",
127 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
129 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
130 [DHCP6_STATUS_SUCCESS] = "Success",
131 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
132 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
133 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
134 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
135 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
138 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
140 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
141 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
143 #define DHCP6_CLIENT_DONT_DESTROY(client) \
144 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
146 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
148 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
149 sd_dhcp6_client_cb_t cb, void *userdata)
151 assert_return(client, -EINVAL);
154 client->userdata = userdata;
159 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
161 assert_return(client, -EINVAL);
162 assert_return(interface_index >= -1, -EINVAL);
164 client->index = interface_index;
169 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
170 size_t addr_len, uint16_t arp_type)
172 assert_return(client, -EINVAL);
173 assert_return(addr, -EINVAL);
174 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
175 assert_return(arp_type > 0, -EINVAL);
177 if (arp_type == ARPHRD_ETHER)
178 assert_return(addr_len == ETH_ALEN, -EINVAL);
179 else if (arp_type == ARPHRD_INFINIBAND)
180 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
184 if (client->mac_addr_len == addr_len &&
185 memcmp(&client->mac_addr, addr, addr_len) == 0)
188 memcpy(&client->mac_addr, addr, addr_len);
189 client->mac_addr_len = addr_len;
190 client->arp_type = arp_type;
195 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
198 assert_return(client, -EINVAL);
199 assert_return(duid, -EINVAL);
200 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
204 if (duid_len <= sizeof(client->duid.llt) - 2)
208 if (duid_len != sizeof(client->duid.en) - 2)
212 if (duid_len <= sizeof(client->duid.ll) - 2)
215 case DHCP6_DUID_UUID:
216 if (duid_len != sizeof(client->duid.uuid) - 2)
220 /* accept unknown type in order to be forward compatible */
224 client->duid.raw.type = htobe16(type);
225 memcpy(&client->duid.raw.data, duid, duid_len);
226 client->duid_len = duid_len + 2; /* +2 for sizeof(type) */
231 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
233 assert_return(client, -EINVAL);
235 client->information_request = enabled;
240 int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
242 assert_return(client, -EINVAL);
243 assert_return(enabled, -EINVAL);
245 *enabled = client->information_request;
250 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
254 assert_return(client, -EINVAL);
255 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
258 case DHCP6_OPTION_DNS_SERVERS:
259 case DHCP6_OPTION_DOMAIN_LIST:
260 case DHCP6_OPTION_SNTP_SERVERS:
261 case DHCP6_OPTION_NTP_SERVER:
268 for (t = 0; t < client->req_opts_len; t++)
269 if (client->req_opts[t] == htobe16(option))
272 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
273 client->req_opts_len + 1))
276 client->req_opts[client->req_opts_len++] = htobe16(option);
281 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
282 assert_return(client, -EINVAL);
283 assert_return(ret, -EINVAL);
288 *ret = sd_dhcp6_lease_ref(client->lease);
293 static void client_notify(sd_dhcp6_client *client, int event) {
295 client->cb(client, event, client->userdata);
298 static int client_reset(sd_dhcp6_client *client) {
299 assert_return(client, -EINVAL);
301 client->receive_message =
302 sd_event_source_unref(client->receive_message);
304 client->fd = safe_close(client->fd);
306 client->transaction_id = 0;
307 client->transaction_start = 0;
309 client->ia_na.timeout_t1 =
310 sd_event_source_unref(client->ia_na.timeout_t1);
311 client->ia_na.timeout_t2 =
312 sd_event_source_unref(client->ia_na.timeout_t2);
314 client->retransmit_time = 0;
315 client->retransmit_count = 0;
316 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
317 client->timeout_resend_expire =
318 sd_event_source_unref(client->timeout_resend_expire);
320 client->state = DHCP6_STATE_STOPPED;
325 static void client_stop(sd_dhcp6_client *client, int error) {
326 DHCP6_CLIENT_DONT_DESTROY(client);
330 client_notify(client, error);
332 client_reset(client);
335 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
336 _cleanup_free_ DHCP6Message *message = NULL;
337 struct in6_addr all_servers =
338 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
339 size_t len, optlen = 512;
345 len = sizeof(DHCP6Message) + optlen;
347 message = malloc0(len);
351 opt = (uint8_t *)(message + 1);
353 message->transaction_id = client->transaction_id;
355 switch(client->state) {
356 case DHCP6_STATE_INFORMATION_REQUEST:
357 message->type = DHCP6_INFORMATION_REQUEST;
361 case DHCP6_STATE_SOLICITATION:
362 message->type = DHCP6_SOLICIT;
364 r = dhcp6_option_append(&opt, &optlen,
365 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
369 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
375 case DHCP6_STATE_REQUEST:
376 case DHCP6_STATE_RENEW:
378 if (client->state == DHCP6_STATE_REQUEST)
379 message->type = DHCP6_REQUEST;
381 message->type = DHCP6_RENEW;
383 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
384 client->lease->serverid_len,
385 client->lease->serverid);
389 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
395 case DHCP6_STATE_REBIND:
396 message->type = DHCP6_REBIND;
398 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
404 case DHCP6_STATE_STOPPED:
405 case DHCP6_STATE_BOUND:
409 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
410 client->req_opts_len * sizeof(be16_t),
415 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
416 client->duid_len, &client->duid);
420 elapsed_usec = time_now - client->transaction_start;
421 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
422 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
424 elapsed_time = 0xffff;
426 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
427 sizeof(elapsed_time), &elapsed_time);
431 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
436 log_dhcp6_client(client, "Sent %s",
437 dhcp6_message_type_to_string(message->type));
442 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
444 sd_dhcp6_client *client = userdata;
446 assert_return(s, -EINVAL);
447 assert_return(client, -EINVAL);
448 assert_return(client->lease, -EINVAL);
450 client->lease->ia.timeout_t2 =
451 sd_event_source_unref(client->lease->ia.timeout_t2);
453 log_dhcp6_client(client, "Timeout T2");
455 client_start(client, DHCP6_STATE_REBIND);
460 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
462 sd_dhcp6_client *client = userdata;
464 assert_return(s, -EINVAL);
465 assert_return(client, -EINVAL);
466 assert_return(client->lease, -EINVAL);
468 client->lease->ia.timeout_t1 =
469 sd_event_source_unref(client->lease->ia.timeout_t1);
471 log_dhcp6_client(client, "Timeout T1");
473 client_start(client, DHCP6_STATE_RENEW);
478 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
480 sd_dhcp6_client *client = userdata;
481 DHCP6_CLIENT_DONT_DESTROY(client);
482 enum DHCP6State state;
486 assert(client->event);
488 state = client->state;
490 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
492 /* RFC 3315, section 18.1.4., says that "...the client may choose to
493 use a Solicit message to locate a new DHCP server..." */
494 if (state == DHCP6_STATE_REBIND)
495 client_start(client, DHCP6_STATE_SOLICITATION);
500 static usec_t client_timeout_compute_random(usec_t val) {
501 return val - val / 10 +
502 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
505 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
508 sd_dhcp6_client *client = userdata;
509 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
510 usec_t max_retransmit_duration = 0;
511 uint8_t max_retransmit_count = 0;
512 char time_string[FORMAT_TIMESPAN_MAX];
517 assert(client->event);
519 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
521 switch (client->state) {
522 case DHCP6_STATE_INFORMATION_REQUEST:
523 init_retransmit_time = DHCP6_INF_TIMEOUT;
524 max_retransmit_time = DHCP6_INF_MAX_RT;
528 case DHCP6_STATE_SOLICITATION:
530 if (client->retransmit_count && client->lease) {
531 client_start(client, DHCP6_STATE_REQUEST);
535 init_retransmit_time = DHCP6_SOL_TIMEOUT;
536 max_retransmit_time = DHCP6_SOL_MAX_RT;
540 case DHCP6_STATE_REQUEST:
541 init_retransmit_time = DHCP6_REQ_TIMEOUT;
542 max_retransmit_time = DHCP6_REQ_MAX_RT;
543 max_retransmit_count = DHCP6_REQ_MAX_RC;
547 case DHCP6_STATE_RENEW:
548 init_retransmit_time = DHCP6_REN_TIMEOUT;
549 max_retransmit_time = DHCP6_REN_MAX_RT;
551 /* RFC 3315, section 18.1.3. says max retransmit duration will
552 be the remaining time until T2. Instead of setting MRD,
553 wait for T2 to trigger with the same end result */
557 case DHCP6_STATE_REBIND:
558 init_retransmit_time = DHCP6_REB_TIMEOUT;
559 max_retransmit_time = DHCP6_REB_MAX_RT;
561 if (!client->timeout_resend_expire) {
562 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
565 client_stop(client, r);
568 max_retransmit_duration = expire * USEC_PER_SEC;
573 case DHCP6_STATE_STOPPED:
574 case DHCP6_STATE_BOUND:
578 if (max_retransmit_count &&
579 client->retransmit_count >= max_retransmit_count) {
580 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
584 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
588 r = client_send_message(client, time_now);
590 client->retransmit_count++;
592 if (!client->retransmit_time) {
593 client->retransmit_time =
594 client_timeout_compute_random(init_retransmit_time);
596 if (client->state == DHCP6_STATE_SOLICITATION)
597 client->retransmit_time += init_retransmit_time / 10;
600 if (max_retransmit_time &&
601 client->retransmit_time > max_retransmit_time / 2)
602 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
604 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
607 log_dhcp6_client(client, "Next retransmission in %s",
608 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
609 client->retransmit_time, 0));
611 r = sd_event_add_time(client->event, &client->timeout_resend,
612 clock_boottime_or_monotonic(),
613 time_now + client->retransmit_time,
614 10 * USEC_PER_MSEC, client_timeout_resend,
619 r = sd_event_source_set_priority(client->timeout_resend,
620 client->event_priority);
624 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
628 if (max_retransmit_duration && !client->timeout_resend_expire) {
630 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
631 max_retransmit_duration / USEC_PER_SEC);
633 r = sd_event_add_time(client->event,
634 &client->timeout_resend_expire,
635 clock_boottime_or_monotonic(),
636 time_now + max_retransmit_duration,
638 client_timeout_resend_expire, client);
642 r = sd_event_source_set_priority(client->timeout_resend_expire,
643 client->event_priority);
647 r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
654 client_stop(client, r);
659 static int client_ensure_iaid(sd_dhcp6_client *client) {
660 /* name is a pointer to memory in the udev_device struct, so must
661 have the same scope */
662 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
663 const char *name = NULL;
668 if (client->ia_na.id)
671 if (detect_container(NULL) <= 0) {
672 /* not in a container, udev will be around */
673 _cleanup_udev_unref_ struct udev *udev;
674 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
680 sprintf(ifindex_str, "n%d", client->index);
681 device = udev_device_new_from_device_id(udev, ifindex_str);
685 if (udev_device_get_is_initialized(device) <= 0)
689 name = net_get_name(device);
693 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
695 /* fall back to mac address if no predictable name available */
696 siphash24((uint8_t*)&id, &client->mac_addr,
697 client->mac_addr_len, HASH_KEY.bytes);
699 /* fold into 32 bits */
700 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
705 static int client_parse_message(sd_dhcp6_client *client,
706 DHCP6Message *message, size_t len,
707 sd_dhcp6_lease *lease) {
709 uint8_t *optval, *option, *id = NULL;
710 uint16_t optcode, status;
711 size_t optlen, id_len;
712 bool clientid = false;
715 option = (uint8_t *)message + sizeof(DHCP6Message);
716 len -= sizeof(DHCP6Message);
718 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
721 case DHCP6_OPTION_CLIENTID:
723 log_dhcp6_client(client, "%s contains multiple clientids",
724 dhcp6_message_type_to_string(message->type));
728 if (optlen != client->duid_len ||
729 memcmp(&client->duid, optval, optlen) != 0) {
730 log_dhcp6_client(client, "%s DUID does not match",
731 dhcp6_message_type_to_string(message->type));
739 case DHCP6_OPTION_SERVERID:
740 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
742 log_dhcp6_client(client, "%s contains multiple serverids",
743 dhcp6_message_type_to_string(message->type));
747 r = dhcp6_lease_set_serverid(lease, optval, optlen);
753 case DHCP6_OPTION_PREFERENCE:
757 r = dhcp6_lease_set_preference(lease, *optval);
763 case DHCP6_OPTION_STATUS_CODE:
767 status = optval[0] << 8 | optval[1];
769 log_dhcp6_client(client, "%s Status %s",
770 dhcp6_message_type_to_string(message->type),
771 dhcp6_message_status_to_string(status));
777 case DHCP6_OPTION_IA_NA:
778 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
779 log_dhcp6_client(client, "Information request ignoring IA NA option");
784 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
786 if (r < 0 && r != -ENOMSG)
789 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
793 if (client->ia_na.id != iaid_lease) {
794 log_dhcp6_client(client, "%s has wrong IAID",
795 dhcp6_message_type_to_string(message->type));
801 case DHCP6_OPTION_RAPID_COMMIT:
802 r = dhcp6_lease_set_rapid_commit(lease);
813 if (r < 0 || !clientid) {
814 log_dhcp6_client(client, "%s has incomplete options",
815 dhcp6_message_type_to_string(message->type));
819 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
820 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
822 log_dhcp6_client(client, "%s has no server id",
823 dhcp6_message_type_to_string(message->type));
829 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
833 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
836 if (reply->type != DHCP6_REPLY)
839 r = dhcp6_lease_new(&lease);
843 r = client_parse_message(client, reply, len, lease);
847 if (client->state == DHCP6_STATE_SOLICITATION) {
848 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
857 dhcp6_lease_clear_timers(&client->lease->ia);
858 client->lease = sd_dhcp6_lease_unref(client->lease);
861 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
862 client->lease = lease;
866 return DHCP6_STATE_BOUND;
869 static int client_receive_advertise(sd_dhcp6_client *client,
870 DHCP6Message *advertise, size_t len) {
872 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
873 uint8_t pref_advertise = 0, pref_lease = 0;
875 if (advertise->type != DHCP6_ADVERTISE)
878 r = dhcp6_lease_new(&lease);
882 r = client_parse_message(client, advertise, len, lease);
886 r = dhcp6_lease_get_preference(lease, &pref_advertise);
890 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
892 if (r < 0 || pref_advertise > pref_lease) {
893 sd_dhcp6_lease_unref(client->lease);
894 client->lease = lease;
899 if (pref_advertise == 255 || client->retransmit_count > 1)
900 r = DHCP6_STATE_REQUEST;
905 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
907 sd_dhcp6_client *client = userdata;
908 DHCP6_CLIENT_DONT_DESTROY(client);
909 _cleanup_free_ DHCP6Message *message;
914 assert(client->event);
916 r = ioctl(fd, FIONREAD, &buflen);
917 if (r < 0 || buflen <= 0)
918 buflen = DHCP6_MIN_OPTIONS_SIZE;
920 message = malloc0(buflen);
924 len = read(fd, message, buflen);
925 if ((size_t)len < sizeof(DHCP6Message)) {
926 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
930 switch(message->type) {
938 case DHCP6_INFORMATION_REQUEST:
939 case DHCP6_RELAY_FORW:
940 case DHCP6_RELAY_REPL:
943 case DHCP6_ADVERTISE:
945 case DHCP6_RECONFIGURE:
949 log_dhcp6_client(client, "unknown message type %d",
954 if (client->transaction_id != (message->transaction_id &
955 htobe32(0x00ffffff)))
958 switch (client->state) {
959 case DHCP6_STATE_INFORMATION_REQUEST:
960 r = client_receive_reply(client, message, len);
964 client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
966 client_start(client, DHCP6_STATE_STOPPED);
970 case DHCP6_STATE_SOLICITATION:
971 r = client_receive_advertise(client, message, len);
973 if (r == DHCP6_STATE_REQUEST) {
974 client_start(client, r);
979 /* fall through for Soliciation Rapid Commit option check */
980 case DHCP6_STATE_REQUEST:
981 case DHCP6_STATE_RENEW:
982 case DHCP6_STATE_REBIND:
984 r = client_receive_reply(client, message, len);
988 if (r == DHCP6_STATE_BOUND) {
990 r = client_start(client, DHCP6_STATE_BOUND);
992 client_stop(client, r);
996 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
1001 case DHCP6_STATE_BOUND:
1005 case DHCP6_STATE_STOPPED:
1010 log_dhcp6_client(client, "Recv %s",
1011 dhcp6_message_type_to_string(message->type));
1017 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
1020 usec_t timeout, time_now;
1021 char time_string[FORMAT_TIMESPAN_MAX];
1023 assert_return(client, -EINVAL);
1024 assert_return(client->event, -EINVAL);
1025 assert_return(client->index > 0, -EINVAL);
1026 assert_return(client->state != state, -EINVAL);
1028 log_dhcp6_client(client, "client state %d new state %d", client->state, state);
1029 client->timeout_resend_expire =
1030 sd_event_source_unref(client->timeout_resend_expire);
1031 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1032 client->retransmit_time = 0;
1033 client->retransmit_count = 0;
1035 if (client->state == DHCP6_STATE_STOPPED) {
1036 time_now = now(clock_boottime_or_monotonic());
1038 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
1045 case DHCP6_STATE_STOPPED:
1046 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1047 client->state = DHCP6_STATE_STOPPED;
1053 case DHCP6_STATE_SOLICITATION:
1054 client->state = DHCP6_STATE_SOLICITATION;
1058 case DHCP6_STATE_INFORMATION_REQUEST:
1059 case DHCP6_STATE_REQUEST:
1060 case DHCP6_STATE_RENEW:
1061 case DHCP6_STATE_REBIND:
1063 client->state = state;
1067 case DHCP6_STATE_BOUND:
1069 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1070 client->lease->ia.lifetime_t2 == 0xffffffff) {
1072 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1073 be32toh(client->lease->ia.lifetime_t1),
1074 be32toh(client->lease->ia.lifetime_t2));
1079 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1081 log_dhcp6_client(client, "T1 expires in %s",
1082 format_timespan(time_string,
1083 FORMAT_TIMESPAN_MAX,
1086 r = sd_event_add_time(client->event,
1087 &client->lease->ia.timeout_t1,
1088 clock_boottime_or_monotonic(), time_now + timeout,
1089 10 * USEC_PER_SEC, client_timeout_t1,
1094 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1095 client->event_priority);
1099 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
1103 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1105 log_dhcp6_client(client, "T2 expires in %s",
1106 format_timespan(time_string,
1107 FORMAT_TIMESPAN_MAX,
1110 r = sd_event_add_time(client->event,
1111 &client->lease->ia.timeout_t2,
1112 clock_boottime_or_monotonic(), time_now + timeout,
1113 10 * USEC_PER_SEC, client_timeout_t2,
1118 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1119 client->event_priority);
1123 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
1127 client->state = state;
1132 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1133 client->transaction_start = time_now;
1135 r = sd_event_add_time(client->event, &client->timeout_resend,
1136 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1141 r = sd_event_source_set_priority(client->timeout_resend,
1142 client->event_priority);
1146 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1153 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1155 client_stop(client, DHCP6_EVENT_STOP);
1160 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1163 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
1165 assert_return(client, -EINVAL);
1166 assert_return(client->event, -EINVAL);
1167 assert_return(client->index > 0, -EINVAL);
1169 r = client_reset(client);
1173 r = client_ensure_iaid(client);
1177 r = dhcp6_network_bind_udp_socket(client->index, NULL);
1183 r = sd_event_add_io(client->event, &client->receive_message,
1184 client->fd, EPOLLIN, client_receive_message,
1189 r = sd_event_source_set_priority(client->receive_message,
1190 client->event_priority);
1194 r = sd_event_source_set_description(client->receive_message,
1195 "dhcp6-receive-message");
1199 if (client->information_request)
1200 state = DHCP6_STATE_INFORMATION_REQUEST;
1202 log_dhcp6_client(client, "Started in %s mode",
1203 client->information_request? "Information request":
1206 return client_start(client, state);
1209 client_reset(client);
1213 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1218 assert_return(client, -EINVAL);
1219 assert_return(!client->event, -EBUSY);
1222 client->event = sd_event_ref(event);
1224 r = sd_event_default(&client->event);
1229 client->event_priority = priority;
1234 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1235 assert_return(client, -EINVAL);
1237 client->event = sd_event_unref(client->event);
1242 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1246 return client->event;
1249 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1251 assert_se(REFCNT_INC(client->n_ref) >= 2);
1256 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1257 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1258 client_reset(client);
1260 sd_dhcp6_client_detach_event(client);
1262 free(client->req_opts);
1271 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1273 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1274 sd_id128_t machine_id;
1278 assert_return(ret, -EINVAL);
1280 client = new0(sd_dhcp6_client, 1);
1284 client->n_ref = REFCNT_INIT;
1286 client->ia_na.type = DHCP6_OPTION_IA_NA;
1292 /* initialize DUID */
1293 client->duid.en.type = htobe16(DHCP6_DUID_EN);
1294 client->duid.en.pen = htobe32(SYSTEMD_PEN);
1295 client->duid_len = sizeof(client->duid.en);
1297 r = sd_id128_get_machine(&machine_id);
1301 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1303 siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1305 client->req_opts_len = ELEMENTSOF(default_req_opts);
1307 client->req_opts = new0(be16_t, client->req_opts_len);
1308 if (!client->req_opts)
1311 for (t = 0; t < client->req_opts_len; t++)
1312 client->req_opts[t] = htobe16(default_req_opts[t]);