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>
27 #include "udev-util.h"
29 #include "siphash24.h"
33 #include "network-internal.h"
34 #include "sd-dhcp6-client.h"
35 #include "dhcp6-protocol.h"
36 #include "dhcp6-internal.h"
37 #include "dhcp6-lease-internal.h"
39 #define SYSTEMD_PEN 43793
40 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
42 #define MAX_DUID_LEN 32
44 struct sd_dhcp6_client {
47 enum DHCP6State state;
51 struct ether_addr mac_addr;
53 be32_t transaction_id;
54 usec_t transaction_start;
55 struct sd_dhcp6_lease *lease;
58 size_t req_opts_allocated;
60 sd_event_source *receive_message;
61 usec_t retransmit_time;
62 uint8_t retransmit_count;
63 sd_event_source *timeout_resend;
64 sd_event_source *timeout_resend_expire;
65 sd_dhcp6_client_cb_t cb;
67 uint8_t duid[MAX_DUID_LEN];
71 static const uint16_t default_req_opts[] = {
72 DHCP6_OPTION_DNS_SERVERS,
73 DHCP6_OPTION_DOMAIN_LIST,
74 DHCP6_OPTION_NTP_SERVER,
77 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
78 [DHCP6_SOLICIT] = "SOLICIT",
79 [DHCP6_ADVERTISE] = "ADVERTISE",
80 [DHCP6_REQUEST] = "REQUEST",
81 [DHCP6_CONFIRM] = "CONFIRM",
82 [DHCP6_RENEW] = "RENEW",
83 [DHCP6_REBIND] = "REBIND",
84 [DHCP6_REPLY] = "REPLY",
85 [DHCP6_RELEASE] = "RELEASE",
86 [DHCP6_DECLINE] = "DECLINE",
87 [DHCP6_RECONFIGURE] = "RECONFIGURE",
88 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
89 [DHCP6_RELAY_FORW] = "RELAY-FORW",
90 [DHCP6_RELAY_REPL] = "RELAY-REPL",
93 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
95 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
96 [DHCP6_STATUS_SUCCESS] = "Success",
97 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
98 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
99 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
100 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
101 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
104 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
106 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
107 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
109 #define DHCP6_CLIENT_DONT_DESTROY(client) \
110 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
112 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
114 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
115 sd_dhcp6_client_cb_t cb, void *userdata)
117 assert_return(client, -EINVAL);
120 client->userdata = userdata;
125 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
127 assert_return(client, -EINVAL);
128 assert_return(interface_index >= -1, -EINVAL);
130 client->index = interface_index;
135 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
136 const struct ether_addr *mac_addr)
138 assert_return(client, -EINVAL);
141 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
143 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
148 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint8_t *duid,
151 assert_return(client, -EINVAL);
152 assert_return(duid, -EINVAL);
153 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
155 memcpy(&client->duid, duid, duid_len);
156 client->duid_len = duid_len;
161 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
165 assert_return(client, -EINVAL);
166 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
169 case DHCP6_OPTION_DNS_SERVERS:
170 case DHCP6_OPTION_DOMAIN_LIST:
171 case DHCP6_OPTION_SNTP_SERVERS:
172 case DHCP6_OPTION_NTP_SERVER:
179 for (t = 0; t < client->req_opts_len; t++)
180 if (client->req_opts[t] == htobe16(option))
183 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
184 client->req_opts_len + 1))
187 client->req_opts[client->req_opts_len++] = htobe16(option);
192 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
193 assert_return(client, -EINVAL);
194 assert_return(ret, -EINVAL);
199 *ret = sd_dhcp6_lease_ref(client->lease);
204 static void client_notify(sd_dhcp6_client *client, int event) {
206 client->cb(client, event, client->userdata);
209 static int client_reset(sd_dhcp6_client *client) {
210 assert_return(client, -EINVAL);
212 client->receive_message =
213 sd_event_source_unref(client->receive_message);
215 client->fd = safe_close(client->fd);
217 client->transaction_id = 0;
218 client->transaction_start = 0;
220 client->ia_na.timeout_t1 =
221 sd_event_source_unref(client->ia_na.timeout_t1);
222 client->ia_na.timeout_t2 =
223 sd_event_source_unref(client->ia_na.timeout_t2);
225 client->retransmit_time = 0;
226 client->retransmit_count = 0;
227 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
228 client->timeout_resend_expire =
229 sd_event_source_unref(client->timeout_resend_expire);
231 client->state = DHCP6_STATE_STOPPED;
236 static void client_stop(sd_dhcp6_client *client, int error) {
237 DHCP6_CLIENT_DONT_DESTROY(client);
241 client_notify(client, error);
243 client_reset(client);
246 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
247 _cleanup_free_ DHCP6Message *message = NULL;
248 struct in6_addr all_servers =
249 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
250 size_t len, optlen = 512;
256 len = sizeof(DHCP6Message) + optlen;
258 message = malloc0(len);
262 opt = (uint8_t *)(message + 1);
264 message->transaction_id = client->transaction_id;
266 switch(client->state) {
267 case DHCP6_STATE_SOLICITATION:
268 message->type = DHCP6_SOLICIT;
270 r = dhcp6_option_append(&opt, &optlen,
271 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
275 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
281 case DHCP6_STATE_REQUEST:
282 case DHCP6_STATE_RENEW:
284 if (client->state == DHCP6_STATE_REQUEST)
285 message->type = DHCP6_REQUEST;
287 message->type = DHCP6_RENEW;
289 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
290 client->lease->serverid_len,
291 client->lease->serverid);
295 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
301 case DHCP6_STATE_REBIND:
302 message->type = DHCP6_REBIND;
304 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
310 case DHCP6_STATE_STOPPED:
311 case DHCP6_STATE_BOUND:
315 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
316 client->req_opts_len * sizeof(be16_t),
321 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
322 client->duid_len, &client->duid);
326 elapsed_usec = time_now - client->transaction_start;
327 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
328 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
330 elapsed_time = 0xffff;
332 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
333 sizeof(elapsed_time), &elapsed_time);
337 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
342 log_dhcp6_client(client, "Sent %s",
343 dhcp6_message_type_to_string(message->type));
348 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
350 sd_dhcp6_client *client = userdata;
352 assert_return(s, -EINVAL);
353 assert_return(client, -EINVAL);
354 assert_return(client->lease, -EINVAL);
356 client->lease->ia.timeout_t2 =
357 sd_event_source_unref(client->lease->ia.timeout_t2);
359 log_dhcp6_client(client, "Timeout T2");
361 client_start(client, DHCP6_STATE_REBIND);
366 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
368 sd_dhcp6_client *client = userdata;
370 assert_return(s, -EINVAL);
371 assert_return(client, -EINVAL);
372 assert_return(client->lease, -EINVAL);
374 client->lease->ia.timeout_t1 =
375 sd_event_source_unref(client->lease->ia.timeout_t1);
377 log_dhcp6_client(client, "Timeout T1");
379 client_start(client, DHCP6_STATE_RENEW);
384 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
386 sd_dhcp6_client *client = userdata;
387 DHCP6_CLIENT_DONT_DESTROY(client);
388 enum DHCP6State state;
392 assert(client->event);
394 state = client->state;
396 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
398 /* RFC 3315, section 18.1.4., says that "...the client may choose to
399 use a Solicit message to locate a new DHCP server..." */
400 if (state == DHCP6_STATE_REBIND)
401 client_start(client, DHCP6_STATE_SOLICITATION);
406 static usec_t client_timeout_compute_random(usec_t val) {
407 return val - val / 10 +
408 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
411 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
414 sd_dhcp6_client *client = userdata;
415 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
416 usec_t max_retransmit_duration = 0;
417 uint8_t max_retransmit_count = 0;
418 char time_string[FORMAT_TIMESPAN_MAX];
423 assert(client->event);
425 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
427 switch (client->state) {
428 case DHCP6_STATE_SOLICITATION:
430 if (client->retransmit_count && client->lease) {
431 client_start(client, DHCP6_STATE_REQUEST);
435 init_retransmit_time = DHCP6_SOL_TIMEOUT;
436 max_retransmit_time = DHCP6_SOL_MAX_RT;
440 case DHCP6_STATE_REQUEST:
441 init_retransmit_time = DHCP6_REQ_TIMEOUT;
442 max_retransmit_time = DHCP6_REQ_MAX_RT;
443 max_retransmit_count = DHCP6_REQ_MAX_RC;
447 case DHCP6_STATE_RENEW:
448 init_retransmit_time = DHCP6_REN_TIMEOUT;
449 max_retransmit_time = DHCP6_REN_MAX_RT;
451 /* RFC 3315, section 18.1.3. says max retransmit duration will
452 be the remaining time until T2. Instead of setting MRD,
453 wait for T2 to trigger with the same end result */
457 case DHCP6_STATE_REBIND:
458 init_retransmit_time = DHCP6_REB_TIMEOUT;
459 max_retransmit_time = DHCP6_REB_MAX_RT;
461 if (!client->timeout_resend_expire) {
462 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
465 client_stop(client, r);
468 max_retransmit_duration = expire * USEC_PER_SEC;
473 case DHCP6_STATE_STOPPED:
474 case DHCP6_STATE_BOUND:
478 if (max_retransmit_count &&
479 client->retransmit_count >= max_retransmit_count) {
480 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
484 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
488 r = client_send_message(client, time_now);
490 client->retransmit_count++;
492 if (!client->retransmit_time) {
493 client->retransmit_time =
494 client_timeout_compute_random(init_retransmit_time);
496 if (client->state == DHCP6_STATE_SOLICITATION)
497 client->retransmit_time += init_retransmit_time / 10;
500 if (max_retransmit_time &&
501 client->retransmit_time > max_retransmit_time / 2)
502 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
504 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
507 log_dhcp6_client(client, "Next retransmission in %s",
508 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
509 client->retransmit_time, 0));
511 r = sd_event_add_time(client->event, &client->timeout_resend,
512 clock_boottime_or_monotonic(),
513 time_now + client->retransmit_time,
514 10 * USEC_PER_MSEC, client_timeout_resend,
519 r = sd_event_source_set_priority(client->timeout_resend,
520 client->event_priority);
524 r = sd_event_source_set_name(client->timeout_resend,
525 "dhcp6-resend-timer");
529 if (max_retransmit_duration && !client->timeout_resend_expire) {
531 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
532 max_retransmit_duration / USEC_PER_SEC);
534 r = sd_event_add_time(client->event,
535 &client->timeout_resend_expire,
536 clock_boottime_or_monotonic(),
537 time_now + max_retransmit_duration,
539 client_timeout_resend_expire, client);
543 r = sd_event_source_set_priority(client->timeout_resend_expire,
544 client->event_priority);
548 r = sd_event_source_set_name(client->timeout_resend_expire,
549 "dhcp6-resend-expire-timer");
556 client_stop(client, r);
561 static int client_ensure_iaid(sd_dhcp6_client *client) {
562 /* name is a pointer to memory in the udev_device struct, so must
563 have the same scope */
564 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
565 const char *name = NULL;
570 if (client->ia_na.id)
573 if (detect_container(NULL) <= 0) {
574 /* not in a container, udev will be around */
575 _cleanup_udev_unref_ struct udev *udev;
576 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
582 sprintf(ifindex_str, "n%d", client->index);
583 device = udev_device_new_from_device_id(udev, ifindex_str);
587 if (udev_device_get_is_initialized(device) <= 0)
591 name = net_get_name(device);
595 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
597 /* fall back to mac address if no predictable name available */
598 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
601 /* fold into 32 bits */
602 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
607 static int client_parse_message(sd_dhcp6_client *client,
608 DHCP6Message *message, size_t len,
609 sd_dhcp6_lease *lease) {
611 uint8_t *optval, *option, *id = NULL;
612 uint16_t optcode, status;
613 size_t optlen, id_len;
614 bool clientid = false;
617 option = (uint8_t *)message + sizeof(DHCP6Message);
618 len -= sizeof(DHCP6Message);
620 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
623 case DHCP6_OPTION_CLIENTID:
625 log_dhcp6_client(client, "%s contains multiple clientids",
626 dhcp6_message_type_to_string(message->type));
630 if (optlen != client->duid_len ||
631 memcmp(&client->duid, optval, optlen) != 0) {
632 log_dhcp6_client(client, "%s DUID does not match",
633 dhcp6_message_type_to_string(message->type));
641 case DHCP6_OPTION_SERVERID:
642 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
644 log_dhcp6_client(client, "%s contains multiple serverids",
645 dhcp6_message_type_to_string(message->type));
649 r = dhcp6_lease_set_serverid(lease, optval, optlen);
655 case DHCP6_OPTION_PREFERENCE:
659 r = dhcp6_lease_set_preference(lease, *optval);
665 case DHCP6_OPTION_STATUS_CODE:
669 status = optval[0] << 8 | optval[1];
671 log_dhcp6_client(client, "%s Status %s",
672 dhcp6_message_type_to_string(message->type),
673 dhcp6_message_status_to_string(status));
679 case DHCP6_OPTION_IA_NA:
680 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
682 if (r < 0 && r != -ENOMSG)
685 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
689 if (client->ia_na.id != iaid_lease) {
690 log_dhcp6_client(client, "%s has wrong IAID",
691 dhcp6_message_type_to_string(message->type));
697 case DHCP6_OPTION_RAPID_COMMIT:
698 r = dhcp6_lease_set_rapid_commit(lease);
706 if ((r < 0 && r != -ENOMSG) || !clientid) {
707 log_dhcp6_client(client, "%s has incomplete options",
708 dhcp6_message_type_to_string(message->type));
712 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
714 log_dhcp6_client(client, "%s has no server id",
715 dhcp6_message_type_to_string(message->type));
720 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
724 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
727 if (reply->type != DHCP6_REPLY)
730 r = dhcp6_lease_new(&lease);
734 r = client_parse_message(client, reply, len, lease);
738 if (client->state == DHCP6_STATE_SOLICITATION) {
739 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
748 dhcp6_lease_clear_timers(&client->lease->ia);
750 client->lease = sd_dhcp6_lease_unref(client->lease);
751 client->lease = lease;
754 return DHCP6_STATE_BOUND;
757 static int client_receive_advertise(sd_dhcp6_client *client,
758 DHCP6Message *advertise, size_t len) {
760 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
761 uint8_t pref_advertise = 0, pref_lease = 0;
763 if (advertise->type != DHCP6_ADVERTISE)
766 r = dhcp6_lease_new(&lease);
770 r = client_parse_message(client, advertise, len, lease);
774 r = dhcp6_lease_get_preference(lease, &pref_advertise);
778 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
779 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
780 sd_dhcp6_lease_unref(client->lease);
781 client->lease = lease;
786 if (pref_advertise == 255 || client->retransmit_count > 1)
787 r = DHCP6_STATE_REQUEST;
792 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
794 sd_dhcp6_client *client = userdata;
795 DHCP6_CLIENT_DONT_DESTROY(client);
796 _cleanup_free_ DHCP6Message *message;
801 assert(client->event);
803 r = ioctl(fd, FIONREAD, &buflen);
804 if (r < 0 || buflen <= 0)
805 buflen = DHCP6_MIN_OPTIONS_SIZE;
807 message = malloc0(buflen);
811 len = read(fd, message, buflen);
812 if ((size_t)len < sizeof(DHCP6Message)) {
813 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
817 switch(message->type) {
825 case DHCP6_INFORMATION_REQUEST:
826 case DHCP6_RELAY_FORW:
827 case DHCP6_RELAY_REPL:
830 case DHCP6_ADVERTISE:
832 case DHCP6_RECONFIGURE:
836 log_dhcp6_client(client, "unknown message type %d",
841 if (client->transaction_id != (message->transaction_id &
842 htobe32(0x00ffffff)))
845 switch (client->state) {
846 case DHCP6_STATE_SOLICITATION:
847 r = client_receive_advertise(client, message, len);
849 if (r == DHCP6_STATE_REQUEST) {
850 client_start(client, r);
855 /* fall through for Soliciation Rapid Commit option check */
856 case DHCP6_STATE_REQUEST:
857 case DHCP6_STATE_RENEW:
858 case DHCP6_STATE_REBIND:
860 r = client_receive_reply(client, message, len);
864 if (r == DHCP6_STATE_BOUND) {
866 r = client_start(client, DHCP6_STATE_BOUND);
868 client_stop(client, r);
872 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
877 case DHCP6_STATE_BOUND:
881 case DHCP6_STATE_STOPPED:
886 log_dhcp6_client(client, "Recv %s",
887 dhcp6_message_type_to_string(message->type));
893 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
896 usec_t timeout, time_now;
897 char time_string[FORMAT_TIMESPAN_MAX];
899 assert_return(client, -EINVAL);
900 assert_return(client->event, -EINVAL);
901 assert_return(client->index > 0, -EINVAL);
902 assert_return(client->state != state, -EINVAL);
904 client->timeout_resend_expire =
905 sd_event_source_unref(client->timeout_resend_expire);
906 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
907 client->retransmit_time = 0;
908 client->retransmit_count = 0;
910 if (client->state == DHCP6_STATE_STOPPED) {
911 time_now = now(clock_boottime_or_monotonic());
913 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
920 case DHCP6_STATE_STOPPED:
921 case DHCP6_STATE_SOLICITATION:
923 r = client_ensure_iaid(client);
927 r = dhcp6_network_bind_udp_socket(client->index, NULL);
933 r = sd_event_add_io(client->event, &client->receive_message,
934 client->fd, EPOLLIN, client_receive_message,
939 r = sd_event_source_set_priority(client->receive_message,
940 client->event_priority);
944 r = sd_event_source_set_name(client->receive_message,
945 "dhcp6-receive-message");
949 client->state = DHCP6_STATE_SOLICITATION;
953 case DHCP6_STATE_REQUEST:
954 case DHCP6_STATE_RENEW:
955 case DHCP6_STATE_REBIND:
957 client->state = state;
961 case DHCP6_STATE_BOUND:
963 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
964 client->lease->ia.lifetime_t2 == 0xffffffff) {
966 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
967 be32toh(client->lease->ia.lifetime_t1),
968 be32toh(client->lease->ia.lifetime_t2));
973 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
975 log_dhcp6_client(client, "T1 expires in %s",
976 format_timespan(time_string,
980 r = sd_event_add_time(client->event,
981 &client->lease->ia.timeout_t1,
982 clock_boottime_or_monotonic(), time_now + timeout,
983 10 * USEC_PER_SEC, client_timeout_t1,
988 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
989 client->event_priority);
993 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
998 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1000 log_dhcp6_client(client, "T2 expires in %s",
1001 format_timespan(time_string,
1002 FORMAT_TIMESPAN_MAX,
1005 r = sd_event_add_time(client->event,
1006 &client->lease->ia.timeout_t2,
1007 clock_boottime_or_monotonic(), time_now + timeout,
1008 10 * USEC_PER_SEC, client_timeout_t2,
1013 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1014 client->event_priority);
1018 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
1019 "dhcp6-t2-timeout");
1023 client->state = state;
1028 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1029 client->transaction_start = time_now;
1031 r = sd_event_add_time(client->event, &client->timeout_resend,
1032 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1037 r = sd_event_source_set_priority(client->timeout_resend,
1038 client->event_priority);
1042 r = sd_event_source_set_name(client->timeout_resend,
1043 "dhcp6-resend-timeout");
1050 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1052 client_stop(client, DHCP6_EVENT_STOP);
1057 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1061 assert_return(client, -EINVAL);
1062 assert_return(client->event, -EINVAL);
1063 assert_return(client->index > 0, -EINVAL);
1065 r = client_reset(client);
1069 return client_start(client, DHCP6_STATE_SOLICITATION);
1072 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1077 assert_return(client, -EINVAL);
1078 assert_return(!client->event, -EBUSY);
1081 client->event = sd_event_ref(event);
1083 r = sd_event_default(&client->event);
1088 client->event_priority = priority;
1093 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1094 assert_return(client, -EINVAL);
1096 client->event = sd_event_unref(client->event);
1101 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1105 return client->event;
1108 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1110 assert_se(REFCNT_INC(client->n_ref) >= 2);
1115 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1116 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1117 client_reset(client);
1119 sd_dhcp6_client_detach_event(client);
1121 free(client->req_opts);
1131 uint16_t type; /* DHCP6_DUID_EN */
1136 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1138 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1139 struct duid_en *duid;
1140 sd_id128_t machine_id;
1144 assert_return(ret, -EINVAL);
1146 client = new0(sd_dhcp6_client, 1);
1150 client->n_ref = REFCNT_INIT;
1152 client->ia_na.type = DHCP6_OPTION_IA_NA;
1158 /* initialize DUID */
1159 duid = (struct duid_en *) &client->duid;
1160 duid->type = htobe16(DHCP6_DUID_EN);
1161 duid->pen = htobe32(SYSTEMD_PEN);
1163 r = sd_id128_get_machine(&machine_id);
1167 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1169 siphash24(duid->id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1170 client->duid_len = sizeof (struct duid_en);
1172 client->req_opts_len = ELEMENTSOF(default_req_opts);
1174 client->req_opts = new0(be16_t, client->req_opts_len);
1175 if (!client->req_opts)
1178 for (t = 0; t < client->req_opts_len; t++)
1179 client->req_opts[t] = htobe16(default_req_opts[t]);