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);
258 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
264 case DHCP6_STATE_REQUEST:
265 case DHCP6_STATE_RENEW:
267 if (client->state == DHCP6_STATE_REQUEST)
268 message->type = DHCP6_REQUEST;
270 message->type = DHCP6_RENEW;
272 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
273 client->lease->serverid_len,
274 client->lease->serverid);
278 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
284 case DHCP6_STATE_REBIND:
285 message->type = DHCP6_REBIND;
287 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
293 case DHCP6_STATE_STOPPED:
295 case DHCP6_STATE_BOUND:
299 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
300 client->req_opts_len * sizeof(be16_t),
305 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
306 sizeof(client->duid), &client->duid);
310 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
315 log_dhcp6_client(client, "Sent %s",
316 dhcp6_message_type_to_string(message->type));
321 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
323 sd_dhcp6_client *client = userdata;
325 assert_return(s, -EINVAL);
326 assert_return(client, -EINVAL);
327 assert_return(client->lease, -EINVAL);
329 client->lease->ia.timeout_t2 =
330 sd_event_source_unref(client->lease->ia.timeout_t2);
332 log_dhcp6_client(client, "Timeout T2");
334 client_start(client, DHCP6_STATE_REBIND);
339 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
341 sd_dhcp6_client *client = userdata;
343 assert_return(s, -EINVAL);
344 assert_return(client, -EINVAL);
345 assert_return(client->lease, -EINVAL);
347 client->lease->ia.timeout_t1 =
348 sd_event_source_unref(client->lease->ia.timeout_t1);
350 log_dhcp6_client(client, "Timeout T1");
352 client_start(client, DHCP6_STATE_RENEW);
357 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
359 sd_dhcp6_client *client = userdata;
360 DHCP6_CLIENT_DONT_DESTROY(client);
361 enum DHCP6State state;
365 assert(client->event);
367 state = client->state;
369 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
371 /* RFC 3315, section 18.1.4., says that "...the client may choose to
372 use a Solicit message to locate a new DHCP server..." */
373 if (state == DHCP6_STATE_REBIND)
374 client_start(client, DHCP6_STATE_SOLICITATION);
379 static usec_t client_timeout_compute_random(usec_t val) {
380 return val - val / 10 +
381 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
384 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
387 sd_dhcp6_client *client = userdata;
388 usec_t time_now, init_retransmit_time, max_retransmit_time;
389 usec_t max_retransmit_duration;
390 uint8_t max_retransmit_count = 0;
391 char time_string[FORMAT_TIMESPAN_MAX];
396 assert(client->event);
398 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
400 switch (client->state) {
401 case DHCP6_STATE_SOLICITATION:
403 if (client->retransmit_count && client->lease) {
404 client_start(client, DHCP6_STATE_REQUEST);
408 init_retransmit_time = DHCP6_SOL_TIMEOUT;
409 max_retransmit_time = DHCP6_SOL_MAX_RT;
410 max_retransmit_count = 0;
411 max_retransmit_duration = 0;
415 case DHCP6_STATE_REQUEST:
416 init_retransmit_time = DHCP6_REQ_TIMEOUT;
417 max_retransmit_time = DHCP6_REQ_MAX_RT;
418 max_retransmit_count = DHCP6_REQ_MAX_RC;
419 max_retransmit_duration = 0;
423 case DHCP6_STATE_RENEW:
424 init_retransmit_time = DHCP6_REN_TIMEOUT;
425 max_retransmit_time = DHCP6_REN_MAX_RT;
426 max_retransmit_count = 0;
428 /* RFC 3315, section 18.1.3. says max retransmit duration will
429 be the remaining time until T2. Instead of setting MRD,
430 wait for T2 to trigger with the same end result */
431 max_retransmit_duration = 0;
435 case DHCP6_STATE_REBIND:
436 init_retransmit_time = DHCP6_REB_TIMEOUT;
437 max_retransmit_time = DHCP6_REB_MAX_RT;
438 max_retransmit_count = 0;
440 max_retransmit_duration = 0;
442 if (!client->timeout_resend_expire) {
443 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
446 client_stop(client, r);
449 max_retransmit_duration = expire * USEC_PER_SEC;
454 case DHCP6_STATE_STOPPED:
456 case DHCP6_STATE_BOUND:
460 if (max_retransmit_count &&
461 client->retransmit_count >= max_retransmit_count) {
462 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
466 r = client_send_message(client);
468 client->retransmit_count++;
471 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
475 if (!client->retransmit_time) {
476 client->retransmit_time =
477 client_timeout_compute_random(init_retransmit_time);
479 if (client->state == DHCP6_STATE_SOLICITATION)
480 client->retransmit_time += init_retransmit_time / 10;
483 if (max_retransmit_time &&
484 client->retransmit_time > max_retransmit_time / 2)
485 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
487 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
490 log_dhcp6_client(client, "Next retransmission in %s",
491 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
492 client->retransmit_time, 0));
494 r = sd_event_add_time(client->event, &client->timeout_resend,
496 time_now + client->retransmit_time,
497 10 * USEC_PER_MSEC, client_timeout_resend,
502 r = sd_event_source_set_priority(client->timeout_resend,
503 client->event_priority);
507 if (max_retransmit_duration && !client->timeout_resend_expire) {
509 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
510 max_retransmit_duration / USEC_PER_SEC);
512 r = sd_event_add_time(client->event,
513 &client->timeout_resend_expire,
515 time_now + max_retransmit_duration,
517 client_timeout_resend_expire, client);
521 r = sd_event_source_set_priority(client->timeout_resend_expire,
522 client->event_priority);
529 client_stop(client, r);
534 static int client_ensure_iaid(sd_dhcp6_client *client) {
535 /* name is a pointer to memory in the udev_device struct, so must
536 have the same scope */
537 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
538 const char *name = NULL;
543 if (client->ia_na.id)
546 if (detect_container(NULL) <= 0) {
547 /* not in a container, udev will be around */
548 _cleanup_udev_unref_ struct udev *udev;
549 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
555 sprintf(ifindex_str, "n%d", client->index);
556 device = udev_device_new_from_device_id(udev, ifindex_str);
560 if (udev_device_get_is_initialized(device) <= 0)
564 name = net_get_name(device);
568 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
570 /* fall back to mac address if no predictable name available */
571 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
574 /* fold into 32 bits */
575 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
580 static int client_parse_message(sd_dhcp6_client *client,
581 DHCP6Message *message, size_t len,
582 sd_dhcp6_lease *lease) {
584 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
585 uint16_t optcode, status;
586 size_t optlen, id_len;
587 bool clientid = false;
590 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
593 case DHCP6_OPTION_CLIENTID:
595 log_dhcp6_client(client, "%s contains multiple clientids",
596 dhcp6_message_type_to_string(message->type));
600 if (optlen != sizeof(client->duid) ||
601 memcmp(&client->duid, optval, optlen) != 0) {
602 log_dhcp6_client(client, "%s DUID does not match",
603 dhcp6_message_type_to_string(message->type));
611 case DHCP6_OPTION_SERVERID:
612 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
614 log_dhcp6_client(client, "%s contains multiple serverids",
615 dhcp6_message_type_to_string(message->type));
619 r = dhcp6_lease_set_serverid(lease, optval, optlen);
625 case DHCP6_OPTION_PREFERENCE:
629 r = dhcp6_lease_set_preference(lease, *optval);
635 case DHCP6_OPTION_STATUS_CODE:
639 status = optval[0] << 8 | optval[1];
641 log_dhcp6_client(client, "%s Status %s",
642 dhcp6_message_type_to_string(message->type),
643 dhcp6_message_status_to_string(status));
649 case DHCP6_OPTION_IA_NA:
650 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
652 if (r < 0 && r != -ENOMSG)
655 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
659 if (client->ia_na.id != iaid_lease) {
660 log_dhcp6_client(client, "%s has wrong IAID",
661 dhcp6_message_type_to_string(message->type));
667 case DHCP6_OPTION_RAPID_COMMIT:
668 r = dhcp6_lease_set_rapid_commit(lease);
676 if ((r < 0 && r != -ENOMSG) || !clientid) {
677 log_dhcp6_client(client, "%s has incomplete options",
678 dhcp6_message_type_to_string(message->type));
682 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
684 log_dhcp6_client(client, "%s has no server id",
685 dhcp6_message_type_to_string(message->type));
690 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
694 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
697 if (reply->type != DHCP6_REPLY)
700 r = dhcp6_lease_new(&lease);
704 r = client_parse_message(client, reply, len, lease);
708 if (client->state == DHCP6_STATE_SOLICITATION) {
709 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
717 dhcp6_lease_clear_timers(&client->lease->ia);
719 client->lease = sd_dhcp6_lease_unref(client->lease);
720 client->lease = lease;
723 return DHCP6_STATE_BOUND;
726 static int client_receive_advertise(sd_dhcp6_client *client,
727 DHCP6Message *advertise, size_t len) {
729 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
730 uint8_t pref_advertise = 0, pref_lease = 0;
732 if (advertise->type != DHCP6_ADVERTISE)
735 r = dhcp6_lease_new(&lease);
739 r = client_parse_message(client, advertise, len, lease);
743 r = dhcp6_lease_get_preference(lease, &pref_advertise);
747 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
748 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
749 sd_dhcp6_lease_unref(client->lease);
750 client->lease = lease;
755 if (pref_advertise == 255 || client->retransmit_count > 1)
756 r = DHCP6_STATE_REQUEST;
761 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
763 sd_dhcp6_client *client = userdata;
764 DHCP6_CLIENT_DONT_DESTROY(client);
765 _cleanup_free_ DHCP6Message *message;
770 assert(client->event);
772 r = ioctl(fd, FIONREAD, &buflen);
773 if (r < 0 || buflen <= 0)
774 buflen = DHCP6_MIN_OPTIONS_SIZE;
776 message = malloc0(buflen);
780 len = read(fd, message, buflen);
781 if ((size_t)len < sizeof(DHCP6Message)) {
782 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
786 switch(message->type) {
794 case DHCP6_INFORMATION_REQUEST:
795 case DHCP6_RELAY_FORW:
796 case DHCP6_RELAY_REPL:
799 case DHCP6_ADVERTISE:
801 case DHCP6_RECONFIGURE:
805 log_dhcp6_client(client, "unknown message type %d",
810 if (client->transaction_id != (message->transaction_id &
811 htobe32(0x00ffffff)))
814 switch (client->state) {
815 case DHCP6_STATE_SOLICITATION:
816 r = client_receive_advertise(client, message, len);
818 if (r == DHCP6_STATE_REQUEST) {
819 client_start(client, r);
824 /* fall through for Soliciation Rapid Commit option check */
825 case DHCP6_STATE_REQUEST:
826 case DHCP6_STATE_RENEW:
827 case DHCP6_STATE_REBIND:
829 r = client_receive_reply(client, message, len);
833 if (r == DHCP6_STATE_BOUND) {
835 r = client_start(client, DHCP6_STATE_BOUND);
837 client_stop(client, r);
841 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
846 case DHCP6_STATE_BOUND:
850 case DHCP6_STATE_STOPPED:
856 log_dhcp6_client(client, "Recv %s",
857 dhcp6_message_type_to_string(message->type));
863 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
866 usec_t timeout, time_now;
867 char time_string[FORMAT_TIMESPAN_MAX];
869 assert_return(client, -EINVAL);
870 assert_return(client->event, -EINVAL);
871 assert_return(client->index > 0, -EINVAL);
872 assert_return(client->state != state, -EINVAL);
874 client->timeout_resend_expire =
875 sd_event_source_unref(client->timeout_resend_expire);
876 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
877 client->retransmit_time = 0;
878 client->retransmit_count = 0;
881 case DHCP6_STATE_STOPPED:
883 case DHCP6_STATE_SOLICITATION:
885 r = client_ensure_iaid(client);
889 r = dhcp6_network_bind_udp_socket(client->index, NULL);
895 r = sd_event_add_io(client->event, &client->receive_message,
896 client->fd, EPOLLIN, client_receive_message,
901 r = sd_event_source_set_priority(client->receive_message,
902 client->event_priority);
906 client->state = DHCP6_STATE_SOLICITATION;
910 case DHCP6_STATE_REQUEST:
911 case DHCP6_STATE_RENEW:
912 case DHCP6_STATE_REBIND:
914 client->state = state;
918 case DHCP6_STATE_BOUND:
920 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
924 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
925 client->lease->ia.lifetime_t2 == 0xffffffff) {
927 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
928 be32toh(client->lease->ia.lifetime_t1),
929 be32toh(client->lease->ia.lifetime_t2));
934 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
936 log_dhcp6_client(client, "T1 expires in %s",
937 format_timespan(time_string,
941 r = sd_event_add_time(client->event,
942 &client->lease->ia.timeout_t1,
943 CLOCK_MONOTONIC, time_now + timeout,
944 10 * USEC_PER_SEC, client_timeout_t1,
949 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
950 client->event_priority);
954 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
956 log_dhcp6_client(client, "T2 expires in %s",
957 format_timespan(time_string,
961 r = sd_event_add_time(client->event,
962 &client->lease->ia.timeout_t2,
963 CLOCK_MONOTONIC, time_now + timeout,
964 10 * USEC_PER_SEC, client_timeout_t2,
969 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
970 client->event_priority);
974 client->state = state;
979 client->transaction_id = random_u32() & htobe32(0x00ffffff);
981 r = sd_event_add_time(client->event, &client->timeout_resend,
982 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
987 r = sd_event_source_set_priority(client->timeout_resend,
988 client->event_priority);
995 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
997 client_stop(client, DHCP6_EVENT_STOP);
1002 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1006 assert_return(client, -EINVAL);
1007 assert_return(client->event, -EINVAL);
1008 assert_return(client->index > 0, -EINVAL);
1010 r = client_reset(client);
1014 return client_start(client, DHCP6_STATE_SOLICITATION);
1017 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1022 assert_return(client, -EINVAL);
1023 assert_return(!client->event, -EBUSY);
1026 client->event = sd_event_ref(event);
1028 r = sd_event_default(&client->event);
1033 client->event_priority = priority;
1038 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1039 assert_return(client, -EINVAL);
1041 client->event = sd_event_unref(client->event);
1046 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1050 return client->event;
1053 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1055 assert_se(REFCNT_INC(client->n_ref) >= 2);
1060 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1061 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1062 client_reset(client);
1064 sd_dhcp6_client_detach_event(client);
1066 free(client->req_opts);
1075 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1077 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1078 sd_id128_t machine_id;
1082 assert_return(ret, -EINVAL);
1084 client = new0(sd_dhcp6_client, 1);
1088 client->n_ref = REFCNT_INIT;
1090 client->ia_na.type = DHCP6_OPTION_IA_NA;
1096 /* initialize DUID */
1097 client->duid.type = htobe16(DHCP6_DUID_EN);
1098 client->duid.pen = htobe32(SYSTEMD_PEN);
1100 r = sd_id128_get_machine(&machine_id);
1104 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1106 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1109 client->req_opts_len = ELEMENTSOF(default_req_opts);
1111 client->req_opts = new0(be16_t, client->req_opts_len);
1112 if (!client->req_opts)
1115 for (t = 0; t < client->req_opts_len; t++)
1116 client->req_opts[t] = htobe16(default_req_opts[t]);