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 struct sd_dhcp6_lease *lease;
55 size_t req_opts_allocated;
57 sd_event_source *receive_message;
58 usec_t retransmit_time;
59 uint8_t retransmit_count;
60 sd_event_source *timeout_resend;
61 sd_event_source *timeout_resend_expire;
62 sd_dhcp6_client_cb_t cb;
66 uint16_t type; /* DHCP6_DUID_EN */
72 static const uint16_t default_req_opts[] = {
73 DHCP6_OPTION_DNS_SERVERS,
74 DHCP6_OPTION_DOMAIN_LIST,
75 DHCP6_OPTION_NTP_SERVER,
78 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
79 [DHCP6_SOLICIT] = "SOLICIT",
80 [DHCP6_ADVERTISE] = "ADVERTISE",
81 [DHCP6_REQUEST] = "REQUEST",
82 [DHCP6_CONFIRM] = "CONFIRM",
83 [DHCP6_RENEW] = "RENEW",
84 [DHCP6_REBIND] = "REBIND",
85 [DHCP6_REPLY] = "REPLY",
86 [DHCP6_RELEASE] = "RELEASE",
87 [DHCP6_DECLINE] = "DECLINE",
88 [DHCP6_RECONFIGURE] = "RECONFIGURE",
89 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
90 [DHCP6_RELAY_FORW] = "RELAY-FORW",
91 [DHCP6_RELAY_REPL] = "RELAY-REPL",
94 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
96 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
97 [DHCP6_STATUS_SUCCESS] = "Success",
98 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
99 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
100 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
101 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
102 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
105 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
107 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
108 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
110 #define DHCP6_CLIENT_DONT_DESTROY(client) \
111 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
113 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
115 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
116 sd_dhcp6_client_cb_t cb, void *userdata)
118 assert_return(client, -EINVAL);
121 client->userdata = userdata;
126 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
128 assert_return(client, -EINVAL);
129 assert_return(interface_index >= -1, -EINVAL);
131 client->index = interface_index;
136 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
137 const struct ether_addr *mac_addr)
139 assert_return(client, -EINVAL);
142 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
144 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
149 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
153 assert_return(client, -EINVAL);
154 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
157 case DHCP6_OPTION_DNS_SERVERS:
158 case DHCP6_OPTION_DOMAIN_LIST:
159 case DHCP6_OPTION_SNTP_SERVERS:
160 case DHCP6_OPTION_NTP_SERVER:
167 for (t = 0; t < client->req_opts_len; t++)
168 if (client->req_opts[t] == htobe16(option))
171 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
172 client->req_opts_len + 1))
175 client->req_opts[client->req_opts_len++] = htobe16(option);
180 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
181 assert_return(client, -EINVAL);
182 assert_return(ret, -EINVAL);
187 *ret = sd_dhcp6_lease_ref(client->lease);
192 static void client_notify(sd_dhcp6_client *client, int event) {
194 client->cb(client, event, client->userdata);
197 static int client_reset(sd_dhcp6_client *client) {
198 assert_return(client, -EINVAL);
200 client->receive_message =
201 sd_event_source_unref(client->receive_message);
203 client->fd = safe_close(client->fd);
205 client->transaction_id = 0;
207 client->ia_na.timeout_t1 =
208 sd_event_source_unref(client->ia_na.timeout_t1);
209 client->ia_na.timeout_t2 =
210 sd_event_source_unref(client->ia_na.timeout_t2);
212 client->retransmit_time = 0;
213 client->retransmit_count = 0;
214 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
215 client->timeout_resend_expire =
216 sd_event_source_unref(client->timeout_resend_expire);
218 client->state = DHCP6_STATE_STOPPED;
223 static void client_stop(sd_dhcp6_client *client, int error) {
224 DHCP6_CLIENT_DONT_DESTROY(client);
228 client_notify(client, error);
230 client_reset(client);
233 static int client_send_message(sd_dhcp6_client *client) {
234 _cleanup_free_ DHCP6Message *message = NULL;
235 struct in6_addr all_servers =
236 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
237 size_t len, optlen = 512;
241 len = sizeof(DHCP6Message) + optlen;
243 message = malloc0(len);
247 opt = (uint8_t *)(message + 1);
249 message->transaction_id = client->transaction_id;
251 switch(client->state) {
252 case DHCP6_STATE_SOLICITATION:
253 message->type = DHCP6_SOLICIT;
255 r = dhcp6_option_append(&opt, &optlen,
256 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
260 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
266 case DHCP6_STATE_REQUEST:
267 case DHCP6_STATE_RENEW:
269 if (client->state == DHCP6_STATE_REQUEST)
270 message->type = DHCP6_REQUEST;
272 message->type = DHCP6_RENEW;
274 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
275 client->lease->serverid_len,
276 client->lease->serverid);
280 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
286 case DHCP6_STATE_REBIND:
287 message->type = DHCP6_REBIND;
289 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
295 case DHCP6_STATE_STOPPED:
296 case DHCP6_STATE_BOUND:
300 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
301 client->req_opts_len * sizeof(be16_t),
306 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
307 sizeof(client->duid), &client->duid);
311 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
316 log_dhcp6_client(client, "Sent %s",
317 dhcp6_message_type_to_string(message->type));
322 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
324 sd_dhcp6_client *client = userdata;
326 assert_return(s, -EINVAL);
327 assert_return(client, -EINVAL);
328 assert_return(client->lease, -EINVAL);
330 client->lease->ia.timeout_t2 =
331 sd_event_source_unref(client->lease->ia.timeout_t2);
333 log_dhcp6_client(client, "Timeout T2");
335 client_start(client, DHCP6_STATE_REBIND);
340 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
342 sd_dhcp6_client *client = userdata;
344 assert_return(s, -EINVAL);
345 assert_return(client, -EINVAL);
346 assert_return(client->lease, -EINVAL);
348 client->lease->ia.timeout_t1 =
349 sd_event_source_unref(client->lease->ia.timeout_t1);
351 log_dhcp6_client(client, "Timeout T1");
353 client_start(client, DHCP6_STATE_RENEW);
358 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
360 sd_dhcp6_client *client = userdata;
361 DHCP6_CLIENT_DONT_DESTROY(client);
362 enum DHCP6State state;
366 assert(client->event);
368 state = client->state;
370 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
372 /* RFC 3315, section 18.1.4., says that "...the client may choose to
373 use a Solicit message to locate a new DHCP server..." */
374 if (state == DHCP6_STATE_REBIND)
375 client_start(client, DHCP6_STATE_SOLICITATION);
380 static usec_t client_timeout_compute_random(usec_t val) {
381 return val - val / 10 +
382 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
385 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
388 sd_dhcp6_client *client = userdata;
389 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
390 usec_t max_retransmit_duration = 0;
391 uint8_t max_retransmit_count = 0;
392 char time_string[FORMAT_TIMESPAN_MAX];
397 assert(client->event);
399 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
401 switch (client->state) {
402 case DHCP6_STATE_SOLICITATION:
404 if (client->retransmit_count && client->lease) {
405 client_start(client, DHCP6_STATE_REQUEST);
409 init_retransmit_time = DHCP6_SOL_TIMEOUT;
410 max_retransmit_time = DHCP6_SOL_MAX_RT;
414 case DHCP6_STATE_REQUEST:
415 init_retransmit_time = DHCP6_REQ_TIMEOUT;
416 max_retransmit_time = DHCP6_REQ_MAX_RT;
417 max_retransmit_count = DHCP6_REQ_MAX_RC;
421 case DHCP6_STATE_RENEW:
422 init_retransmit_time = DHCP6_REN_TIMEOUT;
423 max_retransmit_time = DHCP6_REN_MAX_RT;
425 /* RFC 3315, section 18.1.3. says max retransmit duration will
426 be the remaining time until T2. Instead of setting MRD,
427 wait for T2 to trigger with the same end result */
431 case DHCP6_STATE_REBIND:
432 init_retransmit_time = DHCP6_REB_TIMEOUT;
433 max_retransmit_time = DHCP6_REB_MAX_RT;
435 if (!client->timeout_resend_expire) {
436 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
439 client_stop(client, r);
442 max_retransmit_duration = expire * USEC_PER_SEC;
447 case DHCP6_STATE_STOPPED:
448 case DHCP6_STATE_BOUND:
452 if (max_retransmit_count &&
453 client->retransmit_count >= max_retransmit_count) {
454 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
458 r = client_send_message(client);
460 client->retransmit_count++;
463 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
467 if (!client->retransmit_time) {
468 client->retransmit_time =
469 client_timeout_compute_random(init_retransmit_time);
471 if (client->state == DHCP6_STATE_SOLICITATION)
472 client->retransmit_time += init_retransmit_time / 10;
475 if (max_retransmit_time &&
476 client->retransmit_time > max_retransmit_time / 2)
477 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
479 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
482 log_dhcp6_client(client, "Next retransmission in %s",
483 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
484 client->retransmit_time, 0));
486 r = sd_event_add_time(client->event, &client->timeout_resend,
487 clock_boottime_or_monotonic(),
488 time_now + client->retransmit_time,
489 10 * USEC_PER_MSEC, client_timeout_resend,
494 r = sd_event_source_set_priority(client->timeout_resend,
495 client->event_priority);
499 r = sd_event_source_set_name(client->timeout_resend,
500 "dhcp6-resend-timer");
504 if (max_retransmit_duration && !client->timeout_resend_expire) {
506 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
507 max_retransmit_duration / USEC_PER_SEC);
509 r = sd_event_add_time(client->event,
510 &client->timeout_resend_expire,
511 clock_boottime_or_monotonic(),
512 time_now + max_retransmit_duration,
514 client_timeout_resend_expire, client);
518 r = sd_event_source_set_priority(client->timeout_resend_expire,
519 client->event_priority);
523 r = sd_event_source_set_name(client->timeout_resend_expire,
524 "dhcp6-resend-expire-timer");
531 client_stop(client, r);
536 static int client_ensure_iaid(sd_dhcp6_client *client) {
537 /* name is a pointer to memory in the udev_device struct, so must
538 have the same scope */
539 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
540 const char *name = NULL;
545 if (client->ia_na.id)
548 if (detect_container(NULL) <= 0) {
549 /* not in a container, udev will be around */
550 _cleanup_udev_unref_ struct udev *udev;
551 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
557 sprintf(ifindex_str, "n%d", client->index);
558 device = udev_device_new_from_device_id(udev, ifindex_str);
562 if (udev_device_get_is_initialized(device) <= 0)
566 name = net_get_name(device);
570 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
572 /* fall back to mac address if no predictable name available */
573 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
576 /* fold into 32 bits */
577 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
582 static int client_parse_message(sd_dhcp6_client *client,
583 DHCP6Message *message, size_t len,
584 sd_dhcp6_lease *lease) {
586 uint8_t *optval, *option, *id = NULL;
587 uint16_t optcode, status;
588 size_t optlen, id_len;
589 bool clientid = false;
592 option = (uint8_t *)message + sizeof(DHCP6Message);
593 len -= sizeof(DHCP6Message);
595 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
598 case DHCP6_OPTION_CLIENTID:
600 log_dhcp6_client(client, "%s contains multiple clientids",
601 dhcp6_message_type_to_string(message->type));
605 if (optlen != sizeof(client->duid) ||
606 memcmp(&client->duid, optval, optlen) != 0) {
607 log_dhcp6_client(client, "%s DUID does not match",
608 dhcp6_message_type_to_string(message->type));
616 case DHCP6_OPTION_SERVERID:
617 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
619 log_dhcp6_client(client, "%s contains multiple serverids",
620 dhcp6_message_type_to_string(message->type));
624 r = dhcp6_lease_set_serverid(lease, optval, optlen);
630 case DHCP6_OPTION_PREFERENCE:
634 r = dhcp6_lease_set_preference(lease, *optval);
640 case DHCP6_OPTION_STATUS_CODE:
644 status = optval[0] << 8 | optval[1];
646 log_dhcp6_client(client, "%s Status %s",
647 dhcp6_message_type_to_string(message->type),
648 dhcp6_message_status_to_string(status));
654 case DHCP6_OPTION_IA_NA:
655 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
657 if (r < 0 && r != -ENOMSG)
660 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
664 if (client->ia_na.id != iaid_lease) {
665 log_dhcp6_client(client, "%s has wrong IAID",
666 dhcp6_message_type_to_string(message->type));
672 case DHCP6_OPTION_RAPID_COMMIT:
673 r = dhcp6_lease_set_rapid_commit(lease);
681 if ((r < 0 && r != -ENOMSG) || !clientid) {
682 log_dhcp6_client(client, "%s has incomplete options",
683 dhcp6_message_type_to_string(message->type));
687 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
689 log_dhcp6_client(client, "%s has no server id",
690 dhcp6_message_type_to_string(message->type));
695 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
699 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
702 if (reply->type != DHCP6_REPLY)
705 r = dhcp6_lease_new(&lease);
709 r = client_parse_message(client, reply, len, lease);
713 if (client->state == DHCP6_STATE_SOLICITATION) {
714 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
723 dhcp6_lease_clear_timers(&client->lease->ia);
725 client->lease = sd_dhcp6_lease_unref(client->lease);
726 client->lease = lease;
729 return DHCP6_STATE_BOUND;
732 static int client_receive_advertise(sd_dhcp6_client *client,
733 DHCP6Message *advertise, size_t len) {
735 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
736 uint8_t pref_advertise = 0, pref_lease = 0;
738 if (advertise->type != DHCP6_ADVERTISE)
741 r = dhcp6_lease_new(&lease);
745 r = client_parse_message(client, advertise, len, lease);
749 r = dhcp6_lease_get_preference(lease, &pref_advertise);
753 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
754 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
755 sd_dhcp6_lease_unref(client->lease);
756 client->lease = lease;
761 if (pref_advertise == 255 || client->retransmit_count > 1)
762 r = DHCP6_STATE_REQUEST;
767 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
769 sd_dhcp6_client *client = userdata;
770 DHCP6_CLIENT_DONT_DESTROY(client);
771 _cleanup_free_ DHCP6Message *message;
776 assert(client->event);
778 r = ioctl(fd, FIONREAD, &buflen);
779 if (r < 0 || buflen <= 0)
780 buflen = DHCP6_MIN_OPTIONS_SIZE;
782 message = malloc0(buflen);
786 len = read(fd, message, buflen);
787 if ((size_t)len < sizeof(DHCP6Message)) {
788 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
792 switch(message->type) {
800 case DHCP6_INFORMATION_REQUEST:
801 case DHCP6_RELAY_FORW:
802 case DHCP6_RELAY_REPL:
805 case DHCP6_ADVERTISE:
807 case DHCP6_RECONFIGURE:
811 log_dhcp6_client(client, "unknown message type %d",
816 if (client->transaction_id != (message->transaction_id &
817 htobe32(0x00ffffff)))
820 switch (client->state) {
821 case DHCP6_STATE_SOLICITATION:
822 r = client_receive_advertise(client, message, len);
824 if (r == DHCP6_STATE_REQUEST) {
825 client_start(client, r);
830 /* fall through for Soliciation Rapid Commit option check */
831 case DHCP6_STATE_REQUEST:
832 case DHCP6_STATE_RENEW:
833 case DHCP6_STATE_REBIND:
835 r = client_receive_reply(client, message, len);
839 if (r == DHCP6_STATE_BOUND) {
841 r = client_start(client, DHCP6_STATE_BOUND);
843 client_stop(client, r);
847 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
852 case DHCP6_STATE_BOUND:
856 case DHCP6_STATE_STOPPED:
861 log_dhcp6_client(client, "Recv %s",
862 dhcp6_message_type_to_string(message->type));
868 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
871 usec_t timeout, time_now;
872 char time_string[FORMAT_TIMESPAN_MAX];
874 assert_return(client, -EINVAL);
875 assert_return(client->event, -EINVAL);
876 assert_return(client->index > 0, -EINVAL);
877 assert_return(client->state != state, -EINVAL);
879 client->timeout_resend_expire =
880 sd_event_source_unref(client->timeout_resend_expire);
881 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
882 client->retransmit_time = 0;
883 client->retransmit_count = 0;
886 case DHCP6_STATE_STOPPED:
887 case DHCP6_STATE_SOLICITATION:
889 r = client_ensure_iaid(client);
893 r = dhcp6_network_bind_udp_socket(client->index, NULL);
899 r = sd_event_add_io(client->event, &client->receive_message,
900 client->fd, EPOLLIN, client_receive_message,
905 r = sd_event_source_set_priority(client->receive_message,
906 client->event_priority);
910 r = sd_event_source_set_name(client->receive_message,
911 "dhcp6-receive-message");
915 client->state = DHCP6_STATE_SOLICITATION;
919 case DHCP6_STATE_REQUEST:
920 case DHCP6_STATE_RENEW:
921 case DHCP6_STATE_REBIND:
923 client->state = state;
927 case DHCP6_STATE_BOUND:
929 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
933 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
934 client->lease->ia.lifetime_t2 == 0xffffffff) {
936 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
937 be32toh(client->lease->ia.lifetime_t1),
938 be32toh(client->lease->ia.lifetime_t2));
943 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
945 log_dhcp6_client(client, "T1 expires in %s",
946 format_timespan(time_string,
950 r = sd_event_add_time(client->event,
951 &client->lease->ia.timeout_t1,
952 clock_boottime_or_monotonic(), time_now + timeout,
953 10 * USEC_PER_SEC, client_timeout_t1,
958 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
959 client->event_priority);
963 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
968 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
970 log_dhcp6_client(client, "T2 expires in %s",
971 format_timespan(time_string,
975 r = sd_event_add_time(client->event,
976 &client->lease->ia.timeout_t2,
977 clock_boottime_or_monotonic(), time_now + timeout,
978 10 * USEC_PER_SEC, client_timeout_t2,
983 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
984 client->event_priority);
988 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
993 client->state = state;
998 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1000 r = sd_event_add_time(client->event, &client->timeout_resend,
1001 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1006 r = sd_event_source_set_priority(client->timeout_resend,
1007 client->event_priority);
1011 r = sd_event_source_set_name(client->timeout_resend,
1012 "dhcp6-resend-timeout");
1019 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1021 client_stop(client, DHCP6_EVENT_STOP);
1026 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1030 assert_return(client, -EINVAL);
1031 assert_return(client->event, -EINVAL);
1032 assert_return(client->index > 0, -EINVAL);
1034 r = client_reset(client);
1038 return client_start(client, DHCP6_STATE_SOLICITATION);
1041 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1046 assert_return(client, -EINVAL);
1047 assert_return(!client->event, -EBUSY);
1050 client->event = sd_event_ref(event);
1052 r = sd_event_default(&client->event);
1057 client->event_priority = priority;
1062 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1063 assert_return(client, -EINVAL);
1065 client->event = sd_event_unref(client->event);
1070 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1074 return client->event;
1077 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1079 assert_se(REFCNT_INC(client->n_ref) >= 2);
1084 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1085 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1086 client_reset(client);
1088 sd_dhcp6_client_detach_event(client);
1090 free(client->req_opts);
1099 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1101 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1102 sd_id128_t machine_id;
1106 assert_return(ret, -EINVAL);
1108 client = new0(sd_dhcp6_client, 1);
1112 client->n_ref = REFCNT_INIT;
1114 client->ia_na.type = DHCP6_OPTION_IA_NA;
1120 /* initialize DUID */
1121 client->duid.type = htobe16(DHCP6_DUID_EN);
1122 client->duid.pen = htobe32(SYSTEMD_PEN);
1124 r = sd_id128_get_machine(&machine_id);
1128 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1130 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1133 client->req_opts_len = ELEMENTSOF(default_req_opts);
1135 client->req_opts = new0(be16_t, client->req_opts_len);
1136 if (!client->req_opts)
1139 for (t = 0; t < client->req_opts_len; t++)
1140 client->req_opts[t] = htobe16(default_req_opts[t]);