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;
66 size_t req_opts_allocated;
68 sd_event_source *receive_message;
69 usec_t retransmit_time;
70 uint8_t retransmit_count;
71 sd_event_source *timeout_resend;
72 sd_event_source *timeout_resend_expire;
73 sd_dhcp6_client_cb_t cb;
77 uint16_t type; /* DHCP6_DUID_LLT */
83 uint16_t type; /* DHCP6_DUID_EN */
88 uint16_t type; /* DHCP6_DUID_LL */
93 uint16_t type; /* DHCP6_DUID_UUID */
98 uint8_t data[MAX_DUID_LEN];
104 static const uint16_t default_req_opts[] = {
105 DHCP6_OPTION_DNS_SERVERS,
106 DHCP6_OPTION_DOMAIN_LIST,
107 DHCP6_OPTION_NTP_SERVER,
110 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
111 [DHCP6_SOLICIT] = "SOLICIT",
112 [DHCP6_ADVERTISE] = "ADVERTISE",
113 [DHCP6_REQUEST] = "REQUEST",
114 [DHCP6_CONFIRM] = "CONFIRM",
115 [DHCP6_RENEW] = "RENEW",
116 [DHCP6_REBIND] = "REBIND",
117 [DHCP6_REPLY] = "REPLY",
118 [DHCP6_RELEASE] = "RELEASE",
119 [DHCP6_DECLINE] = "DECLINE",
120 [DHCP6_RECONFIGURE] = "RECONFIGURE",
121 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
122 [DHCP6_RELAY_FORW] = "RELAY-FORW",
123 [DHCP6_RELAY_REPL] = "RELAY-REPL",
126 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
128 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
129 [DHCP6_STATUS_SUCCESS] = "Success",
130 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
131 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
132 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
133 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
134 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
137 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
139 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
140 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
142 #define DHCP6_CLIENT_DONT_DESTROY(client) \
143 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
145 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
147 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
148 sd_dhcp6_client_cb_t cb, void *userdata)
150 assert_return(client, -EINVAL);
153 client->userdata = userdata;
158 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
160 assert_return(client, -EINVAL);
161 assert_return(interface_index >= -1, -EINVAL);
163 client->index = interface_index;
168 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
169 size_t addr_len, uint16_t arp_type)
171 assert_return(client, -EINVAL);
172 assert_return(addr, -EINVAL);
173 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
174 assert_return(arp_type > 0, -EINVAL);
176 if (arp_type == ARPHRD_ETHER)
177 assert_return(addr_len == ETH_ALEN, -EINVAL);
178 else if (arp_type == ARPHRD_INFINIBAND)
179 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
183 if (client->mac_addr_len == addr_len &&
184 memcmp(&client->mac_addr, addr, addr_len) == 0)
187 memcpy(&client->mac_addr, addr, addr_len);
188 client->mac_addr_len = addr_len;
189 client->arp_type = arp_type;
194 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
197 assert_return(client, -EINVAL);
198 assert_return(duid, -EINVAL);
199 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
203 if (duid_len <= sizeof(client->duid.llt) - 2)
207 if (duid_len != sizeof(client->duid.en) - 2)
211 if (duid_len <= sizeof(client->duid.ll) - 2)
214 case DHCP6_DUID_UUID:
215 if (duid_len != sizeof(client->duid.uuid) - 2)
219 /* accept unknown type in order to be forward compatible */
223 client->duid.raw.type = htobe16(type);
224 memcpy(&client->duid.raw.data, duid, duid_len);
225 client->duid_len = duid_len + 2; /* +2 for sizeof(type) */
230 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
234 assert_return(client, -EINVAL);
235 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
238 case DHCP6_OPTION_DNS_SERVERS:
239 case DHCP6_OPTION_DOMAIN_LIST:
240 case DHCP6_OPTION_SNTP_SERVERS:
241 case DHCP6_OPTION_NTP_SERVER:
248 for (t = 0; t < client->req_opts_len; t++)
249 if (client->req_opts[t] == htobe16(option))
252 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
253 client->req_opts_len + 1))
256 client->req_opts[client->req_opts_len++] = htobe16(option);
261 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
262 assert_return(client, -EINVAL);
263 assert_return(ret, -EINVAL);
268 *ret = sd_dhcp6_lease_ref(client->lease);
273 static void client_notify(sd_dhcp6_client *client, int event) {
275 client->cb(client, event, client->userdata);
278 static int client_reset(sd_dhcp6_client *client) {
279 assert_return(client, -EINVAL);
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_SOLICITATION:
337 message->type = DHCP6_SOLICIT;
339 r = dhcp6_option_append(&opt, &optlen,
340 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
344 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
350 case DHCP6_STATE_REQUEST:
351 case DHCP6_STATE_RENEW:
353 if (client->state == DHCP6_STATE_REQUEST)
354 message->type = DHCP6_REQUEST;
356 message->type = DHCP6_RENEW;
358 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
359 client->lease->serverid_len,
360 client->lease->serverid);
364 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
370 case DHCP6_STATE_REBIND:
371 message->type = DHCP6_REBIND;
373 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
379 case DHCP6_STATE_STOPPED:
380 case DHCP6_STATE_BOUND:
384 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
385 client->req_opts_len * sizeof(be16_t),
390 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
391 client->duid_len, &client->duid);
395 elapsed_usec = time_now - client->transaction_start;
396 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
397 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
399 elapsed_time = 0xffff;
401 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
402 sizeof(elapsed_time), &elapsed_time);
406 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
411 log_dhcp6_client(client, "Sent %s",
412 dhcp6_message_type_to_string(message->type));
417 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
419 sd_dhcp6_client *client = userdata;
421 assert_return(s, -EINVAL);
422 assert_return(client, -EINVAL);
423 assert_return(client->lease, -EINVAL);
425 client->lease->ia.timeout_t2 =
426 sd_event_source_unref(client->lease->ia.timeout_t2);
428 log_dhcp6_client(client, "Timeout T2");
430 client_start(client, DHCP6_STATE_REBIND);
435 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
437 sd_dhcp6_client *client = userdata;
439 assert_return(s, -EINVAL);
440 assert_return(client, -EINVAL);
441 assert_return(client->lease, -EINVAL);
443 client->lease->ia.timeout_t1 =
444 sd_event_source_unref(client->lease->ia.timeout_t1);
446 log_dhcp6_client(client, "Timeout T1");
448 client_start(client, DHCP6_STATE_RENEW);
453 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
455 sd_dhcp6_client *client = userdata;
456 DHCP6_CLIENT_DONT_DESTROY(client);
457 enum DHCP6State state;
461 assert(client->event);
463 state = client->state;
465 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
467 /* RFC 3315, section 18.1.4., says that "...the client may choose to
468 use a Solicit message to locate a new DHCP server..." */
469 if (state == DHCP6_STATE_REBIND)
470 client_start(client, DHCP6_STATE_SOLICITATION);
475 static usec_t client_timeout_compute_random(usec_t val) {
476 return val - val / 10 +
477 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
480 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
483 sd_dhcp6_client *client = userdata;
484 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
485 usec_t max_retransmit_duration = 0;
486 uint8_t max_retransmit_count = 0;
487 char time_string[FORMAT_TIMESPAN_MAX];
492 assert(client->event);
494 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
496 switch (client->state) {
497 case DHCP6_STATE_SOLICITATION:
499 if (client->retransmit_count && client->lease) {
500 client_start(client, DHCP6_STATE_REQUEST);
504 init_retransmit_time = DHCP6_SOL_TIMEOUT;
505 max_retransmit_time = DHCP6_SOL_MAX_RT;
509 case DHCP6_STATE_REQUEST:
510 init_retransmit_time = DHCP6_REQ_TIMEOUT;
511 max_retransmit_time = DHCP6_REQ_MAX_RT;
512 max_retransmit_count = DHCP6_REQ_MAX_RC;
516 case DHCP6_STATE_RENEW:
517 init_retransmit_time = DHCP6_REN_TIMEOUT;
518 max_retransmit_time = DHCP6_REN_MAX_RT;
520 /* RFC 3315, section 18.1.3. says max retransmit duration will
521 be the remaining time until T2. Instead of setting MRD,
522 wait for T2 to trigger with the same end result */
526 case DHCP6_STATE_REBIND:
527 init_retransmit_time = DHCP6_REB_TIMEOUT;
528 max_retransmit_time = DHCP6_REB_MAX_RT;
530 if (!client->timeout_resend_expire) {
531 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
534 client_stop(client, r);
537 max_retransmit_duration = expire * USEC_PER_SEC;
542 case DHCP6_STATE_STOPPED:
543 case DHCP6_STATE_BOUND:
547 if (max_retransmit_count &&
548 client->retransmit_count >= max_retransmit_count) {
549 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
553 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
557 r = client_send_message(client, time_now);
559 client->retransmit_count++;
561 if (!client->retransmit_time) {
562 client->retransmit_time =
563 client_timeout_compute_random(init_retransmit_time);
565 if (client->state == DHCP6_STATE_SOLICITATION)
566 client->retransmit_time += init_retransmit_time / 10;
569 if (max_retransmit_time &&
570 client->retransmit_time > max_retransmit_time / 2)
571 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
573 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
576 log_dhcp6_client(client, "Next retransmission in %s",
577 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
578 client->retransmit_time, 0));
580 r = sd_event_add_time(client->event, &client->timeout_resend,
581 clock_boottime_or_monotonic(),
582 time_now + client->retransmit_time,
583 10 * USEC_PER_MSEC, client_timeout_resend,
588 r = sd_event_source_set_priority(client->timeout_resend,
589 client->event_priority);
593 r = sd_event_source_set_name(client->timeout_resend,
594 "dhcp6-resend-timer");
598 if (max_retransmit_duration && !client->timeout_resend_expire) {
600 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
601 max_retransmit_duration / USEC_PER_SEC);
603 r = sd_event_add_time(client->event,
604 &client->timeout_resend_expire,
605 clock_boottime_or_monotonic(),
606 time_now + max_retransmit_duration,
608 client_timeout_resend_expire, client);
612 r = sd_event_source_set_priority(client->timeout_resend_expire,
613 client->event_priority);
617 r = sd_event_source_set_name(client->timeout_resend_expire,
618 "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 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
751 if (r < 0 && r != -ENOMSG)
754 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
758 if (client->ia_na.id != iaid_lease) {
759 log_dhcp6_client(client, "%s has wrong IAID",
760 dhcp6_message_type_to_string(message->type));
766 case DHCP6_OPTION_RAPID_COMMIT:
767 r = dhcp6_lease_set_rapid_commit(lease);
775 if ((r < 0 && r != -ENOMSG) || !clientid) {
776 log_dhcp6_client(client, "%s has incomplete options",
777 dhcp6_message_type_to_string(message->type));
781 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
783 log_dhcp6_client(client, "%s has no server id",
784 dhcp6_message_type_to_string(message->type));
789 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
793 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
796 if (reply->type != DHCP6_REPLY)
799 r = dhcp6_lease_new(&lease);
803 r = client_parse_message(client, reply, len, lease);
807 if (client->state == DHCP6_STATE_SOLICITATION) {
808 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
817 dhcp6_lease_clear_timers(&client->lease->ia);
819 client->lease = sd_dhcp6_lease_unref(client->lease);
820 client->lease = lease;
823 return DHCP6_STATE_BOUND;
826 static int client_receive_advertise(sd_dhcp6_client *client,
827 DHCP6Message *advertise, size_t len) {
829 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
830 uint8_t pref_advertise = 0, pref_lease = 0;
832 if (advertise->type != DHCP6_ADVERTISE)
835 r = dhcp6_lease_new(&lease);
839 r = client_parse_message(client, advertise, len, lease);
843 r = dhcp6_lease_get_preference(lease, &pref_advertise);
847 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
848 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
849 sd_dhcp6_lease_unref(client->lease);
850 client->lease = lease;
855 if (pref_advertise == 255 || client->retransmit_count > 1)
856 r = DHCP6_STATE_REQUEST;
861 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
863 sd_dhcp6_client *client = userdata;
864 DHCP6_CLIENT_DONT_DESTROY(client);
865 _cleanup_free_ DHCP6Message *message;
870 assert(client->event);
872 r = ioctl(fd, FIONREAD, &buflen);
873 if (r < 0 || buflen <= 0)
874 buflen = DHCP6_MIN_OPTIONS_SIZE;
876 message = malloc0(buflen);
880 len = read(fd, message, buflen);
881 if ((size_t)len < sizeof(DHCP6Message)) {
882 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
886 switch(message->type) {
894 case DHCP6_INFORMATION_REQUEST:
895 case DHCP6_RELAY_FORW:
896 case DHCP6_RELAY_REPL:
899 case DHCP6_ADVERTISE:
901 case DHCP6_RECONFIGURE:
905 log_dhcp6_client(client, "unknown message type %d",
910 if (client->transaction_id != (message->transaction_id &
911 htobe32(0x00ffffff)))
914 switch (client->state) {
915 case DHCP6_STATE_SOLICITATION:
916 r = client_receive_advertise(client, message, len);
918 if (r == DHCP6_STATE_REQUEST) {
919 client_start(client, r);
924 /* fall through for Soliciation Rapid Commit option check */
925 case DHCP6_STATE_REQUEST:
926 case DHCP6_STATE_RENEW:
927 case DHCP6_STATE_REBIND:
929 r = client_receive_reply(client, message, len);
933 if (r == DHCP6_STATE_BOUND) {
935 r = client_start(client, DHCP6_STATE_BOUND);
937 client_stop(client, r);
941 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
946 case DHCP6_STATE_BOUND:
950 case DHCP6_STATE_STOPPED:
955 log_dhcp6_client(client, "Recv %s",
956 dhcp6_message_type_to_string(message->type));
962 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
965 usec_t timeout, time_now;
966 char time_string[FORMAT_TIMESPAN_MAX];
968 assert_return(client, -EINVAL);
969 assert_return(client->event, -EINVAL);
970 assert_return(client->index > 0, -EINVAL);
971 assert_return(client->state != state, -EINVAL);
973 client->timeout_resend_expire =
974 sd_event_source_unref(client->timeout_resend_expire);
975 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
976 client->retransmit_time = 0;
977 client->retransmit_count = 0;
979 if (client->state == DHCP6_STATE_STOPPED) {
980 time_now = now(clock_boottime_or_monotonic());
982 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
989 case DHCP6_STATE_STOPPED:
990 case DHCP6_STATE_SOLICITATION:
992 r = client_ensure_iaid(client);
996 r = dhcp6_network_bind_udp_socket(client->index, NULL);
1002 r = sd_event_add_io(client->event, &client->receive_message,
1003 client->fd, EPOLLIN, client_receive_message,
1008 r = sd_event_source_set_priority(client->receive_message,
1009 client->event_priority);
1013 r = sd_event_source_set_name(client->receive_message,
1014 "dhcp6-receive-message");
1018 client->state = DHCP6_STATE_SOLICITATION;
1022 case DHCP6_STATE_REQUEST:
1023 case DHCP6_STATE_RENEW:
1024 case DHCP6_STATE_REBIND:
1026 client->state = state;
1030 case DHCP6_STATE_BOUND:
1032 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1033 client->lease->ia.lifetime_t2 == 0xffffffff) {
1035 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1036 be32toh(client->lease->ia.lifetime_t1),
1037 be32toh(client->lease->ia.lifetime_t2));
1042 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1044 log_dhcp6_client(client, "T1 expires in %s",
1045 format_timespan(time_string,
1046 FORMAT_TIMESPAN_MAX,
1049 r = sd_event_add_time(client->event,
1050 &client->lease->ia.timeout_t1,
1051 clock_boottime_or_monotonic(), time_now + timeout,
1052 10 * USEC_PER_SEC, client_timeout_t1,
1057 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1058 client->event_priority);
1062 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
1063 "dhcp6-t1-timeout");
1067 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1069 log_dhcp6_client(client, "T2 expires in %s",
1070 format_timespan(time_string,
1071 FORMAT_TIMESPAN_MAX,
1074 r = sd_event_add_time(client->event,
1075 &client->lease->ia.timeout_t2,
1076 clock_boottime_or_monotonic(), time_now + timeout,
1077 10 * USEC_PER_SEC, client_timeout_t2,
1082 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1083 client->event_priority);
1087 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
1088 "dhcp6-t2-timeout");
1092 client->state = state;
1097 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1098 client->transaction_start = time_now;
1100 r = sd_event_add_time(client->event, &client->timeout_resend,
1101 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1106 r = sd_event_source_set_priority(client->timeout_resend,
1107 client->event_priority);
1111 r = sd_event_source_set_name(client->timeout_resend,
1112 "dhcp6-resend-timeout");
1119 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1121 client_stop(client, DHCP6_EVENT_STOP);
1126 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1130 assert_return(client, -EINVAL);
1131 assert_return(client->event, -EINVAL);
1132 assert_return(client->index > 0, -EINVAL);
1134 r = client_reset(client);
1138 return client_start(client, DHCP6_STATE_SOLICITATION);
1141 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1146 assert_return(client, -EINVAL);
1147 assert_return(!client->event, -EBUSY);
1150 client->event = sd_event_ref(event);
1152 r = sd_event_default(&client->event);
1157 client->event_priority = priority;
1162 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1163 assert_return(client, -EINVAL);
1165 client->event = sd_event_unref(client->event);
1170 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1174 return client->event;
1177 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1179 assert_se(REFCNT_INC(client->n_ref) >= 2);
1184 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1185 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1186 client_reset(client);
1188 sd_dhcp6_client_detach_event(client);
1190 free(client->req_opts);
1199 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1201 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1202 sd_id128_t machine_id;
1206 assert_return(ret, -EINVAL);
1208 client = new0(sd_dhcp6_client, 1);
1212 client->n_ref = REFCNT_INIT;
1214 client->ia_na.type = DHCP6_OPTION_IA_NA;
1220 /* initialize DUID */
1221 client->duid.en.type = htobe16(DHCP6_DUID_EN);
1222 client->duid.en.pen = htobe32(SYSTEMD_PEN);
1223 client->duid_len = sizeof(client->duid.en);
1225 r = sd_id128_get_machine(&machine_id);
1229 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1231 siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1233 client->req_opts_len = ELEMENTSOF(default_req_opts);
1235 client->req_opts = new0(be16_t, client->req_opts_len);
1236 if (!client->req_opts)
1239 for (t = 0; t < client->req_opts_len; t++)
1240 client->req_opts[t] = htobe16(default_req_opts[t]);