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_description(client->timeout_resend, "dhcp6-resend-timer");
597 if (max_retransmit_duration && !client->timeout_resend_expire) {
599 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
600 max_retransmit_duration / USEC_PER_SEC);
602 r = sd_event_add_time(client->event,
603 &client->timeout_resend_expire,
604 clock_boottime_or_monotonic(),
605 time_now + max_retransmit_duration,
607 client_timeout_resend_expire, client);
611 r = sd_event_source_set_priority(client->timeout_resend_expire,
612 client->event_priority);
616 r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
623 client_stop(client, r);
628 static int client_ensure_iaid(sd_dhcp6_client *client) {
629 /* name is a pointer to memory in the udev_device struct, so must
630 have the same scope */
631 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
632 const char *name = NULL;
637 if (client->ia_na.id)
640 if (detect_container(NULL) <= 0) {
641 /* not in a container, udev will be around */
642 _cleanup_udev_unref_ struct udev *udev;
643 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
649 sprintf(ifindex_str, "n%d", client->index);
650 device = udev_device_new_from_device_id(udev, ifindex_str);
654 if (udev_device_get_is_initialized(device) <= 0)
658 name = net_get_name(device);
662 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
664 /* fall back to mac address if no predictable name available */
665 siphash24((uint8_t*)&id, &client->mac_addr,
666 client->mac_addr_len, HASH_KEY.bytes);
668 /* fold into 32 bits */
669 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
674 static int client_parse_message(sd_dhcp6_client *client,
675 DHCP6Message *message, size_t len,
676 sd_dhcp6_lease *lease) {
678 uint8_t *optval, *option, *id = NULL;
679 uint16_t optcode, status;
680 size_t optlen, id_len;
681 bool clientid = false;
684 option = (uint8_t *)message + sizeof(DHCP6Message);
685 len -= sizeof(DHCP6Message);
687 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
690 case DHCP6_OPTION_CLIENTID:
692 log_dhcp6_client(client, "%s contains multiple clientids",
693 dhcp6_message_type_to_string(message->type));
697 if (optlen != client->duid_len ||
698 memcmp(&client->duid, optval, optlen) != 0) {
699 log_dhcp6_client(client, "%s DUID does not match",
700 dhcp6_message_type_to_string(message->type));
708 case DHCP6_OPTION_SERVERID:
709 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
711 log_dhcp6_client(client, "%s contains multiple serverids",
712 dhcp6_message_type_to_string(message->type));
716 r = dhcp6_lease_set_serverid(lease, optval, optlen);
722 case DHCP6_OPTION_PREFERENCE:
726 r = dhcp6_lease_set_preference(lease, *optval);
732 case DHCP6_OPTION_STATUS_CODE:
736 status = optval[0] << 8 | optval[1];
738 log_dhcp6_client(client, "%s Status %s",
739 dhcp6_message_type_to_string(message->type),
740 dhcp6_message_status_to_string(status));
746 case DHCP6_OPTION_IA_NA:
747 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
749 if (r < 0 && r != -ENOMSG)
752 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
756 if (client->ia_na.id != iaid_lease) {
757 log_dhcp6_client(client, "%s has wrong IAID",
758 dhcp6_message_type_to_string(message->type));
764 case DHCP6_OPTION_RAPID_COMMIT:
765 r = dhcp6_lease_set_rapid_commit(lease);
773 if ((r < 0 && r != -ENOMSG) || !clientid) {
774 log_dhcp6_client(client, "%s has incomplete options",
775 dhcp6_message_type_to_string(message->type));
779 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
781 log_dhcp6_client(client, "%s has no server id",
782 dhcp6_message_type_to_string(message->type));
787 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
791 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
794 if (reply->type != DHCP6_REPLY)
797 r = dhcp6_lease_new(&lease);
801 r = client_parse_message(client, reply, len, lease);
805 if (client->state == DHCP6_STATE_SOLICITATION) {
806 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
815 dhcp6_lease_clear_timers(&client->lease->ia);
817 client->lease = sd_dhcp6_lease_unref(client->lease);
818 client->lease = lease;
821 return DHCP6_STATE_BOUND;
824 static int client_receive_advertise(sd_dhcp6_client *client,
825 DHCP6Message *advertise, size_t len) {
827 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
828 uint8_t pref_advertise = 0, pref_lease = 0;
830 if (advertise->type != DHCP6_ADVERTISE)
833 r = dhcp6_lease_new(&lease);
837 r = client_parse_message(client, advertise, len, lease);
841 r = dhcp6_lease_get_preference(lease, &pref_advertise);
845 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
846 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
847 sd_dhcp6_lease_unref(client->lease);
848 client->lease = lease;
853 if (pref_advertise == 255 || client->retransmit_count > 1)
854 r = DHCP6_STATE_REQUEST;
859 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
861 sd_dhcp6_client *client = userdata;
862 DHCP6_CLIENT_DONT_DESTROY(client);
863 _cleanup_free_ DHCP6Message *message;
868 assert(client->event);
870 r = ioctl(fd, FIONREAD, &buflen);
871 if (r < 0 || buflen <= 0)
872 buflen = DHCP6_MIN_OPTIONS_SIZE;
874 message = malloc0(buflen);
878 len = read(fd, message, buflen);
879 if ((size_t)len < sizeof(DHCP6Message)) {
880 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
884 switch(message->type) {
892 case DHCP6_INFORMATION_REQUEST:
893 case DHCP6_RELAY_FORW:
894 case DHCP6_RELAY_REPL:
897 case DHCP6_ADVERTISE:
899 case DHCP6_RECONFIGURE:
903 log_dhcp6_client(client, "unknown message type %d",
908 if (client->transaction_id != (message->transaction_id &
909 htobe32(0x00ffffff)))
912 switch (client->state) {
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 case DHCP6_STATE_SOLICITATION:
990 r = client_ensure_iaid(client);
994 r = dhcp6_network_bind_udp_socket(client->index, NULL);
1000 r = sd_event_add_io(client->event, &client->receive_message,
1001 client->fd, EPOLLIN, client_receive_message,
1006 r = sd_event_source_set_priority(client->receive_message,
1007 client->event_priority);
1011 r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message");
1015 client->state = DHCP6_STATE_SOLICITATION;
1019 case DHCP6_STATE_REQUEST:
1020 case DHCP6_STATE_RENEW:
1021 case DHCP6_STATE_REBIND:
1023 client->state = state;
1027 case DHCP6_STATE_BOUND:
1029 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1030 client->lease->ia.lifetime_t2 == 0xffffffff) {
1032 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1033 be32toh(client->lease->ia.lifetime_t1),
1034 be32toh(client->lease->ia.lifetime_t2));
1039 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1041 log_dhcp6_client(client, "T1 expires in %s",
1042 format_timespan(time_string,
1043 FORMAT_TIMESPAN_MAX,
1046 r = sd_event_add_time(client->event,
1047 &client->lease->ia.timeout_t1,
1048 clock_boottime_or_monotonic(), time_now + timeout,
1049 10 * USEC_PER_SEC, client_timeout_t1,
1054 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1055 client->event_priority);
1059 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
1063 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1065 log_dhcp6_client(client, "T2 expires in %s",
1066 format_timespan(time_string,
1067 FORMAT_TIMESPAN_MAX,
1070 r = sd_event_add_time(client->event,
1071 &client->lease->ia.timeout_t2,
1072 clock_boottime_or_monotonic(), time_now + timeout,
1073 10 * USEC_PER_SEC, client_timeout_t2,
1078 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1079 client->event_priority);
1083 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
1087 client->state = state;
1092 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1093 client->transaction_start = time_now;
1095 r = sd_event_add_time(client->event, &client->timeout_resend,
1096 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1101 r = sd_event_source_set_priority(client->timeout_resend,
1102 client->event_priority);
1106 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1113 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1115 client_stop(client, DHCP6_EVENT_STOP);
1120 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1124 assert_return(client, -EINVAL);
1125 assert_return(client->event, -EINVAL);
1126 assert_return(client->index > 0, -EINVAL);
1128 r = client_reset(client);
1132 return client_start(client, DHCP6_STATE_SOLICITATION);
1135 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1140 assert_return(client, -EINVAL);
1141 assert_return(!client->event, -EBUSY);
1144 client->event = sd_event_ref(event);
1146 r = sd_event_default(&client->event);
1151 client->event_priority = priority;
1156 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1157 assert_return(client, -EINVAL);
1159 client->event = sd_event_unref(client->event);
1164 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1168 return client->event;
1171 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1173 assert_se(REFCNT_INC(client->n_ref) >= 2);
1178 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1179 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1180 client_reset(client);
1182 sd_dhcp6_client_detach_event(client);
1184 free(client->req_opts);
1193 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1195 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1196 sd_id128_t machine_id;
1200 assert_return(ret, -EINVAL);
1202 client = new0(sd_dhcp6_client, 1);
1206 client->n_ref = REFCNT_INIT;
1208 client->ia_na.type = DHCP6_OPTION_IA_NA;
1214 /* initialize DUID */
1215 client->duid.en.type = htobe16(DHCP6_DUID_EN);
1216 client->duid.en.pen = htobe32(SYSTEMD_PEN);
1217 client->duid_len = sizeof(client->duid.en);
1219 r = sd_id128_get_machine(&machine_id);
1223 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1225 siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1227 client->req_opts_len = ELEMENTSOF(default_req_opts);
1229 client->req_opts = new0(be16_t, client->req_opts_len);
1230 if (!client->req_opts)
1233 for (t = 0; t < client->req_opts_len; t++)
1234 client->req_opts[t] = htobe16(default_req_opts[t]);