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 /* RFC 3315 section 9.1:
43 * A DUID can be no more than 128 octets long (not including the type code).
45 #define MAX_DUID_LEN 128
47 struct sd_dhcp6_client {
50 enum DHCP6State state;
54 struct ether_addr mac_addr;
56 be32_t transaction_id;
57 usec_t transaction_start;
58 struct sd_dhcp6_lease *lease;
61 size_t req_opts_allocated;
63 sd_event_source *receive_message;
64 usec_t retransmit_time;
65 uint8_t retransmit_count;
66 sd_event_source *timeout_resend;
67 sd_event_source *timeout_resend_expire;
68 sd_dhcp6_client_cb_t cb;
72 uint16_t type; /* DHCP6_DUID_EN */
78 uint8_t data[MAX_DUID_LEN];
84 static const uint16_t default_req_opts[] = {
85 DHCP6_OPTION_DNS_SERVERS,
86 DHCP6_OPTION_DOMAIN_LIST,
87 DHCP6_OPTION_NTP_SERVER,
90 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
91 [DHCP6_SOLICIT] = "SOLICIT",
92 [DHCP6_ADVERTISE] = "ADVERTISE",
93 [DHCP6_REQUEST] = "REQUEST",
94 [DHCP6_CONFIRM] = "CONFIRM",
95 [DHCP6_RENEW] = "RENEW",
96 [DHCP6_REBIND] = "REBIND",
97 [DHCP6_REPLY] = "REPLY",
98 [DHCP6_RELEASE] = "RELEASE",
99 [DHCP6_DECLINE] = "DECLINE",
100 [DHCP6_RECONFIGURE] = "RECONFIGURE",
101 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
102 [DHCP6_RELAY_FORW] = "RELAY-FORW",
103 [DHCP6_RELAY_REPL] = "RELAY-REPL",
106 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
108 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
109 [DHCP6_STATUS_SUCCESS] = "Success",
110 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
111 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
112 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
113 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
114 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
117 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
119 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
120 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
122 #define DHCP6_CLIENT_DONT_DESTROY(client) \
123 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
125 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
127 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
128 sd_dhcp6_client_cb_t cb, void *userdata)
130 assert_return(client, -EINVAL);
133 client->userdata = userdata;
138 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
140 assert_return(client, -EINVAL);
141 assert_return(interface_index >= -1, -EINVAL);
143 client->index = interface_index;
148 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
149 const struct ether_addr *mac_addr)
151 assert_return(client, -EINVAL);
154 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
156 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
161 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
164 assert_return(client, -EINVAL);
165 assert_return(duid, -EINVAL);
166 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
168 client->duid.raw.type = htobe16(type);
169 memcpy(&client->duid.raw.data, duid, duid_len);
170 client->duid_len = duid_len;
175 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
179 assert_return(client, -EINVAL);
180 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
183 case DHCP6_OPTION_DNS_SERVERS:
184 case DHCP6_OPTION_DOMAIN_LIST:
185 case DHCP6_OPTION_SNTP_SERVERS:
186 case DHCP6_OPTION_NTP_SERVER:
193 for (t = 0; t < client->req_opts_len; t++)
194 if (client->req_opts[t] == htobe16(option))
197 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
198 client->req_opts_len + 1))
201 client->req_opts[client->req_opts_len++] = htobe16(option);
206 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
207 assert_return(client, -EINVAL);
208 assert_return(ret, -EINVAL);
213 *ret = sd_dhcp6_lease_ref(client->lease);
218 static void client_notify(sd_dhcp6_client *client, int event) {
220 client->cb(client, event, client->userdata);
223 static int client_reset(sd_dhcp6_client *client) {
224 assert_return(client, -EINVAL);
226 client->receive_message =
227 sd_event_source_unref(client->receive_message);
229 client->fd = safe_close(client->fd);
231 client->transaction_id = 0;
232 client->transaction_start = 0;
234 client->ia_na.timeout_t1 =
235 sd_event_source_unref(client->ia_na.timeout_t1);
236 client->ia_na.timeout_t2 =
237 sd_event_source_unref(client->ia_na.timeout_t2);
239 client->retransmit_time = 0;
240 client->retransmit_count = 0;
241 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
242 client->timeout_resend_expire =
243 sd_event_source_unref(client->timeout_resend_expire);
245 client->state = DHCP6_STATE_STOPPED;
250 static void client_stop(sd_dhcp6_client *client, int error) {
251 DHCP6_CLIENT_DONT_DESTROY(client);
255 client_notify(client, error);
257 client_reset(client);
260 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
261 _cleanup_free_ DHCP6Message *message = NULL;
262 struct in6_addr all_servers =
263 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
264 size_t len, optlen = 512;
270 len = sizeof(DHCP6Message) + optlen;
272 message = malloc0(len);
276 opt = (uint8_t *)(message + 1);
278 message->transaction_id = client->transaction_id;
280 switch(client->state) {
281 case DHCP6_STATE_SOLICITATION:
282 message->type = DHCP6_SOLICIT;
284 r = dhcp6_option_append(&opt, &optlen,
285 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
289 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
295 case DHCP6_STATE_REQUEST:
296 case DHCP6_STATE_RENEW:
298 if (client->state == DHCP6_STATE_REQUEST)
299 message->type = DHCP6_REQUEST;
301 message->type = DHCP6_RENEW;
303 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
304 client->lease->serverid_len,
305 client->lease->serverid);
309 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
315 case DHCP6_STATE_REBIND:
316 message->type = DHCP6_REBIND;
318 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
324 case DHCP6_STATE_STOPPED:
325 case DHCP6_STATE_BOUND:
329 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
330 client->req_opts_len * sizeof(be16_t),
335 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
336 client->duid_len, &client->duid);
340 elapsed_usec = time_now - client->transaction_start;
341 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
342 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
344 elapsed_time = 0xffff;
346 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
347 sizeof(elapsed_time), &elapsed_time);
351 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
356 log_dhcp6_client(client, "Sent %s",
357 dhcp6_message_type_to_string(message->type));
362 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
364 sd_dhcp6_client *client = userdata;
366 assert_return(s, -EINVAL);
367 assert_return(client, -EINVAL);
368 assert_return(client->lease, -EINVAL);
370 client->lease->ia.timeout_t2 =
371 sd_event_source_unref(client->lease->ia.timeout_t2);
373 log_dhcp6_client(client, "Timeout T2");
375 client_start(client, DHCP6_STATE_REBIND);
380 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
382 sd_dhcp6_client *client = userdata;
384 assert_return(s, -EINVAL);
385 assert_return(client, -EINVAL);
386 assert_return(client->lease, -EINVAL);
388 client->lease->ia.timeout_t1 =
389 sd_event_source_unref(client->lease->ia.timeout_t1);
391 log_dhcp6_client(client, "Timeout T1");
393 client_start(client, DHCP6_STATE_RENEW);
398 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
400 sd_dhcp6_client *client = userdata;
401 DHCP6_CLIENT_DONT_DESTROY(client);
402 enum DHCP6State state;
406 assert(client->event);
408 state = client->state;
410 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
412 /* RFC 3315, section 18.1.4., says that "...the client may choose to
413 use a Solicit message to locate a new DHCP server..." */
414 if (state == DHCP6_STATE_REBIND)
415 client_start(client, DHCP6_STATE_SOLICITATION);
420 static usec_t client_timeout_compute_random(usec_t val) {
421 return val - val / 10 +
422 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
425 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
428 sd_dhcp6_client *client = userdata;
429 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
430 usec_t max_retransmit_duration = 0;
431 uint8_t max_retransmit_count = 0;
432 char time_string[FORMAT_TIMESPAN_MAX];
437 assert(client->event);
439 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
441 switch (client->state) {
442 case DHCP6_STATE_SOLICITATION:
444 if (client->retransmit_count && client->lease) {
445 client_start(client, DHCP6_STATE_REQUEST);
449 init_retransmit_time = DHCP6_SOL_TIMEOUT;
450 max_retransmit_time = DHCP6_SOL_MAX_RT;
454 case DHCP6_STATE_REQUEST:
455 init_retransmit_time = DHCP6_REQ_TIMEOUT;
456 max_retransmit_time = DHCP6_REQ_MAX_RT;
457 max_retransmit_count = DHCP6_REQ_MAX_RC;
461 case DHCP6_STATE_RENEW:
462 init_retransmit_time = DHCP6_REN_TIMEOUT;
463 max_retransmit_time = DHCP6_REN_MAX_RT;
465 /* RFC 3315, section 18.1.3. says max retransmit duration will
466 be the remaining time until T2. Instead of setting MRD,
467 wait for T2 to trigger with the same end result */
471 case DHCP6_STATE_REBIND:
472 init_retransmit_time = DHCP6_REB_TIMEOUT;
473 max_retransmit_time = DHCP6_REB_MAX_RT;
475 if (!client->timeout_resend_expire) {
476 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
479 client_stop(client, r);
482 max_retransmit_duration = expire * USEC_PER_SEC;
487 case DHCP6_STATE_STOPPED:
488 case DHCP6_STATE_BOUND:
492 if (max_retransmit_count &&
493 client->retransmit_count >= max_retransmit_count) {
494 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
498 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
502 r = client_send_message(client, time_now);
504 client->retransmit_count++;
506 if (!client->retransmit_time) {
507 client->retransmit_time =
508 client_timeout_compute_random(init_retransmit_time);
510 if (client->state == DHCP6_STATE_SOLICITATION)
511 client->retransmit_time += init_retransmit_time / 10;
514 if (max_retransmit_time &&
515 client->retransmit_time > max_retransmit_time / 2)
516 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
518 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
521 log_dhcp6_client(client, "Next retransmission in %s",
522 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
523 client->retransmit_time, 0));
525 r = sd_event_add_time(client->event, &client->timeout_resend,
526 clock_boottime_or_monotonic(),
527 time_now + client->retransmit_time,
528 10 * USEC_PER_MSEC, client_timeout_resend,
533 r = sd_event_source_set_priority(client->timeout_resend,
534 client->event_priority);
538 r = sd_event_source_set_name(client->timeout_resend,
539 "dhcp6-resend-timer");
543 if (max_retransmit_duration && !client->timeout_resend_expire) {
545 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
546 max_retransmit_duration / USEC_PER_SEC);
548 r = sd_event_add_time(client->event,
549 &client->timeout_resend_expire,
550 clock_boottime_or_monotonic(),
551 time_now + max_retransmit_duration,
553 client_timeout_resend_expire, client);
557 r = sd_event_source_set_priority(client->timeout_resend_expire,
558 client->event_priority);
562 r = sd_event_source_set_name(client->timeout_resend_expire,
563 "dhcp6-resend-expire-timer");
570 client_stop(client, r);
575 static int client_ensure_iaid(sd_dhcp6_client *client) {
576 /* name is a pointer to memory in the udev_device struct, so must
577 have the same scope */
578 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
579 const char *name = NULL;
584 if (client->ia_na.id)
587 if (detect_container(NULL) <= 0) {
588 /* not in a container, udev will be around */
589 _cleanup_udev_unref_ struct udev *udev;
590 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
596 sprintf(ifindex_str, "n%d", client->index);
597 device = udev_device_new_from_device_id(udev, ifindex_str);
601 if (udev_device_get_is_initialized(device) <= 0)
605 name = net_get_name(device);
609 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
611 /* fall back to mac address if no predictable name available */
612 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
615 /* fold into 32 bits */
616 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
621 static int client_parse_message(sd_dhcp6_client *client,
622 DHCP6Message *message, size_t len,
623 sd_dhcp6_lease *lease) {
625 uint8_t *optval, *option, *id = NULL;
626 uint16_t optcode, status;
627 size_t optlen, id_len;
628 bool clientid = false;
631 option = (uint8_t *)message + sizeof(DHCP6Message);
632 len -= sizeof(DHCP6Message);
634 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
637 case DHCP6_OPTION_CLIENTID:
639 log_dhcp6_client(client, "%s contains multiple clientids",
640 dhcp6_message_type_to_string(message->type));
644 if (optlen != client->duid_len ||
645 memcmp(&client->duid, optval, optlen) != 0) {
646 log_dhcp6_client(client, "%s DUID does not match",
647 dhcp6_message_type_to_string(message->type));
655 case DHCP6_OPTION_SERVERID:
656 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
658 log_dhcp6_client(client, "%s contains multiple serverids",
659 dhcp6_message_type_to_string(message->type));
663 r = dhcp6_lease_set_serverid(lease, optval, optlen);
669 case DHCP6_OPTION_PREFERENCE:
673 r = dhcp6_lease_set_preference(lease, *optval);
679 case DHCP6_OPTION_STATUS_CODE:
683 status = optval[0] << 8 | optval[1];
685 log_dhcp6_client(client, "%s Status %s",
686 dhcp6_message_type_to_string(message->type),
687 dhcp6_message_status_to_string(status));
693 case DHCP6_OPTION_IA_NA:
694 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
696 if (r < 0 && r != -ENOMSG)
699 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
703 if (client->ia_na.id != iaid_lease) {
704 log_dhcp6_client(client, "%s has wrong IAID",
705 dhcp6_message_type_to_string(message->type));
711 case DHCP6_OPTION_RAPID_COMMIT:
712 r = dhcp6_lease_set_rapid_commit(lease);
720 if ((r < 0 && r != -ENOMSG) || !clientid) {
721 log_dhcp6_client(client, "%s has incomplete options",
722 dhcp6_message_type_to_string(message->type));
726 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
728 log_dhcp6_client(client, "%s has no server id",
729 dhcp6_message_type_to_string(message->type));
734 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
738 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
741 if (reply->type != DHCP6_REPLY)
744 r = dhcp6_lease_new(&lease);
748 r = client_parse_message(client, reply, len, lease);
752 if (client->state == DHCP6_STATE_SOLICITATION) {
753 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
762 dhcp6_lease_clear_timers(&client->lease->ia);
764 client->lease = sd_dhcp6_lease_unref(client->lease);
765 client->lease = lease;
768 return DHCP6_STATE_BOUND;
771 static int client_receive_advertise(sd_dhcp6_client *client,
772 DHCP6Message *advertise, size_t len) {
774 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
775 uint8_t pref_advertise = 0, pref_lease = 0;
777 if (advertise->type != DHCP6_ADVERTISE)
780 r = dhcp6_lease_new(&lease);
784 r = client_parse_message(client, advertise, len, lease);
788 r = dhcp6_lease_get_preference(lease, &pref_advertise);
792 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
793 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
794 sd_dhcp6_lease_unref(client->lease);
795 client->lease = lease;
800 if (pref_advertise == 255 || client->retransmit_count > 1)
801 r = DHCP6_STATE_REQUEST;
806 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
808 sd_dhcp6_client *client = userdata;
809 DHCP6_CLIENT_DONT_DESTROY(client);
810 _cleanup_free_ DHCP6Message *message;
815 assert(client->event);
817 r = ioctl(fd, FIONREAD, &buflen);
818 if (r < 0 || buflen <= 0)
819 buflen = DHCP6_MIN_OPTIONS_SIZE;
821 message = malloc0(buflen);
825 len = read(fd, message, buflen);
826 if ((size_t)len < sizeof(DHCP6Message)) {
827 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
831 switch(message->type) {
839 case DHCP6_INFORMATION_REQUEST:
840 case DHCP6_RELAY_FORW:
841 case DHCP6_RELAY_REPL:
844 case DHCP6_ADVERTISE:
846 case DHCP6_RECONFIGURE:
850 log_dhcp6_client(client, "unknown message type %d",
855 if (client->transaction_id != (message->transaction_id &
856 htobe32(0x00ffffff)))
859 switch (client->state) {
860 case DHCP6_STATE_SOLICITATION:
861 r = client_receive_advertise(client, message, len);
863 if (r == DHCP6_STATE_REQUEST) {
864 client_start(client, r);
869 /* fall through for Soliciation Rapid Commit option check */
870 case DHCP6_STATE_REQUEST:
871 case DHCP6_STATE_RENEW:
872 case DHCP6_STATE_REBIND:
874 r = client_receive_reply(client, message, len);
878 if (r == DHCP6_STATE_BOUND) {
880 r = client_start(client, DHCP6_STATE_BOUND);
882 client_stop(client, r);
886 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
891 case DHCP6_STATE_BOUND:
895 case DHCP6_STATE_STOPPED:
900 log_dhcp6_client(client, "Recv %s",
901 dhcp6_message_type_to_string(message->type));
907 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
910 usec_t timeout, time_now;
911 char time_string[FORMAT_TIMESPAN_MAX];
913 assert_return(client, -EINVAL);
914 assert_return(client->event, -EINVAL);
915 assert_return(client->index > 0, -EINVAL);
916 assert_return(client->state != state, -EINVAL);
918 client->timeout_resend_expire =
919 sd_event_source_unref(client->timeout_resend_expire);
920 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
921 client->retransmit_time = 0;
922 client->retransmit_count = 0;
924 if (client->state == DHCP6_STATE_STOPPED) {
925 time_now = now(clock_boottime_or_monotonic());
927 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
934 case DHCP6_STATE_STOPPED:
935 case DHCP6_STATE_SOLICITATION:
937 r = client_ensure_iaid(client);
941 r = dhcp6_network_bind_udp_socket(client->index, NULL);
947 r = sd_event_add_io(client->event, &client->receive_message,
948 client->fd, EPOLLIN, client_receive_message,
953 r = sd_event_source_set_priority(client->receive_message,
954 client->event_priority);
958 r = sd_event_source_set_name(client->receive_message,
959 "dhcp6-receive-message");
963 client->state = DHCP6_STATE_SOLICITATION;
967 case DHCP6_STATE_REQUEST:
968 case DHCP6_STATE_RENEW:
969 case DHCP6_STATE_REBIND:
971 client->state = state;
975 case DHCP6_STATE_BOUND:
977 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
978 client->lease->ia.lifetime_t2 == 0xffffffff) {
980 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
981 be32toh(client->lease->ia.lifetime_t1),
982 be32toh(client->lease->ia.lifetime_t2));
987 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
989 log_dhcp6_client(client, "T1 expires in %s",
990 format_timespan(time_string,
994 r = sd_event_add_time(client->event,
995 &client->lease->ia.timeout_t1,
996 clock_boottime_or_monotonic(), time_now + timeout,
997 10 * USEC_PER_SEC, client_timeout_t1,
1002 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1003 client->event_priority);
1007 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
1008 "dhcp6-t1-timeout");
1012 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1014 log_dhcp6_client(client, "T2 expires in %s",
1015 format_timespan(time_string,
1016 FORMAT_TIMESPAN_MAX,
1019 r = sd_event_add_time(client->event,
1020 &client->lease->ia.timeout_t2,
1021 clock_boottime_or_monotonic(), time_now + timeout,
1022 10 * USEC_PER_SEC, client_timeout_t2,
1027 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1028 client->event_priority);
1032 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
1033 "dhcp6-t2-timeout");
1037 client->state = state;
1042 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1043 client->transaction_start = time_now;
1045 r = sd_event_add_time(client->event, &client->timeout_resend,
1046 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1051 r = sd_event_source_set_priority(client->timeout_resend,
1052 client->event_priority);
1056 r = sd_event_source_set_name(client->timeout_resend,
1057 "dhcp6-resend-timeout");
1064 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1066 client_stop(client, DHCP6_EVENT_STOP);
1071 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1075 assert_return(client, -EINVAL);
1076 assert_return(client->event, -EINVAL);
1077 assert_return(client->index > 0, -EINVAL);
1079 r = client_reset(client);
1083 return client_start(client, DHCP6_STATE_SOLICITATION);
1086 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1091 assert_return(client, -EINVAL);
1092 assert_return(!client->event, -EBUSY);
1095 client->event = sd_event_ref(event);
1097 r = sd_event_default(&client->event);
1102 client->event_priority = priority;
1107 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1108 assert_return(client, -EINVAL);
1110 client->event = sd_event_unref(client->event);
1115 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1119 return client->event;
1122 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1124 assert_se(REFCNT_INC(client->n_ref) >= 2);
1129 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1130 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1131 client_reset(client);
1133 sd_dhcp6_client_detach_event(client);
1135 free(client->req_opts);
1144 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1146 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1147 sd_id128_t machine_id;
1151 assert_return(ret, -EINVAL);
1153 client = new0(sd_dhcp6_client, 1);
1157 client->n_ref = REFCNT_INIT;
1159 client->ia_na.type = DHCP6_OPTION_IA_NA;
1165 /* initialize DUID */
1166 client->duid.en.type = htobe16(DHCP6_DUID_EN);
1167 client->duid.en.pen = htobe32(SYSTEMD_PEN);
1168 client->duid_len = sizeof(client->duid.en);
1170 r = sd_id128_get_machine(&machine_id);
1174 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1176 siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1178 client->req_opts_len = ELEMENTSOF(default_req_opts);
1180 client->req_opts = new0(be16_t, client->req_opts_len);
1181 if (!client->req_opts)
1184 for (t = 0; t < client->req_opts_len; t++)
1185 client->req_opts[t] = htobe16(default_req_opts[t]);