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:
297 case DHCP6_STATE_BOUND:
301 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
302 client->req_opts_len * sizeof(be16_t),
307 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
308 sizeof(client->duid), &client->duid);
312 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
317 log_dhcp6_client(client, "Sent %s",
318 dhcp6_message_type_to_string(message->type));
323 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
325 sd_dhcp6_client *client = userdata;
327 assert_return(s, -EINVAL);
328 assert_return(client, -EINVAL);
329 assert_return(client->lease, -EINVAL);
331 client->lease->ia.timeout_t2 =
332 sd_event_source_unref(client->lease->ia.timeout_t2);
334 log_dhcp6_client(client, "Timeout T2");
336 client_start(client, DHCP6_STATE_REBIND);
341 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
343 sd_dhcp6_client *client = userdata;
345 assert_return(s, -EINVAL);
346 assert_return(client, -EINVAL);
347 assert_return(client->lease, -EINVAL);
349 client->lease->ia.timeout_t1 =
350 sd_event_source_unref(client->lease->ia.timeout_t1);
352 log_dhcp6_client(client, "Timeout T1");
354 client_start(client, DHCP6_STATE_RENEW);
359 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
361 sd_dhcp6_client *client = userdata;
362 DHCP6_CLIENT_DONT_DESTROY(client);
363 enum DHCP6State state;
367 assert(client->event);
369 state = client->state;
371 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
373 /* RFC 3315, section 18.1.4., says that "...the client may choose to
374 use a Solicit message to locate a new DHCP server..." */
375 if (state == DHCP6_STATE_REBIND)
376 client_start(client, DHCP6_STATE_SOLICITATION);
381 static usec_t client_timeout_compute_random(usec_t val) {
382 return val - val / 10 +
383 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
386 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
389 sd_dhcp6_client *client = userdata;
390 usec_t time_now, init_retransmit_time, max_retransmit_time;
391 usec_t max_retransmit_duration;
392 uint8_t max_retransmit_count = 0;
393 char time_string[FORMAT_TIMESPAN_MAX];
398 assert(client->event);
400 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
402 switch (client->state) {
403 case DHCP6_STATE_SOLICITATION:
405 if (client->retransmit_count && client->lease) {
406 client_start(client, DHCP6_STATE_REQUEST);
410 init_retransmit_time = DHCP6_SOL_TIMEOUT;
411 max_retransmit_time = DHCP6_SOL_MAX_RT;
412 max_retransmit_count = 0;
413 max_retransmit_duration = 0;
417 case DHCP6_STATE_REQUEST:
418 init_retransmit_time = DHCP6_REQ_TIMEOUT;
419 max_retransmit_time = DHCP6_REQ_MAX_RT;
420 max_retransmit_count = DHCP6_REQ_MAX_RC;
421 max_retransmit_duration = 0;
425 case DHCP6_STATE_RENEW:
426 init_retransmit_time = DHCP6_REN_TIMEOUT;
427 max_retransmit_time = DHCP6_REN_MAX_RT;
428 max_retransmit_count = 0;
430 /* RFC 3315, section 18.1.3. says max retransmit duration will
431 be the remaining time until T2. Instead of setting MRD,
432 wait for T2 to trigger with the same end result */
433 max_retransmit_duration = 0;
437 case DHCP6_STATE_REBIND:
438 init_retransmit_time = DHCP6_REB_TIMEOUT;
439 max_retransmit_time = DHCP6_REB_MAX_RT;
440 max_retransmit_count = 0;
442 max_retransmit_duration = 0;
444 if (!client->timeout_resend_expire) {
445 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
448 client_stop(client, r);
451 max_retransmit_duration = expire * USEC_PER_SEC;
456 case DHCP6_STATE_STOPPED:
458 case DHCP6_STATE_BOUND:
462 if (max_retransmit_count &&
463 client->retransmit_count >= max_retransmit_count) {
464 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
468 r = client_send_message(client);
470 client->retransmit_count++;
473 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
477 if (!client->retransmit_time) {
478 client->retransmit_time =
479 client_timeout_compute_random(init_retransmit_time);
481 if (client->state == DHCP6_STATE_SOLICITATION)
482 client->retransmit_time += init_retransmit_time / 10;
485 if (max_retransmit_time &&
486 client->retransmit_time > max_retransmit_time / 2)
487 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
489 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
492 log_dhcp6_client(client, "Next retransmission in %s",
493 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
494 client->retransmit_time, 0));
496 r = sd_event_add_time(client->event, &client->timeout_resend,
498 time_now + client->retransmit_time,
499 10 * USEC_PER_MSEC, client_timeout_resend,
504 r = sd_event_source_set_priority(client->timeout_resend,
505 client->event_priority);
509 if (max_retransmit_duration && !client->timeout_resend_expire) {
511 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
512 max_retransmit_duration / USEC_PER_SEC);
514 r = sd_event_add_time(client->event,
515 &client->timeout_resend_expire,
517 time_now + max_retransmit_duration,
519 client_timeout_resend_expire, client);
523 r = sd_event_source_set_priority(client->timeout_resend_expire,
524 client->event_priority);
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 = (uint8_t *)(message + 1), *id = NULL;
587 uint16_t optcode, status;
588 size_t optlen, id_len;
589 bool clientid = false;
592 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
595 case DHCP6_OPTION_CLIENTID:
597 log_dhcp6_client(client, "%s contains multiple clientids",
598 dhcp6_message_type_to_string(message->type));
602 if (optlen != sizeof(client->duid) ||
603 memcmp(&client->duid, optval, optlen) != 0) {
604 log_dhcp6_client(client, "%s DUID does not match",
605 dhcp6_message_type_to_string(message->type));
613 case DHCP6_OPTION_SERVERID:
614 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
616 log_dhcp6_client(client, "%s contains multiple serverids",
617 dhcp6_message_type_to_string(message->type));
621 r = dhcp6_lease_set_serverid(lease, optval, optlen);
627 case DHCP6_OPTION_PREFERENCE:
631 r = dhcp6_lease_set_preference(lease, *optval);
637 case DHCP6_OPTION_STATUS_CODE:
641 status = optval[0] << 8 | optval[1];
643 log_dhcp6_client(client, "%s Status %s",
644 dhcp6_message_type_to_string(message->type),
645 dhcp6_message_status_to_string(status));
651 case DHCP6_OPTION_IA_NA:
652 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
654 if (r < 0 && r != -ENOMSG)
657 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
661 if (client->ia_na.id != iaid_lease) {
662 log_dhcp6_client(client, "%s has wrong IAID",
663 dhcp6_message_type_to_string(message->type));
669 case DHCP6_OPTION_RAPID_COMMIT:
670 r = dhcp6_lease_set_rapid_commit(lease);
678 if ((r < 0 && r != -ENOMSG) || !clientid) {
679 log_dhcp6_client(client, "%s has incomplete options",
680 dhcp6_message_type_to_string(message->type));
684 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
686 log_dhcp6_client(client, "%s has no server id",
687 dhcp6_message_type_to_string(message->type));
692 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
696 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
699 if (reply->type != DHCP6_REPLY)
702 r = dhcp6_lease_new(&lease);
706 r = client_parse_message(client, reply, len, lease);
710 if (client->state == DHCP6_STATE_SOLICITATION) {
711 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
719 dhcp6_lease_clear_timers(&client->lease->ia);
721 client->lease = sd_dhcp6_lease_unref(client->lease);
722 client->lease = lease;
725 return DHCP6_STATE_BOUND;
728 static int client_receive_advertise(sd_dhcp6_client *client,
729 DHCP6Message *advertise, size_t len) {
731 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
732 uint8_t pref_advertise = 0, pref_lease = 0;
734 if (advertise->type != DHCP6_ADVERTISE)
737 r = dhcp6_lease_new(&lease);
741 r = client_parse_message(client, advertise, len, lease);
745 r = dhcp6_lease_get_preference(lease, &pref_advertise);
749 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
750 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
751 sd_dhcp6_lease_unref(client->lease);
752 client->lease = lease;
757 if (pref_advertise == 255 || client->retransmit_count > 1)
758 r = DHCP6_STATE_REQUEST;
763 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
765 sd_dhcp6_client *client = userdata;
766 DHCP6_CLIENT_DONT_DESTROY(client);
767 _cleanup_free_ DHCP6Message *message;
772 assert(client->event);
774 r = ioctl(fd, FIONREAD, &buflen);
775 if (r < 0 || buflen <= 0)
776 buflen = DHCP6_MIN_OPTIONS_SIZE;
778 message = malloc0(buflen);
782 len = read(fd, message, buflen);
783 if ((size_t)len < sizeof(DHCP6Message)) {
784 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
788 switch(message->type) {
796 case DHCP6_INFORMATION_REQUEST:
797 case DHCP6_RELAY_FORW:
798 case DHCP6_RELAY_REPL:
801 case DHCP6_ADVERTISE:
803 case DHCP6_RECONFIGURE:
807 log_dhcp6_client(client, "unknown message type %d",
812 if (client->transaction_id != (message->transaction_id &
813 htobe32(0x00ffffff)))
816 switch (client->state) {
817 case DHCP6_STATE_SOLICITATION:
818 r = client_receive_advertise(client, message, len);
820 if (r == DHCP6_STATE_REQUEST) {
821 client_start(client, r);
826 /* fall through for Soliciation Rapid Commit option check */
827 case DHCP6_STATE_REQUEST:
828 case DHCP6_STATE_RENEW:
829 case DHCP6_STATE_REBIND:
831 r = client_receive_reply(client, message, len);
835 if (r == DHCP6_STATE_BOUND) {
837 r = client_start(client, DHCP6_STATE_BOUND);
839 client_stop(client, r);
843 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
848 case DHCP6_STATE_BOUND:
852 case DHCP6_STATE_STOPPED:
858 log_dhcp6_client(client, "Recv %s",
859 dhcp6_message_type_to_string(message->type));
865 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
868 usec_t timeout, time_now;
869 char time_string[FORMAT_TIMESPAN_MAX];
871 assert_return(client, -EINVAL);
872 assert_return(client->event, -EINVAL);
873 assert_return(client->index > 0, -EINVAL);
874 assert_return(client->state != state, -EINVAL);
876 client->timeout_resend_expire =
877 sd_event_source_unref(client->timeout_resend_expire);
878 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
879 client->retransmit_time = 0;
880 client->retransmit_count = 0;
883 case DHCP6_STATE_STOPPED:
885 case DHCP6_STATE_SOLICITATION:
887 r = client_ensure_iaid(client);
891 r = dhcp6_network_bind_udp_socket(client->index, NULL);
897 r = sd_event_add_io(client->event, &client->receive_message,
898 client->fd, EPOLLIN, client_receive_message,
903 r = sd_event_source_set_priority(client->receive_message,
904 client->event_priority);
908 client->state = DHCP6_STATE_SOLICITATION;
912 case DHCP6_STATE_REQUEST:
913 case DHCP6_STATE_RENEW:
914 case DHCP6_STATE_REBIND:
916 client->state = state;
920 case DHCP6_STATE_BOUND:
922 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
926 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
927 client->lease->ia.lifetime_t2 == 0xffffffff) {
929 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
930 be32toh(client->lease->ia.lifetime_t1),
931 be32toh(client->lease->ia.lifetime_t2));
936 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
938 log_dhcp6_client(client, "T1 expires in %s",
939 format_timespan(time_string,
943 r = sd_event_add_time(client->event,
944 &client->lease->ia.timeout_t1,
945 CLOCK_MONOTONIC, time_now + timeout,
946 10 * USEC_PER_SEC, client_timeout_t1,
951 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
952 client->event_priority);
956 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
958 log_dhcp6_client(client, "T2 expires in %s",
959 format_timespan(time_string,
963 r = sd_event_add_time(client->event,
964 &client->lease->ia.timeout_t2,
965 CLOCK_MONOTONIC, time_now + timeout,
966 10 * USEC_PER_SEC, client_timeout_t2,
971 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
972 client->event_priority);
976 client->state = state;
981 client->transaction_id = random_u32() & htobe32(0x00ffffff);
983 r = sd_event_add_time(client->event, &client->timeout_resend,
984 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
989 r = sd_event_source_set_priority(client->timeout_resend,
990 client->event_priority);
997 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
999 client_stop(client, DHCP6_EVENT_STOP);
1004 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1008 assert_return(client, -EINVAL);
1009 assert_return(client->event, -EINVAL);
1010 assert_return(client->index > 0, -EINVAL);
1012 r = client_reset(client);
1016 return client_start(client, DHCP6_STATE_SOLICITATION);
1019 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1024 assert_return(client, -EINVAL);
1025 assert_return(!client->event, -EBUSY);
1028 client->event = sd_event_ref(event);
1030 r = sd_event_default(&client->event);
1035 client->event_priority = priority;
1040 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1041 assert_return(client, -EINVAL);
1043 client->event = sd_event_unref(client->event);
1048 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1052 return client->event;
1055 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1057 assert_se(REFCNT_INC(client->n_ref) >= 2);
1062 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1063 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1064 client_reset(client);
1066 sd_dhcp6_client_detach_event(client);
1068 free(client->req_opts);
1077 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1079 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1080 sd_id128_t machine_id;
1084 assert_return(ret, -EINVAL);
1086 client = new0(sd_dhcp6_client, 1);
1090 client->n_ref = REFCNT_INIT;
1092 client->ia_na.type = DHCP6_OPTION_IA_NA;
1098 /* initialize DUID */
1099 client->duid.type = htobe16(DHCP6_DUID_EN);
1100 client->duid.pen = htobe32(SYSTEMD_PEN);
1102 r = sd_id128_get_machine(&machine_id);
1106 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1108 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1111 client->req_opts_len = ELEMENTSOF(default_req_opts);
1113 client->req_opts = new0(be16_t, client->req_opts_len);
1114 if (!client->req_opts)
1117 for (t = 0; t < client->req_opts_len; t++)
1118 client->req_opts[t] = htobe16(default_req_opts[t]);