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>
25 #include <linux/if_infiniband.h>
28 #include "udev-util.h"
30 #include "siphash24.h"
34 #include "network-internal.h"
35 #include "sd-dhcp6-client.h"
36 #include "dhcp6-protocol.h"
37 #include "dhcp6-internal.h"
38 #include "dhcp6-lease-internal.h"
40 #define SYSTEMD_PEN 43793
41 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
43 /* RFC 3315 section 9.1:
44 * A DUID can be no more than 128 octets long (not including the type code).
46 #define MAX_DUID_LEN 128
48 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
50 struct sd_dhcp6_client {
53 enum DHCP6State state;
57 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
61 be32_t transaction_id;
62 usec_t transaction_start;
63 struct sd_dhcp6_lease *lease;
66 size_t req_opts_allocated;
68 sd_event_source *receive_message;
69 usec_t retransmit_time;
70 uint8_t retransmit_count;
71 sd_event_source *timeout_resend;
72 sd_event_source *timeout_resend_expire;
73 sd_dhcp6_client_cb_t cb;
77 uint16_t type; /* DHCP6_DUID_LLT */
83 uint16_t type; /* DHCP6_DUID_EN */
88 uint16_t type; /* DHCP6_DUID_LL */
93 uint16_t type; /* DHCP6_DUID_UUID */
98 uint8_t data[MAX_DUID_LEN];
104 static const uint16_t default_req_opts[] = {
105 DHCP6_OPTION_DNS_SERVERS,
106 DHCP6_OPTION_DOMAIN_LIST,
107 DHCP6_OPTION_NTP_SERVER,
110 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
111 [DHCP6_SOLICIT] = "SOLICIT",
112 [DHCP6_ADVERTISE] = "ADVERTISE",
113 [DHCP6_REQUEST] = "REQUEST",
114 [DHCP6_CONFIRM] = "CONFIRM",
115 [DHCP6_RENEW] = "RENEW",
116 [DHCP6_REBIND] = "REBIND",
117 [DHCP6_REPLY] = "REPLY",
118 [DHCP6_RELEASE] = "RELEASE",
119 [DHCP6_DECLINE] = "DECLINE",
120 [DHCP6_RECONFIGURE] = "RECONFIGURE",
121 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
122 [DHCP6_RELAY_FORW] = "RELAY-FORW",
123 [DHCP6_RELAY_REPL] = "RELAY-REPL",
126 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
128 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
129 [DHCP6_STATUS_SUCCESS] = "Success",
130 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
131 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
132 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
133 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
134 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
137 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
139 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
140 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
142 #define DHCP6_CLIENT_DONT_DESTROY(client) \
143 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
145 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
147 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
148 sd_dhcp6_client_cb_t cb, void *userdata)
150 assert_return(client, -EINVAL);
153 client->userdata = userdata;
158 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
160 assert_return(client, -EINVAL);
161 assert_return(interface_index >= -1, -EINVAL);
163 client->index = interface_index;
168 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
169 size_t addr_len, uint16_t arp_type)
171 assert_return(client, -EINVAL);
172 assert_return(addr, -EINVAL);
173 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
174 assert_return(arp_type > 0, -EINVAL);
176 if (arp_type == ARPHRD_ETHER)
177 assert_return(addr_len == ETH_ALEN, -EINVAL);
178 else if (arp_type == ARPHRD_INFINIBAND)
179 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
183 if (client->mac_addr_len == addr_len &&
184 memcmp(&client->mac_addr, addr, addr_len) == 0)
187 memcpy(&client->mac_addr, addr, addr_len);
188 client->mac_addr_len = addr_len;
189 client->arp_type = arp_type;
194 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
197 assert_return(client, -EINVAL);
198 assert_return(duid, -EINVAL);
199 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
203 if (duid_len <= sizeof(client->duid.llt) - 2)
207 if (duid_len != sizeof(client->duid.en) - 2)
211 if (duid_len <= sizeof(client->duid.ll) - 2)
214 case DHCP6_DUID_UUID:
215 if (duid_len != sizeof(client->duid.uuid) - 2)
219 /* accept unknown type in order to be forward compatible */
223 client->duid.raw.type = htobe16(type);
224 memcpy(&client->duid.raw.data, duid, duid_len);
225 client->duid_len = duid_len + 2; /* +2 for sizeof(type) */
230 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
234 assert_return(client, -EINVAL);
235 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
238 case DHCP6_OPTION_DNS_SERVERS:
239 case DHCP6_OPTION_DOMAIN_LIST:
240 case DHCP6_OPTION_SNTP_SERVERS:
241 case DHCP6_OPTION_NTP_SERVER:
248 for (t = 0; t < client->req_opts_len; t++)
249 if (client->req_opts[t] == htobe16(option))
252 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
253 client->req_opts_len + 1))
256 client->req_opts[client->req_opts_len++] = htobe16(option);
261 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
262 assert_return(client, -EINVAL);
263 assert_return(ret, -EINVAL);
268 *ret = sd_dhcp6_lease_ref(client->lease);
273 static void client_notify(sd_dhcp6_client *client, int event) {
275 client->cb(client, event, client->userdata);
278 static int client_reset(sd_dhcp6_client *client) {
279 assert_return(client, -EINVAL);
281 client->receive_message =
282 sd_event_source_unref(client->receive_message);
284 client->fd = safe_close(client->fd);
286 client->transaction_id = 0;
287 client->transaction_start = 0;
289 client->ia_na.timeout_t1 =
290 sd_event_source_unref(client->ia_na.timeout_t1);
291 client->ia_na.timeout_t2 =
292 sd_event_source_unref(client->ia_na.timeout_t2);
294 client->retransmit_time = 0;
295 client->retransmit_count = 0;
296 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
297 client->timeout_resend_expire =
298 sd_event_source_unref(client->timeout_resend_expire);
300 client->state = DHCP6_STATE_STOPPED;
305 static void client_stop(sd_dhcp6_client *client, int error) {
306 DHCP6_CLIENT_DONT_DESTROY(client);
310 client_notify(client, error);
312 client_reset(client);
315 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
316 _cleanup_free_ DHCP6Message *message = NULL;
317 struct in6_addr all_servers =
318 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
319 size_t len, optlen = 512;
325 len = sizeof(DHCP6Message) + optlen;
327 message = malloc0(len);
331 opt = (uint8_t *)(message + 1);
333 message->transaction_id = client->transaction_id;
335 switch(client->state) {
336 case DHCP6_STATE_SOLICITATION:
337 message->type = DHCP6_SOLICIT;
339 r = dhcp6_option_append(&opt, &optlen,
340 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
344 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
350 case DHCP6_STATE_REQUEST:
351 case DHCP6_STATE_RENEW:
353 if (client->state == DHCP6_STATE_REQUEST)
354 message->type = DHCP6_REQUEST;
356 message->type = DHCP6_RENEW;
358 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
359 client->lease->serverid_len,
360 client->lease->serverid);
364 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
370 case DHCP6_STATE_REBIND:
371 message->type = DHCP6_REBIND;
373 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
379 case DHCP6_STATE_STOPPED:
380 case DHCP6_STATE_BOUND:
384 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
385 client->req_opts_len * sizeof(be16_t),
390 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
391 client->duid_len, &client->duid);
395 elapsed_usec = time_now - client->transaction_start;
396 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
397 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
399 elapsed_time = 0xffff;
401 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
402 sizeof(elapsed_time), &elapsed_time);
406 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
411 log_dhcp6_client(client, "Sent %s",
412 dhcp6_message_type_to_string(message->type));
417 static int client_timeout_t2(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_t2 =
426 sd_event_source_unref(client->lease->ia.timeout_t2);
428 log_dhcp6_client(client, "Timeout T2");
430 client_start(client, DHCP6_STATE_REBIND);
435 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
437 sd_dhcp6_client *client = userdata;
439 assert_return(s, -EINVAL);
440 assert_return(client, -EINVAL);
441 assert_return(client->lease, -EINVAL);
443 client->lease->ia.timeout_t1 =
444 sd_event_source_unref(client->lease->ia.timeout_t1);
446 log_dhcp6_client(client, "Timeout T1");
448 client_start(client, DHCP6_STATE_RENEW);
453 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
455 sd_dhcp6_client *client = userdata;
456 DHCP6_CLIENT_DONT_DESTROY(client);
457 enum DHCP6State state;
461 assert(client->event);
463 state = client->state;
465 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
467 /* RFC 3315, section 18.1.4., says that "...the client may choose to
468 use a Solicit message to locate a new DHCP server..." */
469 if (state == DHCP6_STATE_REBIND)
470 client_start(client, DHCP6_STATE_SOLICITATION);
475 static usec_t client_timeout_compute_random(usec_t val) {
476 return val - val / 10 +
477 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
480 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
483 sd_dhcp6_client *client = userdata;
484 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
485 usec_t max_retransmit_duration = 0;
486 uint8_t max_retransmit_count = 0;
487 char time_string[FORMAT_TIMESPAN_MAX];
492 assert(client->event);
494 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
496 switch (client->state) {
497 case DHCP6_STATE_SOLICITATION:
499 if (client->retransmit_count && client->lease) {
500 client_start(client, DHCP6_STATE_REQUEST);
504 init_retransmit_time = DHCP6_SOL_TIMEOUT;
505 max_retransmit_time = DHCP6_SOL_MAX_RT;
509 case DHCP6_STATE_REQUEST:
510 init_retransmit_time = DHCP6_REQ_TIMEOUT;
511 max_retransmit_time = DHCP6_REQ_MAX_RT;
512 max_retransmit_count = DHCP6_REQ_MAX_RC;
516 case DHCP6_STATE_RENEW:
517 init_retransmit_time = DHCP6_REN_TIMEOUT;
518 max_retransmit_time = DHCP6_REN_MAX_RT;
520 /* RFC 3315, section 18.1.3. says max retransmit duration will
521 be the remaining time until T2. Instead of setting MRD,
522 wait for T2 to trigger with the same end result */
526 case DHCP6_STATE_REBIND:
527 init_retransmit_time = DHCP6_REB_TIMEOUT;
528 max_retransmit_time = DHCP6_REB_MAX_RT;
530 if (!client->timeout_resend_expire) {
531 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
534 client_stop(client, r);
537 max_retransmit_duration = expire * USEC_PER_SEC;
542 case DHCP6_STATE_STOPPED:
543 case DHCP6_STATE_BOUND:
547 if (max_retransmit_count &&
548 client->retransmit_count >= max_retransmit_count) {
549 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
553 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
557 r = client_send_message(client, time_now);
559 client->retransmit_count++;
561 if (!client->retransmit_time) {
562 client->retransmit_time =
563 client_timeout_compute_random(init_retransmit_time);
565 if (client->state == DHCP6_STATE_SOLICITATION)
566 client->retransmit_time += init_retransmit_time / 10;
569 if (max_retransmit_time &&
570 client->retransmit_time > max_retransmit_time / 2)
571 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
573 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
576 log_dhcp6_client(client, "Next retransmission in %s",
577 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
578 client->retransmit_time, 0));
580 r = sd_event_add_time(client->event, &client->timeout_resend,
581 clock_boottime_or_monotonic(),
582 time_now + client->retransmit_time,
583 10 * USEC_PER_MSEC, client_timeout_resend,
588 r = sd_event_source_set_priority(client->timeout_resend,
589 client->event_priority);
593 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
597 if (max_retransmit_duration && !client->timeout_resend_expire) {
599 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
600 max_retransmit_duration / USEC_PER_SEC);
602 r = sd_event_add_time(client->event,
603 &client->timeout_resend_expire,
604 clock_boottime_or_monotonic(),
605 time_now + max_retransmit_duration,
607 client_timeout_resend_expire, client);
611 r = sd_event_source_set_priority(client->timeout_resend_expire,
612 client->event_priority);
616 r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
623 client_stop(client, r);
628 static int client_ensure_iaid(sd_dhcp6_client *client) {
629 /* name is a pointer to memory in the udev_device struct, so must
630 have the same scope */
631 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
632 const char *name = NULL;
637 if (client->ia_na.id)
640 if (detect_container(NULL) <= 0) {
641 /* not in a container, udev will be around */
642 _cleanup_udev_unref_ struct udev *udev;
643 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
649 sprintf(ifindex_str, "n%d", client->index);
650 device = udev_device_new_from_device_id(udev, ifindex_str);
654 if (udev_device_get_is_initialized(device) <= 0)
658 name = net_get_name(device);
662 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
664 /* fall back to mac address if no predictable name available */
665 siphash24((uint8_t*)&id, &client->mac_addr,
666 client->mac_addr_len, HASH_KEY.bytes);
668 /* fold into 32 bits */
669 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
674 static int client_parse_message(sd_dhcp6_client *client,
675 DHCP6Message *message, size_t len,
676 sd_dhcp6_lease *lease) {
678 uint8_t *optval, *option, *id = NULL;
679 uint16_t optcode, status;
680 size_t optlen, id_len;
681 bool clientid = false;
684 option = (uint8_t *)message + sizeof(DHCP6Message);
685 len -= sizeof(DHCP6Message);
687 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
690 case DHCP6_OPTION_CLIENTID:
692 log_dhcp6_client(client, "%s contains multiple clientids",
693 dhcp6_message_type_to_string(message->type));
697 if (optlen != client->duid_len ||
698 memcmp(&client->duid, optval, optlen) != 0) {
699 log_dhcp6_client(client, "%s DUID does not match",
700 dhcp6_message_type_to_string(message->type));
708 case DHCP6_OPTION_SERVERID:
709 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
711 log_dhcp6_client(client, "%s contains multiple serverids",
712 dhcp6_message_type_to_string(message->type));
716 r = dhcp6_lease_set_serverid(lease, optval, optlen);
722 case DHCP6_OPTION_PREFERENCE:
726 r = dhcp6_lease_set_preference(lease, *optval);
732 case DHCP6_OPTION_STATUS_CODE:
736 status = optval[0] << 8 | optval[1];
738 log_dhcp6_client(client, "%s Status %s",
739 dhcp6_message_type_to_string(message->type),
740 dhcp6_message_status_to_string(status));
746 case DHCP6_OPTION_IA_NA:
747 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
749 if (r < 0 && r != -ENOMSG)
752 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
756 if (client->ia_na.id != iaid_lease) {
757 log_dhcp6_client(client, "%s has wrong IAID",
758 dhcp6_message_type_to_string(message->type));
764 case DHCP6_OPTION_RAPID_COMMIT:
765 r = dhcp6_lease_set_rapid_commit(lease);
776 if (r < 0 || !clientid) {
777 log_dhcp6_client(client, "%s has incomplete options",
778 dhcp6_message_type_to_string(message->type));
782 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
784 log_dhcp6_client(client, "%s has no server id",
785 dhcp6_message_type_to_string(message->type));
790 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
794 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
797 if (reply->type != DHCP6_REPLY)
800 r = dhcp6_lease_new(&lease);
804 r = client_parse_message(client, reply, len, lease);
808 if (client->state == DHCP6_STATE_SOLICITATION) {
809 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
818 dhcp6_lease_clear_timers(&client->lease->ia);
820 client->lease = sd_dhcp6_lease_unref(client->lease);
821 client->lease = lease;
824 return DHCP6_STATE_BOUND;
827 static int client_receive_advertise(sd_dhcp6_client *client,
828 DHCP6Message *advertise, size_t len) {
830 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
831 uint8_t pref_advertise = 0, pref_lease = 0;
833 if (advertise->type != DHCP6_ADVERTISE)
836 r = dhcp6_lease_new(&lease);
840 r = client_parse_message(client, advertise, len, lease);
844 r = dhcp6_lease_get_preference(lease, &pref_advertise);
848 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
849 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
850 sd_dhcp6_lease_unref(client->lease);
851 client->lease = lease;
856 if (pref_advertise == 255 || client->retransmit_count > 1)
857 r = DHCP6_STATE_REQUEST;
862 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
864 sd_dhcp6_client *client = userdata;
865 DHCP6_CLIENT_DONT_DESTROY(client);
866 _cleanup_free_ DHCP6Message *message;
871 assert(client->event);
873 r = ioctl(fd, FIONREAD, &buflen);
874 if (r < 0 || buflen <= 0)
875 buflen = DHCP6_MIN_OPTIONS_SIZE;
877 message = malloc0(buflen);
881 len = read(fd, message, buflen);
882 if ((size_t)len < sizeof(DHCP6Message)) {
883 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
887 switch(message->type) {
895 case DHCP6_INFORMATION_REQUEST:
896 case DHCP6_RELAY_FORW:
897 case DHCP6_RELAY_REPL:
900 case DHCP6_ADVERTISE:
902 case DHCP6_RECONFIGURE:
906 log_dhcp6_client(client, "unknown message type %d",
911 if (client->transaction_id != (message->transaction_id &
912 htobe32(0x00ffffff)))
915 switch (client->state) {
916 case DHCP6_STATE_SOLICITATION:
917 r = client_receive_advertise(client, message, len);
919 if (r == DHCP6_STATE_REQUEST) {
920 client_start(client, r);
925 /* fall through for Soliciation Rapid Commit option check */
926 case DHCP6_STATE_REQUEST:
927 case DHCP6_STATE_RENEW:
928 case DHCP6_STATE_REBIND:
930 r = client_receive_reply(client, message, len);
934 if (r == DHCP6_STATE_BOUND) {
936 r = client_start(client, DHCP6_STATE_BOUND);
938 client_stop(client, r);
942 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
947 case DHCP6_STATE_BOUND:
951 case DHCP6_STATE_STOPPED:
956 log_dhcp6_client(client, "Recv %s",
957 dhcp6_message_type_to_string(message->type));
963 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
966 usec_t timeout, time_now;
967 char time_string[FORMAT_TIMESPAN_MAX];
969 assert_return(client, -EINVAL);
970 assert_return(client->event, -EINVAL);
971 assert_return(client->index > 0, -EINVAL);
972 assert_return(client->state != state, -EINVAL);
974 client->timeout_resend_expire =
975 sd_event_source_unref(client->timeout_resend_expire);
976 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
977 client->retransmit_time = 0;
978 client->retransmit_count = 0;
980 if (client->state == DHCP6_STATE_STOPPED) {
981 time_now = now(clock_boottime_or_monotonic());
983 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
990 case DHCP6_STATE_STOPPED:
991 case DHCP6_STATE_SOLICITATION:
993 r = client_ensure_iaid(client);
997 r = dhcp6_network_bind_udp_socket(client->index, NULL);
1003 r = sd_event_add_io(client->event, &client->receive_message,
1004 client->fd, EPOLLIN, client_receive_message,
1009 r = sd_event_source_set_priority(client->receive_message,
1010 client->event_priority);
1014 r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message");
1018 client->state = DHCP6_STATE_SOLICITATION;
1022 case DHCP6_STATE_REQUEST:
1023 case DHCP6_STATE_RENEW:
1024 case DHCP6_STATE_REBIND:
1026 client->state = state;
1030 case DHCP6_STATE_BOUND:
1032 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1033 client->lease->ia.lifetime_t2 == 0xffffffff) {
1035 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1036 be32toh(client->lease->ia.lifetime_t1),
1037 be32toh(client->lease->ia.lifetime_t2));
1042 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1044 log_dhcp6_client(client, "T1 expires in %s",
1045 format_timespan(time_string,
1046 FORMAT_TIMESPAN_MAX,
1049 r = sd_event_add_time(client->event,
1050 &client->lease->ia.timeout_t1,
1051 clock_boottime_or_monotonic(), time_now + timeout,
1052 10 * USEC_PER_SEC, client_timeout_t1,
1057 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1058 client->event_priority);
1062 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
1066 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1068 log_dhcp6_client(client, "T2 expires in %s",
1069 format_timespan(time_string,
1070 FORMAT_TIMESPAN_MAX,
1073 r = sd_event_add_time(client->event,
1074 &client->lease->ia.timeout_t2,
1075 clock_boottime_or_monotonic(), time_now + timeout,
1076 10 * USEC_PER_SEC, client_timeout_t2,
1081 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1082 client->event_priority);
1086 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
1090 client->state = state;
1095 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1096 client->transaction_start = time_now;
1098 r = sd_event_add_time(client->event, &client->timeout_resend,
1099 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1104 r = sd_event_source_set_priority(client->timeout_resend,
1105 client->event_priority);
1109 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1116 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1118 client_stop(client, DHCP6_EVENT_STOP);
1123 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1127 assert_return(client, -EINVAL);
1128 assert_return(client->event, -EINVAL);
1129 assert_return(client->index > 0, -EINVAL);
1131 r = client_reset(client);
1135 return client_start(client, DHCP6_STATE_SOLICITATION);
1138 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1143 assert_return(client, -EINVAL);
1144 assert_return(!client->event, -EBUSY);
1147 client->event = sd_event_ref(event);
1149 r = sd_event_default(&client->event);
1154 client->event_priority = priority;
1159 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1160 assert_return(client, -EINVAL);
1162 client->event = sd_event_unref(client->event);
1167 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1171 return client->event;
1174 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1176 assert_se(REFCNT_INC(client->n_ref) >= 2);
1181 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1182 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1183 client_reset(client);
1185 sd_dhcp6_client_detach_event(client);
1187 free(client->req_opts);
1196 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1198 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1199 sd_id128_t machine_id;
1203 assert_return(ret, -EINVAL);
1205 client = new0(sd_dhcp6_client, 1);
1209 client->n_ref = REFCNT_INIT;
1211 client->ia_na.type = DHCP6_OPTION_IA_NA;
1217 /* initialize DUID */
1218 client->duid.en.type = htobe16(DHCP6_DUID_EN);
1219 client->duid.en.pen = htobe32(SYSTEMD_PEN);
1220 client->duid_len = sizeof(client->duid.en);
1222 r = sd_id128_get_machine(&machine_id);
1226 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1228 siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1230 client->req_opts_len = ELEMENTSOF(default_req_opts);
1232 client->req_opts = new0(be16_t, client->req_opts_len);
1233 if (!client->req_opts)
1236 for (t = 0; t < client->req_opts_len; t++)
1237 client->req_opts[t] = htobe16(default_req_opts[t]);