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 struct sd_dhcp6_client {
45 enum DHCP6State state;
49 struct ether_addr mac_addr;
51 be32_t transaction_id;
52 usec_t transaction_start;
53 struct sd_dhcp6_lease *lease;
56 size_t req_opts_allocated;
58 sd_event_source *receive_message;
59 usec_t retransmit_time;
60 uint8_t retransmit_count;
61 sd_event_source *timeout_resend;
62 sd_event_source *timeout_resend_expire;
63 sd_dhcp6_client_cb_t cb;
67 uint16_t type; /* DHCP6_DUID_EN */
73 static const uint16_t default_req_opts[] = {
74 DHCP6_OPTION_DNS_SERVERS,
75 DHCP6_OPTION_DOMAIN_LIST,
76 DHCP6_OPTION_NTP_SERVER,
79 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
80 [DHCP6_SOLICIT] = "SOLICIT",
81 [DHCP6_ADVERTISE] = "ADVERTISE",
82 [DHCP6_REQUEST] = "REQUEST",
83 [DHCP6_CONFIRM] = "CONFIRM",
84 [DHCP6_RENEW] = "RENEW",
85 [DHCP6_REBIND] = "REBIND",
86 [DHCP6_REPLY] = "REPLY",
87 [DHCP6_RELEASE] = "RELEASE",
88 [DHCP6_DECLINE] = "DECLINE",
89 [DHCP6_RECONFIGURE] = "RECONFIGURE",
90 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
91 [DHCP6_RELAY_FORW] = "RELAY-FORW",
92 [DHCP6_RELAY_REPL] = "RELAY-REPL",
95 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
97 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
98 [DHCP6_STATUS_SUCCESS] = "Success",
99 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
100 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
101 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
102 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
103 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
106 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
108 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
109 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
111 #define DHCP6_CLIENT_DONT_DESTROY(client) \
112 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
114 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
116 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
117 sd_dhcp6_client_cb_t cb, void *userdata)
119 assert_return(client, -EINVAL);
122 client->userdata = userdata;
127 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
129 assert_return(client, -EINVAL);
130 assert_return(interface_index >= -1, -EINVAL);
132 client->index = interface_index;
137 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
138 const struct ether_addr *mac_addr)
140 assert_return(client, -EINVAL);
143 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
145 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
150 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
154 assert_return(client, -EINVAL);
155 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
158 case DHCP6_OPTION_DNS_SERVERS:
159 case DHCP6_OPTION_DOMAIN_LIST:
160 case DHCP6_OPTION_SNTP_SERVERS:
161 case DHCP6_OPTION_NTP_SERVER:
168 for (t = 0; t < client->req_opts_len; t++)
169 if (client->req_opts[t] == htobe16(option))
172 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
173 client->req_opts_len + 1))
176 client->req_opts[client->req_opts_len++] = htobe16(option);
181 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
182 assert_return(client, -EINVAL);
183 assert_return(ret, -EINVAL);
188 *ret = sd_dhcp6_lease_ref(client->lease);
193 static void client_notify(sd_dhcp6_client *client, int event) {
195 client->cb(client, event, client->userdata);
198 static int client_reset(sd_dhcp6_client *client) {
199 assert_return(client, -EINVAL);
201 client->receive_message =
202 sd_event_source_unref(client->receive_message);
204 client->fd = safe_close(client->fd);
206 client->transaction_id = 0;
207 client->transaction_start = 0;
209 client->ia_na.timeout_t1 =
210 sd_event_source_unref(client->ia_na.timeout_t1);
211 client->ia_na.timeout_t2 =
212 sd_event_source_unref(client->ia_na.timeout_t2);
214 client->retransmit_time = 0;
215 client->retransmit_count = 0;
216 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
217 client->timeout_resend_expire =
218 sd_event_source_unref(client->timeout_resend_expire);
220 client->state = DHCP6_STATE_STOPPED;
225 static void client_stop(sd_dhcp6_client *client, int error) {
226 DHCP6_CLIENT_DONT_DESTROY(client);
230 client_notify(client, error);
232 client_reset(client);
235 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
236 _cleanup_free_ DHCP6Message *message = NULL;
237 struct in6_addr all_servers =
238 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
239 size_t len, optlen = 512;
245 len = sizeof(DHCP6Message) + optlen;
247 message = malloc0(len);
251 opt = (uint8_t *)(message + 1);
253 message->transaction_id = client->transaction_id;
255 switch(client->state) {
256 case DHCP6_STATE_SOLICITATION:
257 message->type = DHCP6_SOLICIT;
259 r = dhcp6_option_append(&opt, &optlen,
260 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
264 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
270 case DHCP6_STATE_REQUEST:
271 case DHCP6_STATE_RENEW:
273 if (client->state == DHCP6_STATE_REQUEST)
274 message->type = DHCP6_REQUEST;
276 message->type = DHCP6_RENEW;
278 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
279 client->lease->serverid_len,
280 client->lease->serverid);
284 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
290 case DHCP6_STATE_REBIND:
291 message->type = DHCP6_REBIND;
293 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
299 case DHCP6_STATE_STOPPED:
300 case DHCP6_STATE_BOUND:
304 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
305 client->req_opts_len * sizeof(be16_t),
310 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
311 sizeof(client->duid), &client->duid);
315 elapsed_usec = time_now - client->transaction_start;
316 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
317 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
319 elapsed_time = 0xffff;
321 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
322 sizeof(elapsed_time), &elapsed_time);
326 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
331 log_dhcp6_client(client, "Sent %s",
332 dhcp6_message_type_to_string(message->type));
337 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
339 sd_dhcp6_client *client = userdata;
341 assert_return(s, -EINVAL);
342 assert_return(client, -EINVAL);
343 assert_return(client->lease, -EINVAL);
345 client->lease->ia.timeout_t2 =
346 sd_event_source_unref(client->lease->ia.timeout_t2);
348 log_dhcp6_client(client, "Timeout T2");
350 client_start(client, DHCP6_STATE_REBIND);
355 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
357 sd_dhcp6_client *client = userdata;
359 assert_return(s, -EINVAL);
360 assert_return(client, -EINVAL);
361 assert_return(client->lease, -EINVAL);
363 client->lease->ia.timeout_t1 =
364 sd_event_source_unref(client->lease->ia.timeout_t1);
366 log_dhcp6_client(client, "Timeout T1");
368 client_start(client, DHCP6_STATE_RENEW);
373 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
375 sd_dhcp6_client *client = userdata;
376 DHCP6_CLIENT_DONT_DESTROY(client);
377 enum DHCP6State state;
381 assert(client->event);
383 state = client->state;
385 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
387 /* RFC 3315, section 18.1.4., says that "...the client may choose to
388 use a Solicit message to locate a new DHCP server..." */
389 if (state == DHCP6_STATE_REBIND)
390 client_start(client, DHCP6_STATE_SOLICITATION);
395 static usec_t client_timeout_compute_random(usec_t val) {
396 return val - val / 10 +
397 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
400 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
403 sd_dhcp6_client *client = userdata;
404 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
405 usec_t max_retransmit_duration = 0;
406 uint8_t max_retransmit_count = 0;
407 char time_string[FORMAT_TIMESPAN_MAX];
412 assert(client->event);
414 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
416 switch (client->state) {
417 case DHCP6_STATE_SOLICITATION:
419 if (client->retransmit_count && client->lease) {
420 client_start(client, DHCP6_STATE_REQUEST);
424 init_retransmit_time = DHCP6_SOL_TIMEOUT;
425 max_retransmit_time = DHCP6_SOL_MAX_RT;
429 case DHCP6_STATE_REQUEST:
430 init_retransmit_time = DHCP6_REQ_TIMEOUT;
431 max_retransmit_time = DHCP6_REQ_MAX_RT;
432 max_retransmit_count = DHCP6_REQ_MAX_RC;
436 case DHCP6_STATE_RENEW:
437 init_retransmit_time = DHCP6_REN_TIMEOUT;
438 max_retransmit_time = DHCP6_REN_MAX_RT;
440 /* RFC 3315, section 18.1.3. says max retransmit duration will
441 be the remaining time until T2. Instead of setting MRD,
442 wait for T2 to trigger with the same end result */
446 case DHCP6_STATE_REBIND:
447 init_retransmit_time = DHCP6_REB_TIMEOUT;
448 max_retransmit_time = DHCP6_REB_MAX_RT;
450 if (!client->timeout_resend_expire) {
451 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
454 client_stop(client, r);
457 max_retransmit_duration = expire * USEC_PER_SEC;
462 case DHCP6_STATE_STOPPED:
463 case DHCP6_STATE_BOUND:
467 if (max_retransmit_count &&
468 client->retransmit_count >= max_retransmit_count) {
469 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
473 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
477 r = client_send_message(client, time_now);
479 client->retransmit_count++;
481 if (!client->retransmit_time) {
482 client->retransmit_time =
483 client_timeout_compute_random(init_retransmit_time);
485 if (client->state == DHCP6_STATE_SOLICITATION)
486 client->retransmit_time += init_retransmit_time / 10;
489 if (max_retransmit_time &&
490 client->retransmit_time > max_retransmit_time / 2)
491 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
493 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
496 log_dhcp6_client(client, "Next retransmission in %s",
497 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
498 client->retransmit_time, 0));
500 r = sd_event_add_time(client->event, &client->timeout_resend,
501 clock_boottime_or_monotonic(),
502 time_now + client->retransmit_time,
503 10 * USEC_PER_MSEC, client_timeout_resend,
508 r = sd_event_source_set_priority(client->timeout_resend,
509 client->event_priority);
513 r = sd_event_source_set_name(client->timeout_resend,
514 "dhcp6-resend-timer");
518 if (max_retransmit_duration && !client->timeout_resend_expire) {
520 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
521 max_retransmit_duration / USEC_PER_SEC);
523 r = sd_event_add_time(client->event,
524 &client->timeout_resend_expire,
525 clock_boottime_or_monotonic(),
526 time_now + max_retransmit_duration,
528 client_timeout_resend_expire, client);
532 r = sd_event_source_set_priority(client->timeout_resend_expire,
533 client->event_priority);
537 r = sd_event_source_set_name(client->timeout_resend_expire,
538 "dhcp6-resend-expire-timer");
545 client_stop(client, r);
550 static int client_ensure_iaid(sd_dhcp6_client *client) {
551 /* name is a pointer to memory in the udev_device struct, so must
552 have the same scope */
553 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
554 const char *name = NULL;
559 if (client->ia_na.id)
562 if (detect_container(NULL) <= 0) {
563 /* not in a container, udev will be around */
564 _cleanup_udev_unref_ struct udev *udev;
565 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
571 sprintf(ifindex_str, "n%d", client->index);
572 device = udev_device_new_from_device_id(udev, ifindex_str);
576 if (udev_device_get_is_initialized(device) <= 0)
580 name = net_get_name(device);
584 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
586 /* fall back to mac address if no predictable name available */
587 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
590 /* fold into 32 bits */
591 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
596 static int client_parse_message(sd_dhcp6_client *client,
597 DHCP6Message *message, size_t len,
598 sd_dhcp6_lease *lease) {
600 uint8_t *optval, *option, *id = NULL;
601 uint16_t optcode, status;
602 size_t optlen, id_len;
603 bool clientid = false;
606 option = (uint8_t *)message + sizeof(DHCP6Message);
607 len -= sizeof(DHCP6Message);
609 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
612 case DHCP6_OPTION_CLIENTID:
614 log_dhcp6_client(client, "%s contains multiple clientids",
615 dhcp6_message_type_to_string(message->type));
619 if (optlen != sizeof(client->duid) ||
620 memcmp(&client->duid, optval, optlen) != 0) {
621 log_dhcp6_client(client, "%s DUID does not match",
622 dhcp6_message_type_to_string(message->type));
630 case DHCP6_OPTION_SERVERID:
631 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
633 log_dhcp6_client(client, "%s contains multiple serverids",
634 dhcp6_message_type_to_string(message->type));
638 r = dhcp6_lease_set_serverid(lease, optval, optlen);
644 case DHCP6_OPTION_PREFERENCE:
648 r = dhcp6_lease_set_preference(lease, *optval);
654 case DHCP6_OPTION_STATUS_CODE:
658 status = optval[0] << 8 | optval[1];
660 log_dhcp6_client(client, "%s Status %s",
661 dhcp6_message_type_to_string(message->type),
662 dhcp6_message_status_to_string(status));
668 case DHCP6_OPTION_IA_NA:
669 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
671 if (r < 0 && r != -ENOMSG)
674 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
678 if (client->ia_na.id != iaid_lease) {
679 log_dhcp6_client(client, "%s has wrong IAID",
680 dhcp6_message_type_to_string(message->type));
686 case DHCP6_OPTION_RAPID_COMMIT:
687 r = dhcp6_lease_set_rapid_commit(lease);
695 if ((r < 0 && r != -ENOMSG) || !clientid) {
696 log_dhcp6_client(client, "%s has incomplete options",
697 dhcp6_message_type_to_string(message->type));
701 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
703 log_dhcp6_client(client, "%s has no server id",
704 dhcp6_message_type_to_string(message->type));
709 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
713 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
716 if (reply->type != DHCP6_REPLY)
719 r = dhcp6_lease_new(&lease);
723 r = client_parse_message(client, reply, len, lease);
727 if (client->state == DHCP6_STATE_SOLICITATION) {
728 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
737 dhcp6_lease_clear_timers(&client->lease->ia);
739 client->lease = sd_dhcp6_lease_unref(client->lease);
740 client->lease = lease;
743 return DHCP6_STATE_BOUND;
746 static int client_receive_advertise(sd_dhcp6_client *client,
747 DHCP6Message *advertise, size_t len) {
749 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
750 uint8_t pref_advertise = 0, pref_lease = 0;
752 if (advertise->type != DHCP6_ADVERTISE)
755 r = dhcp6_lease_new(&lease);
759 r = client_parse_message(client, advertise, len, lease);
763 r = dhcp6_lease_get_preference(lease, &pref_advertise);
767 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
768 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
769 sd_dhcp6_lease_unref(client->lease);
770 client->lease = lease;
775 if (pref_advertise == 255 || client->retransmit_count > 1)
776 r = DHCP6_STATE_REQUEST;
781 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
783 sd_dhcp6_client *client = userdata;
784 DHCP6_CLIENT_DONT_DESTROY(client);
785 _cleanup_free_ DHCP6Message *message;
790 assert(client->event);
792 r = ioctl(fd, FIONREAD, &buflen);
793 if (r < 0 || buflen <= 0)
794 buflen = DHCP6_MIN_OPTIONS_SIZE;
796 message = malloc0(buflen);
800 len = read(fd, message, buflen);
801 if ((size_t)len < sizeof(DHCP6Message)) {
802 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
806 switch(message->type) {
814 case DHCP6_INFORMATION_REQUEST:
815 case DHCP6_RELAY_FORW:
816 case DHCP6_RELAY_REPL:
819 case DHCP6_ADVERTISE:
821 case DHCP6_RECONFIGURE:
825 log_dhcp6_client(client, "unknown message type %d",
830 if (client->transaction_id != (message->transaction_id &
831 htobe32(0x00ffffff)))
834 switch (client->state) {
835 case DHCP6_STATE_SOLICITATION:
836 r = client_receive_advertise(client, message, len);
838 if (r == DHCP6_STATE_REQUEST) {
839 client_start(client, r);
844 /* fall through for Soliciation Rapid Commit option check */
845 case DHCP6_STATE_REQUEST:
846 case DHCP6_STATE_RENEW:
847 case DHCP6_STATE_REBIND:
849 r = client_receive_reply(client, message, len);
853 if (r == DHCP6_STATE_BOUND) {
855 r = client_start(client, DHCP6_STATE_BOUND);
857 client_stop(client, r);
861 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
866 case DHCP6_STATE_BOUND:
870 case DHCP6_STATE_STOPPED:
875 log_dhcp6_client(client, "Recv %s",
876 dhcp6_message_type_to_string(message->type));
882 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
885 usec_t timeout, time_now;
886 char time_string[FORMAT_TIMESPAN_MAX];
888 assert_return(client, -EINVAL);
889 assert_return(client->event, -EINVAL);
890 assert_return(client->index > 0, -EINVAL);
891 assert_return(client->state != state, -EINVAL);
893 client->timeout_resend_expire =
894 sd_event_source_unref(client->timeout_resend_expire);
895 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
896 client->retransmit_time = 0;
897 client->retransmit_count = 0;
899 if (client->state == DHCP6_STATE_STOPPED) {
900 time_now = now(clock_boottime_or_monotonic());
902 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
909 case DHCP6_STATE_STOPPED:
910 case DHCP6_STATE_SOLICITATION:
912 r = client_ensure_iaid(client);
916 r = dhcp6_network_bind_udp_socket(client->index, NULL);
922 r = sd_event_add_io(client->event, &client->receive_message,
923 client->fd, EPOLLIN, client_receive_message,
928 r = sd_event_source_set_priority(client->receive_message,
929 client->event_priority);
933 r = sd_event_source_set_name(client->receive_message,
934 "dhcp6-receive-message");
938 client->state = DHCP6_STATE_SOLICITATION;
942 case DHCP6_STATE_REQUEST:
943 case DHCP6_STATE_RENEW:
944 case DHCP6_STATE_REBIND:
946 client->state = state;
950 case DHCP6_STATE_BOUND:
952 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
953 client->lease->ia.lifetime_t2 == 0xffffffff) {
955 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
956 be32toh(client->lease->ia.lifetime_t1),
957 be32toh(client->lease->ia.lifetime_t2));
962 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
964 log_dhcp6_client(client, "T1 expires in %s",
965 format_timespan(time_string,
969 r = sd_event_add_time(client->event,
970 &client->lease->ia.timeout_t1,
971 clock_boottime_or_monotonic(), time_now + timeout,
972 10 * USEC_PER_SEC, client_timeout_t1,
977 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
978 client->event_priority);
982 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
987 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
989 log_dhcp6_client(client, "T2 expires in %s",
990 format_timespan(time_string,
994 r = sd_event_add_time(client->event,
995 &client->lease->ia.timeout_t2,
996 clock_boottime_or_monotonic(), time_now + timeout,
997 10 * USEC_PER_SEC, client_timeout_t2,
1002 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1003 client->event_priority);
1007 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
1008 "dhcp6-t2-timeout");
1012 client->state = state;
1017 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1018 client->transaction_start = time_now;
1020 r = sd_event_add_time(client->event, &client->timeout_resend,
1021 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1026 r = sd_event_source_set_priority(client->timeout_resend,
1027 client->event_priority);
1031 r = sd_event_source_set_name(client->timeout_resend,
1032 "dhcp6-resend-timeout");
1039 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1041 client_stop(client, DHCP6_EVENT_STOP);
1046 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1050 assert_return(client, -EINVAL);
1051 assert_return(client->event, -EINVAL);
1052 assert_return(client->index > 0, -EINVAL);
1054 r = client_reset(client);
1058 return client_start(client, DHCP6_STATE_SOLICITATION);
1061 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1066 assert_return(client, -EINVAL);
1067 assert_return(!client->event, -EBUSY);
1070 client->event = sd_event_ref(event);
1072 r = sd_event_default(&client->event);
1077 client->event_priority = priority;
1082 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1083 assert_return(client, -EINVAL);
1085 client->event = sd_event_unref(client->event);
1090 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1094 return client->event;
1097 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1099 assert_se(REFCNT_INC(client->n_ref) >= 2);
1104 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1105 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1106 client_reset(client);
1108 sd_dhcp6_client_detach_event(client);
1110 free(client->req_opts);
1119 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1121 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1122 sd_id128_t machine_id;
1126 assert_return(ret, -EINVAL);
1128 client = new0(sd_dhcp6_client, 1);
1132 client->n_ref = REFCNT_INIT;
1134 client->ia_na.type = DHCP6_OPTION_IA_NA;
1140 /* initialize DUID */
1141 client->duid.type = htobe16(DHCP6_DUID_EN);
1142 client->duid.pen = htobe32(SYSTEMD_PEN);
1144 r = sd_id128_get_machine(&machine_id);
1148 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1150 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1153 client->req_opts_len = ELEMENTSOF(default_req_opts);
1155 client->req_opts = new0(be16_t, client->req_opts_len);
1156 if (!client->req_opts)
1159 for (t = 0; t < client->req_opts_len; t++)
1160 client->req_opts[t] = htobe16(default_req_opts[t]);