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"
39 #include "dhcp-identifier.h"
41 #define SYSTEMD_PEN 43793
42 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
44 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
46 struct sd_dhcp6_client {
49 enum DHCP6State state;
53 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
57 be32_t transaction_id;
58 usec_t transaction_start;
59 struct sd_dhcp6_lease *lease;
61 bool information_request;
63 size_t req_opts_allocated;
65 sd_event_source *receive_message;
66 usec_t retransmit_time;
67 uint8_t retransmit_count;
68 sd_event_source *timeout_resend;
69 sd_event_source *timeout_resend_expire;
70 sd_dhcp6_client_cb_t cb;
76 static const uint16_t default_req_opts[] = {
77 DHCP6_OPTION_DNS_SERVERS,
78 DHCP6_OPTION_DOMAIN_LIST,
79 DHCP6_OPTION_NTP_SERVER,
82 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
83 [DHCP6_SOLICIT] = "SOLICIT",
84 [DHCP6_ADVERTISE] = "ADVERTISE",
85 [DHCP6_REQUEST] = "REQUEST",
86 [DHCP6_CONFIRM] = "CONFIRM",
87 [DHCP6_RENEW] = "RENEW",
88 [DHCP6_REBIND] = "REBIND",
89 [DHCP6_REPLY] = "REPLY",
90 [DHCP6_RELEASE] = "RELEASE",
91 [DHCP6_DECLINE] = "DECLINE",
92 [DHCP6_RECONFIGURE] = "RECONFIGURE",
93 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
94 [DHCP6_RELAY_FORW] = "RELAY-FORW",
95 [DHCP6_RELAY_REPL] = "RELAY-REPL",
98 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
100 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
101 [DHCP6_STATUS_SUCCESS] = "Success",
102 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
103 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
104 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
105 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
106 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
109 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
111 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
112 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
114 #define DHCP6_CLIENT_DONT_DESTROY(client) \
115 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
117 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
119 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
120 sd_dhcp6_client_cb_t cb, void *userdata)
122 assert_return(client, -EINVAL);
125 client->userdata = userdata;
130 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
132 assert_return(client, -EINVAL);
133 assert_return(interface_index >= -1, -EINVAL);
135 client->index = interface_index;
140 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
141 size_t addr_len, uint16_t arp_type)
143 assert_return(client, -EINVAL);
144 assert_return(addr, -EINVAL);
145 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
146 assert_return(arp_type > 0, -EINVAL);
148 if (arp_type == ARPHRD_ETHER)
149 assert_return(addr_len == ETH_ALEN, -EINVAL);
150 else if (arp_type == ARPHRD_INFINIBAND)
151 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
155 if (client->mac_addr_len == addr_len &&
156 memcmp(&client->mac_addr, addr, addr_len) == 0)
159 memcpy(&client->mac_addr, addr, addr_len);
160 client->mac_addr_len = addr_len;
161 client->arp_type = arp_type;
166 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
169 assert_return(client, -EINVAL);
170 assert_return(duid, -EINVAL);
171 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
175 if (duid_len <= sizeof(client->duid.llt))
179 if (duid_len != sizeof(client->duid.en))
183 if (duid_len <= sizeof(client->duid.ll))
186 case DHCP6_DUID_UUID:
187 if (duid_len != sizeof(client->duid.uuid))
191 /* accept unknown type in order to be forward compatible */
195 client->duid.type = htobe16(type);
196 memcpy(&client->duid.raw.data, duid, duid_len);
197 client->duid_len = duid_len + sizeof(client->duid.type);
202 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client,
204 assert_return(client, -EINVAL);
206 client->information_request = enabled;
211 int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client,
213 assert_return(client, -EINVAL);
214 assert_return(enabled, -EINVAL);
216 *enabled = client->information_request;
221 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
225 assert_return(client, -EINVAL);
226 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
229 case DHCP6_OPTION_DNS_SERVERS:
230 case DHCP6_OPTION_DOMAIN_LIST:
231 case DHCP6_OPTION_SNTP_SERVERS:
232 case DHCP6_OPTION_NTP_SERVER:
239 for (t = 0; t < client->req_opts_len; t++)
240 if (client->req_opts[t] == htobe16(option))
243 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
244 client->req_opts_len + 1))
247 client->req_opts[client->req_opts_len++] = htobe16(option);
252 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
253 assert_return(client, -EINVAL);
254 assert_return(ret, -EINVAL);
259 *ret = sd_dhcp6_lease_ref(client->lease);
264 static void client_notify(sd_dhcp6_client *client, int event) {
266 client->cb(client, event, client->userdata);
269 static int client_reset(sd_dhcp6_client *client) {
270 assert_return(client, -EINVAL);
272 client->receive_message =
273 sd_event_source_unref(client->receive_message);
275 client->fd = safe_close(client->fd);
277 client->transaction_id = 0;
278 client->transaction_start = 0;
280 client->ia_na.timeout_t1 =
281 sd_event_source_unref(client->ia_na.timeout_t1);
282 client->ia_na.timeout_t2 =
283 sd_event_source_unref(client->ia_na.timeout_t2);
285 client->retransmit_time = 0;
286 client->retransmit_count = 0;
287 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
288 client->timeout_resend_expire =
289 sd_event_source_unref(client->timeout_resend_expire);
291 client->state = DHCP6_STATE_STOPPED;
296 static void client_stop(sd_dhcp6_client *client, int error) {
297 DHCP6_CLIENT_DONT_DESTROY(client);
301 client_notify(client, error);
303 client_reset(client);
306 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
307 _cleanup_free_ DHCP6Message *message = NULL;
308 struct in6_addr all_servers =
309 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
310 size_t len, optlen = 512;
316 len = sizeof(DHCP6Message) + optlen;
318 message = malloc0(len);
322 opt = (uint8_t *)(message + 1);
324 message->transaction_id = client->transaction_id;
326 switch(client->state) {
327 case DHCP6_STATE_INFORMATION_REQUEST:
328 message->type = DHCP6_INFORMATION_REQUEST;
332 case DHCP6_STATE_SOLICITATION:
333 message->type = DHCP6_SOLICIT;
335 r = dhcp6_option_append(&opt, &optlen,
336 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
340 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
346 case DHCP6_STATE_REQUEST:
347 case DHCP6_STATE_RENEW:
349 if (client->state == DHCP6_STATE_REQUEST)
350 message->type = DHCP6_REQUEST;
352 message->type = DHCP6_RENEW;
354 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
355 client->lease->serverid_len,
356 client->lease->serverid);
360 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
366 case DHCP6_STATE_REBIND:
367 message->type = DHCP6_REBIND;
369 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
375 case DHCP6_STATE_STOPPED:
376 case DHCP6_STATE_BOUND:
380 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
381 client->req_opts_len * sizeof(be16_t),
386 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
387 client->duid_len, &client->duid);
391 elapsed_usec = time_now - client->transaction_start;
392 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
393 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
395 elapsed_time = 0xffff;
397 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
398 sizeof(elapsed_time), &elapsed_time);
402 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
407 log_dhcp6_client(client, "Sent %s",
408 dhcp6_message_type_to_string(message->type));
413 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
415 sd_dhcp6_client *client = userdata;
417 assert_return(s, -EINVAL);
418 assert_return(client, -EINVAL);
419 assert_return(client->lease, -EINVAL);
421 client->lease->ia.timeout_t2 =
422 sd_event_source_unref(client->lease->ia.timeout_t2);
424 log_dhcp6_client(client, "Timeout T2");
426 client_start(client, DHCP6_STATE_REBIND);
431 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
433 sd_dhcp6_client *client = userdata;
435 assert_return(s, -EINVAL);
436 assert_return(client, -EINVAL);
437 assert_return(client->lease, -EINVAL);
439 client->lease->ia.timeout_t1 =
440 sd_event_source_unref(client->lease->ia.timeout_t1);
442 log_dhcp6_client(client, "Timeout T1");
444 client_start(client, DHCP6_STATE_RENEW);
449 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
451 sd_dhcp6_client *client = userdata;
452 DHCP6_CLIENT_DONT_DESTROY(client);
453 enum DHCP6State state;
457 assert(client->event);
459 state = client->state;
461 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
463 /* RFC 3315, section 18.1.4., says that "...the client may choose to
464 use a Solicit message to locate a new DHCP server..." */
465 if (state == DHCP6_STATE_REBIND)
466 client_start(client, DHCP6_STATE_SOLICITATION);
471 static usec_t client_timeout_compute_random(usec_t val) {
472 return val - val / 10 +
473 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
476 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
479 sd_dhcp6_client *client = userdata;
480 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
481 usec_t max_retransmit_duration = 0;
482 uint8_t max_retransmit_count = 0;
483 char time_string[FORMAT_TIMESPAN_MAX];
488 assert(client->event);
490 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
492 switch (client->state) {
493 case DHCP6_STATE_INFORMATION_REQUEST:
494 init_retransmit_time = DHCP6_INF_TIMEOUT;
495 max_retransmit_time = DHCP6_INF_MAX_RT;
499 case DHCP6_STATE_SOLICITATION:
501 if (client->retransmit_count && client->lease) {
502 client_start(client, DHCP6_STATE_REQUEST);
506 init_retransmit_time = DHCP6_SOL_TIMEOUT;
507 max_retransmit_time = DHCP6_SOL_MAX_RT;
511 case DHCP6_STATE_REQUEST:
512 init_retransmit_time = DHCP6_REQ_TIMEOUT;
513 max_retransmit_time = DHCP6_REQ_MAX_RT;
514 max_retransmit_count = DHCP6_REQ_MAX_RC;
518 case DHCP6_STATE_RENEW:
519 init_retransmit_time = DHCP6_REN_TIMEOUT;
520 max_retransmit_time = DHCP6_REN_MAX_RT;
522 /* RFC 3315, section 18.1.3. says max retransmit duration will
523 be the remaining time until T2. Instead of setting MRD,
524 wait for T2 to trigger with the same end result */
528 case DHCP6_STATE_REBIND:
529 init_retransmit_time = DHCP6_REB_TIMEOUT;
530 max_retransmit_time = DHCP6_REB_MAX_RT;
532 if (!client->timeout_resend_expire) {
533 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
536 client_stop(client, r);
539 max_retransmit_duration = expire * USEC_PER_SEC;
544 case DHCP6_STATE_STOPPED:
545 case DHCP6_STATE_BOUND:
549 if (max_retransmit_count &&
550 client->retransmit_count >= max_retransmit_count) {
551 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
555 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
559 r = client_send_message(client, time_now);
561 client->retransmit_count++;
563 if (!client->retransmit_time) {
564 client->retransmit_time =
565 client_timeout_compute_random(init_retransmit_time);
567 if (client->state == DHCP6_STATE_SOLICITATION)
568 client->retransmit_time += init_retransmit_time / 10;
571 if (max_retransmit_time &&
572 client->retransmit_time > max_retransmit_time / 2)
573 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
575 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
578 log_dhcp6_client(client, "Next retransmission in %s",
579 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
580 client->retransmit_time, 0));
582 r = sd_event_add_time(client->event, &client->timeout_resend,
583 clock_boottime_or_monotonic(),
584 time_now + client->retransmit_time,
585 10 * USEC_PER_MSEC, client_timeout_resend,
590 r = sd_event_source_set_priority(client->timeout_resend,
591 client->event_priority);
595 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
599 if (max_retransmit_duration && !client->timeout_resend_expire) {
601 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
602 max_retransmit_duration / USEC_PER_SEC);
604 r = sd_event_add_time(client->event,
605 &client->timeout_resend_expire,
606 clock_boottime_or_monotonic(),
607 time_now + max_retransmit_duration,
609 client_timeout_resend_expire, client);
613 r = sd_event_source_set_priority(client->timeout_resend_expire,
614 client->event_priority);
618 r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
625 client_stop(client, r);
630 static int client_ensure_iaid(sd_dhcp6_client *client) {
631 /* name is a pointer to memory in the udev_device struct, so must
632 have the same scope */
633 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
634 const char *name = NULL;
639 if (client->ia_na.id)
642 if (detect_container(NULL) <= 0) {
643 /* not in a container, udev will be around */
644 _cleanup_udev_unref_ struct udev *udev;
645 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
651 sprintf(ifindex_str, "n%d", client->index);
652 device = udev_device_new_from_device_id(udev, ifindex_str);
656 if (udev_device_get_is_initialized(device) <= 0)
660 name = net_get_name(device);
664 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
666 /* fall back to mac address if no predictable name available */
667 siphash24((uint8_t*)&id, &client->mac_addr,
668 client->mac_addr_len, HASH_KEY.bytes);
670 /* fold into 32 bits */
671 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
676 static int client_parse_message(sd_dhcp6_client *client,
677 DHCP6Message *message, size_t len,
678 sd_dhcp6_lease *lease) {
680 uint8_t *optval, *option, *id = NULL;
681 uint16_t optcode, status;
682 size_t optlen, id_len;
683 bool clientid = false;
686 option = (uint8_t *)message + sizeof(DHCP6Message);
687 len -= sizeof(DHCP6Message);
689 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
692 case DHCP6_OPTION_CLIENTID:
694 log_dhcp6_client(client, "%s contains multiple clientids",
695 dhcp6_message_type_to_string(message->type));
699 if (optlen != client->duid_len ||
700 memcmp(&client->duid, optval, optlen) != 0) {
701 log_dhcp6_client(client, "%s DUID does not match",
702 dhcp6_message_type_to_string(message->type));
710 case DHCP6_OPTION_SERVERID:
711 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
713 log_dhcp6_client(client, "%s contains multiple serverids",
714 dhcp6_message_type_to_string(message->type));
718 r = dhcp6_lease_set_serverid(lease, optval, optlen);
724 case DHCP6_OPTION_PREFERENCE:
728 r = dhcp6_lease_set_preference(lease, *optval);
734 case DHCP6_OPTION_STATUS_CODE:
738 status = optval[0] << 8 | optval[1];
740 log_dhcp6_client(client, "%s Status %s",
741 dhcp6_message_type_to_string(message->type),
742 dhcp6_message_status_to_string(status));
748 case DHCP6_OPTION_IA_NA:
749 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
750 log_dhcp6_client(client, "Information request ignoring IA NA option");
755 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
757 if (r < 0 && r != -ENOMSG)
760 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
764 if (client->ia_na.id != iaid_lease) {
765 log_dhcp6_client(client, "%s has wrong IAID",
766 dhcp6_message_type_to_string(message->type));
772 case DHCP6_OPTION_RAPID_COMMIT:
773 r = dhcp6_lease_set_rapid_commit(lease);
784 if (r < 0 || !clientid) {
785 log_dhcp6_client(client, "%s has incomplete options",
786 dhcp6_message_type_to_string(message->type));
790 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
791 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
793 log_dhcp6_client(client, "%s has no server id",
794 dhcp6_message_type_to_string(message->type));
800 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
804 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
807 if (reply->type != DHCP6_REPLY)
810 r = dhcp6_lease_new(&lease);
814 r = client_parse_message(client, reply, len, lease);
818 if (client->state == DHCP6_STATE_SOLICITATION) {
819 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
828 dhcp6_lease_clear_timers(&client->lease->ia);
829 client->lease = sd_dhcp6_lease_unref(client->lease);
832 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
833 client->lease = lease;
837 return DHCP6_STATE_BOUND;
840 static int client_receive_advertise(sd_dhcp6_client *client,
841 DHCP6Message *advertise, size_t len) {
843 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
844 uint8_t pref_advertise = 0, pref_lease = 0;
846 if (advertise->type != DHCP6_ADVERTISE)
849 r = dhcp6_lease_new(&lease);
853 r = client_parse_message(client, advertise, len, lease);
857 r = dhcp6_lease_get_preference(lease, &pref_advertise);
861 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
863 if (r < 0 || pref_advertise > pref_lease) {
864 sd_dhcp6_lease_unref(client->lease);
865 client->lease = lease;
870 if (pref_advertise == 255 || client->retransmit_count > 1)
871 r = DHCP6_STATE_REQUEST;
876 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
878 sd_dhcp6_client *client = userdata;
879 DHCP6_CLIENT_DONT_DESTROY(client);
880 _cleanup_free_ DHCP6Message *message;
885 assert(client->event);
887 r = ioctl(fd, FIONREAD, &buflen);
888 if (r < 0 || buflen <= 0)
889 buflen = DHCP6_MIN_OPTIONS_SIZE;
891 message = malloc0(buflen);
895 len = read(fd, message, buflen);
896 if ((size_t)len < sizeof(DHCP6Message)) {
897 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
901 switch(message->type) {
909 case DHCP6_INFORMATION_REQUEST:
910 case DHCP6_RELAY_FORW:
911 case DHCP6_RELAY_REPL:
914 case DHCP6_ADVERTISE:
916 case DHCP6_RECONFIGURE:
920 log_dhcp6_client(client, "unknown message type %d",
925 if (client->transaction_id != (message->transaction_id &
926 htobe32(0x00ffffff)))
929 switch (client->state) {
930 case DHCP6_STATE_INFORMATION_REQUEST:
931 r = client_receive_reply(client, message, len);
935 client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST);
937 client_start(client, DHCP6_STATE_STOPPED);
941 case DHCP6_STATE_SOLICITATION:
942 r = client_receive_advertise(client, message, len);
944 if (r == DHCP6_STATE_REQUEST) {
945 client_start(client, r);
950 /* fall through for Soliciation Rapid Commit option check */
951 case DHCP6_STATE_REQUEST:
952 case DHCP6_STATE_RENEW:
953 case DHCP6_STATE_REBIND:
955 r = client_receive_reply(client, message, len);
959 if (r == DHCP6_STATE_BOUND) {
961 r = client_start(client, DHCP6_STATE_BOUND);
963 client_stop(client, r);
967 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
972 case DHCP6_STATE_BOUND:
976 case DHCP6_STATE_STOPPED:
981 log_dhcp6_client(client, "Recv %s",
982 dhcp6_message_type_to_string(message->type));
988 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
991 usec_t timeout, time_now;
992 char time_string[FORMAT_TIMESPAN_MAX];
994 assert_return(client, -EINVAL);
995 assert_return(client->event, -EINVAL);
996 assert_return(client->index > 0, -EINVAL);
997 assert_return(client->state != state, -EINVAL);
999 client->timeout_resend_expire =
1000 sd_event_source_unref(client->timeout_resend_expire);
1001 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1002 client->retransmit_time = 0;
1003 client->retransmit_count = 0;
1005 if (client->state == DHCP6_STATE_STOPPED) {
1006 time_now = now(clock_boottime_or_monotonic());
1008 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
1015 case DHCP6_STATE_STOPPED:
1016 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1017 client->state = DHCP6_STATE_STOPPED;
1023 case DHCP6_STATE_SOLICITATION:
1024 client->state = DHCP6_STATE_SOLICITATION;
1028 case DHCP6_STATE_INFORMATION_REQUEST:
1029 case DHCP6_STATE_REQUEST:
1030 case DHCP6_STATE_RENEW:
1031 case DHCP6_STATE_REBIND:
1033 client->state = state;
1037 case DHCP6_STATE_BOUND:
1039 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1040 client->lease->ia.lifetime_t2 == 0xffffffff) {
1042 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1043 be32toh(client->lease->ia.lifetime_t1),
1044 be32toh(client->lease->ia.lifetime_t2));
1049 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1051 log_dhcp6_client(client, "T1 expires in %s",
1052 format_timespan(time_string,
1053 FORMAT_TIMESPAN_MAX,
1056 r = sd_event_add_time(client->event,
1057 &client->lease->ia.timeout_t1,
1058 clock_boottime_or_monotonic(), time_now + timeout,
1059 10 * USEC_PER_SEC, client_timeout_t1,
1064 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1065 client->event_priority);
1069 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
1073 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1075 log_dhcp6_client(client, "T2 expires in %s",
1076 format_timespan(time_string,
1077 FORMAT_TIMESPAN_MAX,
1080 r = sd_event_add_time(client->event,
1081 &client->lease->ia.timeout_t2,
1082 clock_boottime_or_monotonic(), time_now + timeout,
1083 10 * USEC_PER_SEC, client_timeout_t2,
1088 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1089 client->event_priority);
1093 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
1097 client->state = state;
1102 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1103 client->transaction_start = time_now;
1105 r = sd_event_add_time(client->event, &client->timeout_resend,
1106 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1111 r = sd_event_source_set_priority(client->timeout_resend,
1112 client->event_priority);
1116 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1123 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1125 client_stop(client, DHCP6_EVENT_STOP);
1130 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1133 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
1135 assert_return(client, -EINVAL);
1136 assert_return(client->event, -EINVAL);
1137 assert_return(client->index > 0, -EINVAL);
1139 r = client_reset(client);
1143 r = client_ensure_iaid(client);
1147 r = dhcp6_network_bind_udp_socket(client->index, NULL);
1153 r = sd_event_add_io(client->event, &client->receive_message,
1154 client->fd, EPOLLIN, client_receive_message,
1159 r = sd_event_source_set_priority(client->receive_message,
1160 client->event_priority);
1164 r = sd_event_source_set_description(client->receive_message,
1165 "dhcp6-receive-message");
1169 if (client->information_request)
1170 state = DHCP6_STATE_INFORMATION_REQUEST;
1172 log_dhcp6_client(client, "Started in %s mode",
1173 client->information_request? "Information request":
1176 return client_start(client, state);
1179 client_reset(client);
1183 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1188 assert_return(client, -EINVAL);
1189 assert_return(!client->event, -EBUSY);
1192 client->event = sd_event_ref(event);
1194 r = sd_event_default(&client->event);
1199 client->event_priority = priority;
1204 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1205 assert_return(client, -EINVAL);
1207 client->event = sd_event_unref(client->event);
1212 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1216 return client->event;
1219 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1221 assert_se(REFCNT_INC(client->n_ref) >= 2);
1226 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1227 if (client && REFCNT_DEC(client->n_ref) == 0) {
1228 client_reset(client);
1230 sd_dhcp6_client_detach_event(client);
1232 free(client->req_opts);
1241 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1243 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1244 sd_id128_t machine_id;
1248 assert_return(ret, -EINVAL);
1250 client = new0(sd_dhcp6_client, 1);
1254 client->n_ref = REFCNT_INIT;
1256 client->ia_na.type = DHCP6_OPTION_IA_NA;
1262 /* initialize DUID */
1263 client->duid.type = htobe16(DHCP6_DUID_EN);
1264 client->duid.en.pen = htobe32(SYSTEMD_PEN);
1265 client->duid_len = sizeof(client->duid.en);
1267 r = sd_id128_get_machine(&machine_id);
1271 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1273 siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1275 client->req_opts_len = ELEMENTSOF(default_req_opts);
1277 client->req_opts = new0(be16_t, client->req_opts_len);
1278 if (!client->req_opts)
1281 for (t = 0; t < client->req_opts_len; t++)
1282 client->req_opts[t] = htobe16(default_req_opts[t]);