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 /* RFC 3315 section 9.1:
43 * A DUID can be no more than 128 octets long (not including the type code).
45 #define MAX_DUID_LEN 128
47 struct sd_dhcp6_client {
50 enum DHCP6State state;
54 struct ether_addr mac_addr;
56 be32_t transaction_id;
57 usec_t transaction_start;
58 struct sd_dhcp6_lease *lease;
61 size_t req_opts_allocated;
63 sd_event_source *receive_message;
64 usec_t retransmit_time;
65 uint8_t retransmit_count;
66 sd_event_source *timeout_resend;
67 sd_event_source *timeout_resend_expire;
68 sd_dhcp6_client_cb_t cb;
72 uint16_t type; /* DHCP6_DUID_LLT */
78 uint16_t type; /* DHCP6_DUID_EN */
83 uint16_t type; /* DHCP6_DUID_LL */
88 uint16_t type; /* DHCP6_DUID_UUID */
93 uint8_t data[MAX_DUID_LEN];
99 static const uint16_t default_req_opts[] = {
100 DHCP6_OPTION_DNS_SERVERS,
101 DHCP6_OPTION_DOMAIN_LIST,
102 DHCP6_OPTION_NTP_SERVER,
105 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
106 [DHCP6_SOLICIT] = "SOLICIT",
107 [DHCP6_ADVERTISE] = "ADVERTISE",
108 [DHCP6_REQUEST] = "REQUEST",
109 [DHCP6_CONFIRM] = "CONFIRM",
110 [DHCP6_RENEW] = "RENEW",
111 [DHCP6_REBIND] = "REBIND",
112 [DHCP6_REPLY] = "REPLY",
113 [DHCP6_RELEASE] = "RELEASE",
114 [DHCP6_DECLINE] = "DECLINE",
115 [DHCP6_RECONFIGURE] = "RECONFIGURE",
116 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
117 [DHCP6_RELAY_FORW] = "RELAY-FORW",
118 [DHCP6_RELAY_REPL] = "RELAY-REPL",
121 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
123 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
124 [DHCP6_STATUS_SUCCESS] = "Success",
125 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
126 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
127 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
128 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
129 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
132 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
134 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
135 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
137 #define DHCP6_CLIENT_DONT_DESTROY(client) \
138 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
140 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
142 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
143 sd_dhcp6_client_cb_t cb, void *userdata)
145 assert_return(client, -EINVAL);
148 client->userdata = userdata;
153 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
155 assert_return(client, -EINVAL);
156 assert_return(interface_index >= -1, -EINVAL);
158 client->index = interface_index;
163 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
164 const struct ether_addr *mac_addr)
166 assert_return(client, -EINVAL);
169 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
171 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
176 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
179 assert_return(client, -EINVAL);
180 assert_return(duid, -EINVAL);
181 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
185 if (duid_len <= sizeof(client->duid.llt))
189 if (duid_len != sizeof(client->duid.en))
193 if (duid_len <= sizeof(client->duid.ll))
196 case DHCP6_DUID_UUID:
197 if (duid_len != sizeof(client->duid.uuid))
201 /* accept unknown type in order to be forward compatible */
205 client->duid.raw.type = htobe16(type);
206 memcpy(&client->duid.raw.data, duid, duid_len);
207 client->duid_len = duid_len;
212 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
216 assert_return(client, -EINVAL);
217 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
220 case DHCP6_OPTION_DNS_SERVERS:
221 case DHCP6_OPTION_DOMAIN_LIST:
222 case DHCP6_OPTION_SNTP_SERVERS:
223 case DHCP6_OPTION_NTP_SERVER:
230 for (t = 0; t < client->req_opts_len; t++)
231 if (client->req_opts[t] == htobe16(option))
234 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
235 client->req_opts_len + 1))
238 client->req_opts[client->req_opts_len++] = htobe16(option);
243 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
244 assert_return(client, -EINVAL);
245 assert_return(ret, -EINVAL);
250 *ret = sd_dhcp6_lease_ref(client->lease);
255 static void client_notify(sd_dhcp6_client *client, int event) {
257 client->cb(client, event, client->userdata);
260 static int client_reset(sd_dhcp6_client *client) {
261 assert_return(client, -EINVAL);
263 client->receive_message =
264 sd_event_source_unref(client->receive_message);
266 client->fd = safe_close(client->fd);
268 client->transaction_id = 0;
269 client->transaction_start = 0;
271 client->ia_na.timeout_t1 =
272 sd_event_source_unref(client->ia_na.timeout_t1);
273 client->ia_na.timeout_t2 =
274 sd_event_source_unref(client->ia_na.timeout_t2);
276 client->retransmit_time = 0;
277 client->retransmit_count = 0;
278 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
279 client->timeout_resend_expire =
280 sd_event_source_unref(client->timeout_resend_expire);
282 client->state = DHCP6_STATE_STOPPED;
287 static void client_stop(sd_dhcp6_client *client, int error) {
288 DHCP6_CLIENT_DONT_DESTROY(client);
292 client_notify(client, error);
294 client_reset(client);
297 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
298 _cleanup_free_ DHCP6Message *message = NULL;
299 struct in6_addr all_servers =
300 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
301 size_t len, optlen = 512;
307 len = sizeof(DHCP6Message) + optlen;
309 message = malloc0(len);
313 opt = (uint8_t *)(message + 1);
315 message->transaction_id = client->transaction_id;
317 switch(client->state) {
318 case DHCP6_STATE_SOLICITATION:
319 message->type = DHCP6_SOLICIT;
321 r = dhcp6_option_append(&opt, &optlen,
322 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
326 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
332 case DHCP6_STATE_REQUEST:
333 case DHCP6_STATE_RENEW:
335 if (client->state == DHCP6_STATE_REQUEST)
336 message->type = DHCP6_REQUEST;
338 message->type = DHCP6_RENEW;
340 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
341 client->lease->serverid_len,
342 client->lease->serverid);
346 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
352 case DHCP6_STATE_REBIND:
353 message->type = DHCP6_REBIND;
355 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
361 case DHCP6_STATE_STOPPED:
362 case DHCP6_STATE_BOUND:
366 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
367 client->req_opts_len * sizeof(be16_t),
372 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
373 client->duid_len, &client->duid);
377 elapsed_usec = time_now - client->transaction_start;
378 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
379 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
381 elapsed_time = 0xffff;
383 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
384 sizeof(elapsed_time), &elapsed_time);
388 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
393 log_dhcp6_client(client, "Sent %s",
394 dhcp6_message_type_to_string(message->type));
399 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
401 sd_dhcp6_client *client = userdata;
403 assert_return(s, -EINVAL);
404 assert_return(client, -EINVAL);
405 assert_return(client->lease, -EINVAL);
407 client->lease->ia.timeout_t2 =
408 sd_event_source_unref(client->lease->ia.timeout_t2);
410 log_dhcp6_client(client, "Timeout T2");
412 client_start(client, DHCP6_STATE_REBIND);
417 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
419 sd_dhcp6_client *client = userdata;
421 assert_return(s, -EINVAL);
422 assert_return(client, -EINVAL);
423 assert_return(client->lease, -EINVAL);
425 client->lease->ia.timeout_t1 =
426 sd_event_source_unref(client->lease->ia.timeout_t1);
428 log_dhcp6_client(client, "Timeout T1");
430 client_start(client, DHCP6_STATE_RENEW);
435 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
437 sd_dhcp6_client *client = userdata;
438 DHCP6_CLIENT_DONT_DESTROY(client);
439 enum DHCP6State state;
443 assert(client->event);
445 state = client->state;
447 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
449 /* RFC 3315, section 18.1.4., says that "...the client may choose to
450 use a Solicit message to locate a new DHCP server..." */
451 if (state == DHCP6_STATE_REBIND)
452 client_start(client, DHCP6_STATE_SOLICITATION);
457 static usec_t client_timeout_compute_random(usec_t val) {
458 return val - val / 10 +
459 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
462 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
465 sd_dhcp6_client *client = userdata;
466 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
467 usec_t max_retransmit_duration = 0;
468 uint8_t max_retransmit_count = 0;
469 char time_string[FORMAT_TIMESPAN_MAX];
474 assert(client->event);
476 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
478 switch (client->state) {
479 case DHCP6_STATE_SOLICITATION:
481 if (client->retransmit_count && client->lease) {
482 client_start(client, DHCP6_STATE_REQUEST);
486 init_retransmit_time = DHCP6_SOL_TIMEOUT;
487 max_retransmit_time = DHCP6_SOL_MAX_RT;
491 case DHCP6_STATE_REQUEST:
492 init_retransmit_time = DHCP6_REQ_TIMEOUT;
493 max_retransmit_time = DHCP6_REQ_MAX_RT;
494 max_retransmit_count = DHCP6_REQ_MAX_RC;
498 case DHCP6_STATE_RENEW:
499 init_retransmit_time = DHCP6_REN_TIMEOUT;
500 max_retransmit_time = DHCP6_REN_MAX_RT;
502 /* RFC 3315, section 18.1.3. says max retransmit duration will
503 be the remaining time until T2. Instead of setting MRD,
504 wait for T2 to trigger with the same end result */
508 case DHCP6_STATE_REBIND:
509 init_retransmit_time = DHCP6_REB_TIMEOUT;
510 max_retransmit_time = DHCP6_REB_MAX_RT;
512 if (!client->timeout_resend_expire) {
513 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
516 client_stop(client, r);
519 max_retransmit_duration = expire * USEC_PER_SEC;
524 case DHCP6_STATE_STOPPED:
525 case DHCP6_STATE_BOUND:
529 if (max_retransmit_count &&
530 client->retransmit_count >= max_retransmit_count) {
531 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
535 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
539 r = client_send_message(client, time_now);
541 client->retransmit_count++;
543 if (!client->retransmit_time) {
544 client->retransmit_time =
545 client_timeout_compute_random(init_retransmit_time);
547 if (client->state == DHCP6_STATE_SOLICITATION)
548 client->retransmit_time += init_retransmit_time / 10;
551 if (max_retransmit_time &&
552 client->retransmit_time > max_retransmit_time / 2)
553 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
555 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
558 log_dhcp6_client(client, "Next retransmission in %s",
559 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
560 client->retransmit_time, 0));
562 r = sd_event_add_time(client->event, &client->timeout_resend,
563 clock_boottime_or_monotonic(),
564 time_now + client->retransmit_time,
565 10 * USEC_PER_MSEC, client_timeout_resend,
570 r = sd_event_source_set_priority(client->timeout_resend,
571 client->event_priority);
575 r = sd_event_source_set_name(client->timeout_resend,
576 "dhcp6-resend-timer");
580 if (max_retransmit_duration && !client->timeout_resend_expire) {
582 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
583 max_retransmit_duration / USEC_PER_SEC);
585 r = sd_event_add_time(client->event,
586 &client->timeout_resend_expire,
587 clock_boottime_or_monotonic(),
588 time_now + max_retransmit_duration,
590 client_timeout_resend_expire, client);
594 r = sd_event_source_set_priority(client->timeout_resend_expire,
595 client->event_priority);
599 r = sd_event_source_set_name(client->timeout_resend_expire,
600 "dhcp6-resend-expire-timer");
607 client_stop(client, r);
612 static int client_ensure_iaid(sd_dhcp6_client *client) {
613 /* name is a pointer to memory in the udev_device struct, so must
614 have the same scope */
615 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
616 const char *name = NULL;
621 if (client->ia_na.id)
624 if (detect_container(NULL) <= 0) {
625 /* not in a container, udev will be around */
626 _cleanup_udev_unref_ struct udev *udev;
627 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
633 sprintf(ifindex_str, "n%d", client->index);
634 device = udev_device_new_from_device_id(udev, ifindex_str);
638 if (udev_device_get_is_initialized(device) <= 0)
642 name = net_get_name(device);
646 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
648 /* fall back to mac address if no predictable name available */
649 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
652 /* fold into 32 bits */
653 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
658 static int client_parse_message(sd_dhcp6_client *client,
659 DHCP6Message *message, size_t len,
660 sd_dhcp6_lease *lease) {
662 uint8_t *optval, *option, *id = NULL;
663 uint16_t optcode, status;
664 size_t optlen, id_len;
665 bool clientid = false;
668 option = (uint8_t *)message + sizeof(DHCP6Message);
669 len -= sizeof(DHCP6Message);
671 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
674 case DHCP6_OPTION_CLIENTID:
676 log_dhcp6_client(client, "%s contains multiple clientids",
677 dhcp6_message_type_to_string(message->type));
681 if (optlen != client->duid_len ||
682 memcmp(&client->duid, optval, optlen) != 0) {
683 log_dhcp6_client(client, "%s DUID does not match",
684 dhcp6_message_type_to_string(message->type));
692 case DHCP6_OPTION_SERVERID:
693 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
695 log_dhcp6_client(client, "%s contains multiple serverids",
696 dhcp6_message_type_to_string(message->type));
700 r = dhcp6_lease_set_serverid(lease, optval, optlen);
706 case DHCP6_OPTION_PREFERENCE:
710 r = dhcp6_lease_set_preference(lease, *optval);
716 case DHCP6_OPTION_STATUS_CODE:
720 status = optval[0] << 8 | optval[1];
722 log_dhcp6_client(client, "%s Status %s",
723 dhcp6_message_type_to_string(message->type),
724 dhcp6_message_status_to_string(status));
730 case DHCP6_OPTION_IA_NA:
731 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
733 if (r < 0 && r != -ENOMSG)
736 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
740 if (client->ia_na.id != iaid_lease) {
741 log_dhcp6_client(client, "%s has wrong IAID",
742 dhcp6_message_type_to_string(message->type));
748 case DHCP6_OPTION_RAPID_COMMIT:
749 r = dhcp6_lease_set_rapid_commit(lease);
757 if ((r < 0 && r != -ENOMSG) || !clientid) {
758 log_dhcp6_client(client, "%s has incomplete options",
759 dhcp6_message_type_to_string(message->type));
763 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
765 log_dhcp6_client(client, "%s has no server id",
766 dhcp6_message_type_to_string(message->type));
771 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
775 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
778 if (reply->type != DHCP6_REPLY)
781 r = dhcp6_lease_new(&lease);
785 r = client_parse_message(client, reply, len, lease);
789 if (client->state == DHCP6_STATE_SOLICITATION) {
790 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
799 dhcp6_lease_clear_timers(&client->lease->ia);
801 client->lease = sd_dhcp6_lease_unref(client->lease);
802 client->lease = lease;
805 return DHCP6_STATE_BOUND;
808 static int client_receive_advertise(sd_dhcp6_client *client,
809 DHCP6Message *advertise, size_t len) {
811 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
812 uint8_t pref_advertise = 0, pref_lease = 0;
814 if (advertise->type != DHCP6_ADVERTISE)
817 r = dhcp6_lease_new(&lease);
821 r = client_parse_message(client, advertise, len, lease);
825 r = dhcp6_lease_get_preference(lease, &pref_advertise);
829 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
830 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
831 sd_dhcp6_lease_unref(client->lease);
832 client->lease = lease;
837 if (pref_advertise == 255 || client->retransmit_count > 1)
838 r = DHCP6_STATE_REQUEST;
843 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
845 sd_dhcp6_client *client = userdata;
846 DHCP6_CLIENT_DONT_DESTROY(client);
847 _cleanup_free_ DHCP6Message *message;
852 assert(client->event);
854 r = ioctl(fd, FIONREAD, &buflen);
855 if (r < 0 || buflen <= 0)
856 buflen = DHCP6_MIN_OPTIONS_SIZE;
858 message = malloc0(buflen);
862 len = read(fd, message, buflen);
863 if ((size_t)len < sizeof(DHCP6Message)) {
864 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
868 switch(message->type) {
876 case DHCP6_INFORMATION_REQUEST:
877 case DHCP6_RELAY_FORW:
878 case DHCP6_RELAY_REPL:
881 case DHCP6_ADVERTISE:
883 case DHCP6_RECONFIGURE:
887 log_dhcp6_client(client, "unknown message type %d",
892 if (client->transaction_id != (message->transaction_id &
893 htobe32(0x00ffffff)))
896 switch (client->state) {
897 case DHCP6_STATE_SOLICITATION:
898 r = client_receive_advertise(client, message, len);
900 if (r == DHCP6_STATE_REQUEST) {
901 client_start(client, r);
906 /* fall through for Soliciation Rapid Commit option check */
907 case DHCP6_STATE_REQUEST:
908 case DHCP6_STATE_RENEW:
909 case DHCP6_STATE_REBIND:
911 r = client_receive_reply(client, message, len);
915 if (r == DHCP6_STATE_BOUND) {
917 r = client_start(client, DHCP6_STATE_BOUND);
919 client_stop(client, r);
923 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
928 case DHCP6_STATE_BOUND:
932 case DHCP6_STATE_STOPPED:
937 log_dhcp6_client(client, "Recv %s",
938 dhcp6_message_type_to_string(message->type));
944 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
947 usec_t timeout, time_now;
948 char time_string[FORMAT_TIMESPAN_MAX];
950 assert_return(client, -EINVAL);
951 assert_return(client->event, -EINVAL);
952 assert_return(client->index > 0, -EINVAL);
953 assert_return(client->state != state, -EINVAL);
955 client->timeout_resend_expire =
956 sd_event_source_unref(client->timeout_resend_expire);
957 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
958 client->retransmit_time = 0;
959 client->retransmit_count = 0;
961 if (client->state == DHCP6_STATE_STOPPED) {
962 time_now = now(clock_boottime_or_monotonic());
964 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
971 case DHCP6_STATE_STOPPED:
972 case DHCP6_STATE_SOLICITATION:
974 r = client_ensure_iaid(client);
978 r = dhcp6_network_bind_udp_socket(client->index, NULL);
984 r = sd_event_add_io(client->event, &client->receive_message,
985 client->fd, EPOLLIN, client_receive_message,
990 r = sd_event_source_set_priority(client->receive_message,
991 client->event_priority);
995 r = sd_event_source_set_name(client->receive_message,
996 "dhcp6-receive-message");
1000 client->state = DHCP6_STATE_SOLICITATION;
1004 case DHCP6_STATE_REQUEST:
1005 case DHCP6_STATE_RENEW:
1006 case DHCP6_STATE_REBIND:
1008 client->state = state;
1012 case DHCP6_STATE_BOUND:
1014 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1015 client->lease->ia.lifetime_t2 == 0xffffffff) {
1017 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1018 be32toh(client->lease->ia.lifetime_t1),
1019 be32toh(client->lease->ia.lifetime_t2));
1024 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1026 log_dhcp6_client(client, "T1 expires in %s",
1027 format_timespan(time_string,
1028 FORMAT_TIMESPAN_MAX,
1031 r = sd_event_add_time(client->event,
1032 &client->lease->ia.timeout_t1,
1033 clock_boottime_or_monotonic(), time_now + timeout,
1034 10 * USEC_PER_SEC, client_timeout_t1,
1039 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1040 client->event_priority);
1044 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
1045 "dhcp6-t1-timeout");
1049 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1051 log_dhcp6_client(client, "T2 expires in %s",
1052 format_timespan(time_string,
1053 FORMAT_TIMESPAN_MAX,
1056 r = sd_event_add_time(client->event,
1057 &client->lease->ia.timeout_t2,
1058 clock_boottime_or_monotonic(), time_now + timeout,
1059 10 * USEC_PER_SEC, client_timeout_t2,
1064 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1065 client->event_priority);
1069 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
1070 "dhcp6-t2-timeout");
1074 client->state = state;
1079 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1080 client->transaction_start = time_now;
1082 r = sd_event_add_time(client->event, &client->timeout_resend,
1083 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1088 r = sd_event_source_set_priority(client->timeout_resend,
1089 client->event_priority);
1093 r = sd_event_source_set_name(client->timeout_resend,
1094 "dhcp6-resend-timeout");
1101 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1103 client_stop(client, DHCP6_EVENT_STOP);
1108 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1112 assert_return(client, -EINVAL);
1113 assert_return(client->event, -EINVAL);
1114 assert_return(client->index > 0, -EINVAL);
1116 r = client_reset(client);
1120 return client_start(client, DHCP6_STATE_SOLICITATION);
1123 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1128 assert_return(client, -EINVAL);
1129 assert_return(!client->event, -EBUSY);
1132 client->event = sd_event_ref(event);
1134 r = sd_event_default(&client->event);
1139 client->event_priority = priority;
1144 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1145 assert_return(client, -EINVAL);
1147 client->event = sd_event_unref(client->event);
1152 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1156 return client->event;
1159 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1161 assert_se(REFCNT_INC(client->n_ref) >= 2);
1166 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1167 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1168 client_reset(client);
1170 sd_dhcp6_client_detach_event(client);
1172 free(client->req_opts);
1181 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1183 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1184 sd_id128_t machine_id;
1188 assert_return(ret, -EINVAL);
1190 client = new0(sd_dhcp6_client, 1);
1194 client->n_ref = REFCNT_INIT;
1196 client->ia_na.type = DHCP6_OPTION_IA_NA;
1202 /* initialize DUID */
1203 client->duid.en.type = htobe16(DHCP6_DUID_EN);
1204 client->duid.en.pen = htobe32(SYSTEMD_PEN);
1205 client->duid_len = sizeof(client->duid.en);
1207 r = sd_id128_get_machine(&machine_id);
1211 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1213 siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1215 client->req_opts_len = ELEMENTSOF(default_req_opts);
1217 client->req_opts = new0(be16_t, client->req_opts_len);
1218 if (!client->req_opts)
1221 for (t = 0; t < client->req_opts_len; t++)
1222 client->req_opts[t] = htobe16(default_req_opts[t]);