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_ia(&opt, &optlen, &client->ia_na);
261 case DHCP6_STATE_REQUEST:
262 case DHCP6_STATE_RENEW:
264 if (client->state == DHCP6_STATE_REQUEST)
265 message->type = DHCP6_REQUEST;
267 message->type = DHCP6_RENEW;
269 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
270 client->lease->serverid_len,
271 client->lease->serverid);
275 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
281 case DHCP6_STATE_REBIND:
282 message->type = DHCP6_REBIND;
284 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
290 case DHCP6_STATE_STOPPED:
292 case DHCP6_STATE_BOUND:
296 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
297 client->req_opts_len * sizeof(be16_t),
302 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
303 sizeof(client->duid), &client->duid);
307 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
312 log_dhcp6_client(client, "Sent %s",
313 dhcp6_message_type_to_string(message->type));
318 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
320 sd_dhcp6_client *client = userdata;
322 assert_return(s, -EINVAL);
323 assert_return(client, -EINVAL);
324 assert_return(client->lease, -EINVAL);
326 client->lease->ia.timeout_t2 =
327 sd_event_source_unref(client->lease->ia.timeout_t2);
329 log_dhcp6_client(client, "Timeout T2");
331 client_start(client, DHCP6_STATE_REBIND);
336 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
338 sd_dhcp6_client *client = userdata;
340 assert_return(s, -EINVAL);
341 assert_return(client, -EINVAL);
342 assert_return(client->lease, -EINVAL);
344 client->lease->ia.timeout_t1 =
345 sd_event_source_unref(client->lease->ia.timeout_t1);
347 log_dhcp6_client(client, "Timeout T1");
349 client_start(client, DHCP6_STATE_RENEW);
354 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
356 sd_dhcp6_client *client = userdata;
357 DHCP6_CLIENT_DONT_DESTROY(client);
358 enum DHCP6State state;
362 assert(client->event);
364 state = client->state;
366 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
368 /* RFC 3315, section 18.1.4., says that "...the client may choose to
369 use a Solicit message to locate a new DHCP server..." */
370 if (state == DHCP6_STATE_REBIND)
371 client_start(client, DHCP6_STATE_SOLICITATION);
376 static usec_t client_timeout_compute_random(usec_t val) {
377 return val - val / 10 +
378 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
381 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
384 sd_dhcp6_client *client = userdata;
385 usec_t time_now, init_retransmit_time, max_retransmit_time;
386 usec_t max_retransmit_duration;
387 uint8_t max_retransmit_count = 0;
388 char time_string[FORMAT_TIMESPAN_MAX];
393 assert(client->event);
395 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
397 switch (client->state) {
398 case DHCP6_STATE_SOLICITATION:
400 if (client->retransmit_count && client->lease) {
401 client_start(client, DHCP6_STATE_REQUEST);
405 init_retransmit_time = DHCP6_SOL_TIMEOUT;
406 max_retransmit_time = DHCP6_SOL_MAX_RT;
407 max_retransmit_count = 0;
408 max_retransmit_duration = 0;
412 case DHCP6_STATE_REQUEST:
413 init_retransmit_time = DHCP6_REQ_TIMEOUT;
414 max_retransmit_time = DHCP6_REQ_MAX_RT;
415 max_retransmit_count = DHCP6_REQ_MAX_RC;
416 max_retransmit_duration = 0;
420 case DHCP6_STATE_RENEW:
421 init_retransmit_time = DHCP6_REN_TIMEOUT;
422 max_retransmit_time = DHCP6_REN_MAX_RT;
423 max_retransmit_count = 0;
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 */
428 max_retransmit_duration = 0;
432 case DHCP6_STATE_REBIND:
433 init_retransmit_time = DHCP6_REB_TIMEOUT;
434 max_retransmit_time = DHCP6_REB_MAX_RT;
435 max_retransmit_count = 0;
437 max_retransmit_duration = 0;
439 if (!client->timeout_resend_expire) {
440 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
443 client_stop(client, r);
446 max_retransmit_duration = expire * USEC_PER_SEC;
451 case DHCP6_STATE_STOPPED:
453 case DHCP6_STATE_BOUND:
457 if (max_retransmit_count &&
458 client->retransmit_count >= max_retransmit_count) {
459 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
463 r = client_send_message(client);
465 client->retransmit_count++;
468 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
472 if (!client->retransmit_time) {
473 client->retransmit_time =
474 client_timeout_compute_random(init_retransmit_time);
476 if (client->state == DHCP6_STATE_SOLICITATION)
477 client->retransmit_time += init_retransmit_time / 10;
480 if (max_retransmit_time &&
481 client->retransmit_time > max_retransmit_time / 2)
482 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
484 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
487 log_dhcp6_client(client, "Next retransmission in %s",
488 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
489 client->retransmit_time, 0));
491 r = sd_event_add_time(client->event, &client->timeout_resend,
493 time_now + client->retransmit_time,
494 10 * USEC_PER_MSEC, client_timeout_resend,
499 r = sd_event_source_set_priority(client->timeout_resend,
500 client->event_priority);
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,
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);
526 client_stop(client, r);
531 static int client_ensure_iaid(sd_dhcp6_client *client) {
532 const char *name = NULL;
537 if (client->ia_na.id)
540 if (detect_container(NULL) <= 0) {
541 /* not in a container, udev will be around */
542 _cleanup_udev_unref_ struct udev *udev;
543 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
544 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
550 sprintf(ifindex_str, "n%d", client->index);
551 device = udev_device_new_from_device_id(udev, ifindex_str);
555 if (udev_device_get_is_initialized(device) <= 0)
559 name = net_get_name(device);
563 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
565 /* fall back to mac address if no predictable name available */
566 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
569 /* fold into 32 bits */
570 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
575 static int client_parse_message(sd_dhcp6_client *client,
576 DHCP6Message *message, size_t len,
577 sd_dhcp6_lease *lease) {
579 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
580 uint16_t optcode, status;
581 size_t optlen, id_len;
582 bool clientid = false;
585 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
588 case DHCP6_OPTION_CLIENTID:
590 log_dhcp6_client(client, "%s contains multiple clientids",
591 dhcp6_message_type_to_string(message->type));
595 if (optlen != sizeof(client->duid) ||
596 memcmp(&client->duid, optval, optlen) != 0) {
597 log_dhcp6_client(client, "%s DUID does not match",
598 dhcp6_message_type_to_string(message->type));
606 case DHCP6_OPTION_SERVERID:
607 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
609 log_dhcp6_client(client, "%s contains multiple serverids",
610 dhcp6_message_type_to_string(message->type));
614 r = dhcp6_lease_set_serverid(lease, optval, optlen);
620 case DHCP6_OPTION_PREFERENCE:
624 r = dhcp6_lease_set_preference(lease, *optval);
630 case DHCP6_OPTION_STATUS_CODE:
634 status = optval[0] << 8 | optval[1];
636 log_dhcp6_client(client, "%s Status %s",
637 dhcp6_message_type_to_string(message->type),
638 dhcp6_message_status_to_string(status));
644 case DHCP6_OPTION_IA_NA:
645 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
647 if (r < 0 && r != -ENOMSG)
650 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
654 if (client->ia_na.id != iaid_lease) {
655 log_dhcp6_client(client, "%s has wrong IAID",
656 dhcp6_message_type_to_string(message->type));
664 if ((r < 0 && r != -ENOMSG) || !clientid) {
665 log_dhcp6_client(client, "%s has incomplete options",
666 dhcp6_message_type_to_string(message->type));
670 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
672 log_dhcp6_client(client, "%s has no server id",
673 dhcp6_message_type_to_string(message->type));
678 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
682 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
684 if (reply->type != DHCP6_REPLY)
687 r = dhcp6_lease_new(&lease);
691 r = client_parse_message(client, reply, len, lease);
695 dhcp6_lease_clear_timers(&client->lease->ia);
697 client->lease = sd_dhcp6_lease_unref(client->lease);
698 client->lease = lease;
701 return DHCP6_STATE_BOUND;
704 static int client_receive_advertise(sd_dhcp6_client *client,
705 DHCP6Message *advertise, size_t len) {
707 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
708 uint8_t pref_advertise = 0, pref_lease = 0;
710 if (advertise->type != DHCP6_ADVERTISE)
713 r = dhcp6_lease_new(&lease);
717 r = client_parse_message(client, advertise, len, lease);
721 r = dhcp6_lease_get_preference(lease, &pref_advertise);
725 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
726 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
727 sd_dhcp6_lease_unref(client->lease);
728 client->lease = lease;
733 if (pref_advertise == 255 || client->retransmit_count > 1)
734 r = DHCP6_STATE_REQUEST;
739 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
741 sd_dhcp6_client *client = userdata;
742 DHCP6_CLIENT_DONT_DESTROY(client);
743 _cleanup_free_ DHCP6Message *message;
748 assert(client->event);
750 r = ioctl(fd, FIONREAD, &buflen);
751 if (r < 0 || buflen <= 0)
752 buflen = DHCP6_MIN_OPTIONS_SIZE;
754 message = malloc0(buflen);
758 len = read(fd, message, buflen);
759 if ((size_t)len < sizeof(DHCP6Message)) {
760 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
764 switch(message->type) {
772 case DHCP6_INFORMATION_REQUEST:
773 case DHCP6_RELAY_FORW:
774 case DHCP6_RELAY_REPL:
777 case DHCP6_ADVERTISE:
779 case DHCP6_RECONFIGURE:
783 log_dhcp6_client(client, "unknown message type %d",
788 if (client->transaction_id != (message->transaction_id &
789 htobe32(0x00ffffff)))
792 switch (client->state) {
793 case DHCP6_STATE_SOLICITATION:
794 r = client_receive_advertise(client, message, len);
796 if (r == DHCP6_STATE_REQUEST)
797 client_start(client, r);
801 case DHCP6_STATE_REQUEST:
802 case DHCP6_STATE_RENEW:
803 case DHCP6_STATE_REBIND:
805 r = client_receive_reply(client, message, len);
809 if (r == DHCP6_STATE_BOUND) {
811 r = client_start(client, DHCP6_STATE_BOUND);
813 client_stop(client, r);
817 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
822 case DHCP6_STATE_BOUND:
826 case DHCP6_STATE_STOPPED:
832 log_dhcp6_client(client, "Recv %s",
833 dhcp6_message_type_to_string(message->type));
839 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
842 usec_t timeout, time_now;
843 char time_string[FORMAT_TIMESPAN_MAX];
845 assert_return(client, -EINVAL);
846 assert_return(client->event, -EINVAL);
847 assert_return(client->index > 0, -EINVAL);
848 assert_return(client->state != state, -EINVAL);
850 client->timeout_resend_expire =
851 sd_event_source_unref(client->timeout_resend_expire);
852 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
853 client->retransmit_time = 0;
854 client->retransmit_count = 0;
857 case DHCP6_STATE_STOPPED:
859 case DHCP6_STATE_SOLICITATION:
861 r = client_ensure_iaid(client);
865 r = dhcp6_network_bind_udp_socket(client->index, NULL);
871 r = sd_event_add_io(client->event, &client->receive_message,
872 client->fd, EPOLLIN, client_receive_message,
877 r = sd_event_source_set_priority(client->receive_message,
878 client->event_priority);
882 client->state = DHCP6_STATE_SOLICITATION;
886 case DHCP6_STATE_REQUEST:
887 case DHCP6_STATE_RENEW:
888 case DHCP6_STATE_REBIND:
890 client->state = state;
894 case DHCP6_STATE_BOUND:
896 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
900 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
901 client->lease->ia.lifetime_t2 == 0xffffffff) {
903 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
904 be32toh(client->lease->ia.lifetime_t1),
905 be32toh(client->lease->ia.lifetime_t2));
910 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
912 log_dhcp6_client(client, "T1 expires in %s",
913 format_timespan(time_string,
917 r = sd_event_add_time(client->event,
918 &client->lease->ia.timeout_t1,
919 CLOCK_MONOTONIC, time_now + timeout,
920 10 * USEC_PER_SEC, client_timeout_t1,
925 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
926 client->event_priority);
930 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
932 log_dhcp6_client(client, "T2 expires in %s",
933 format_timespan(time_string,
937 r = sd_event_add_time(client->event,
938 &client->lease->ia.timeout_t2,
939 CLOCK_MONOTONIC, time_now + timeout,
940 10 * USEC_PER_SEC, client_timeout_t2,
945 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
946 client->event_priority);
950 client->state = state;
955 client->transaction_id = random_u32() & htobe32(0x00ffffff);
957 r = sd_event_add_time(client->event, &client->timeout_resend,
958 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
963 r = sd_event_source_set_priority(client->timeout_resend,
964 client->event_priority);
971 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
973 client_stop(client, DHCP6_EVENT_STOP);
978 int sd_dhcp6_client_start(sd_dhcp6_client *client)
982 assert_return(client, -EINVAL);
983 assert_return(client->event, -EINVAL);
984 assert_return(client->index > 0, -EINVAL);
986 r = client_reset(client);
990 return client_start(client, DHCP6_STATE_SOLICITATION);
993 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
998 assert_return(client, -EINVAL);
999 assert_return(!client->event, -EBUSY);
1002 client->event = sd_event_ref(event);
1004 r = sd_event_default(&client->event);
1009 client->event_priority = priority;
1014 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1015 assert_return(client, -EINVAL);
1017 client->event = sd_event_unref(client->event);
1022 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1026 return client->event;
1029 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1031 assert_se(REFCNT_INC(client->n_ref) >= 2);
1036 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1037 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1038 client_reset(client);
1040 sd_dhcp6_client_detach_event(client);
1042 free(client->req_opts);
1051 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1053 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1054 sd_id128_t machine_id;
1058 assert_return(ret, -EINVAL);
1060 client = new0(sd_dhcp6_client, 1);
1064 client->n_ref = REFCNT_INIT;
1066 client->ia_na.type = DHCP6_OPTION_IA_NA;
1072 /* initialize DUID */
1073 client->duid.type = htobe16(DHCP6_DUID_EN);
1074 client->duid.pen = htobe32(SYSTEMD_PEN);
1076 r = sd_id128_get_machine(&machine_id);
1080 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1082 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1085 client->req_opts_len = ELEMENTSOF(default_req_opts);
1087 client->req_opts = new0(be16_t, client->req_opts_len);
1088 if (!client->req_opts)
1091 for (t = 0; t < client->req_opts_len; t++)
1092 client->req_opts[t] = htobe16(default_req_opts[t]);