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;
70 uint8_t duid[MAX_DUID_LEN];
74 static const uint16_t default_req_opts[] = {
75 DHCP6_OPTION_DNS_SERVERS,
76 DHCP6_OPTION_DOMAIN_LIST,
77 DHCP6_OPTION_NTP_SERVER,
80 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
81 [DHCP6_SOLICIT] = "SOLICIT",
82 [DHCP6_ADVERTISE] = "ADVERTISE",
83 [DHCP6_REQUEST] = "REQUEST",
84 [DHCP6_CONFIRM] = "CONFIRM",
85 [DHCP6_RENEW] = "RENEW",
86 [DHCP6_REBIND] = "REBIND",
87 [DHCP6_REPLY] = "REPLY",
88 [DHCP6_RELEASE] = "RELEASE",
89 [DHCP6_DECLINE] = "DECLINE",
90 [DHCP6_RECONFIGURE] = "RECONFIGURE",
91 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
92 [DHCP6_RELAY_FORW] = "RELAY-FORW",
93 [DHCP6_RELAY_REPL] = "RELAY-REPL",
96 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
98 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
99 [DHCP6_STATUS_SUCCESS] = "Success",
100 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
101 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
102 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
103 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
104 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
107 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
109 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
110 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
112 #define DHCP6_CLIENT_DONT_DESTROY(client) \
113 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
115 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
117 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
118 sd_dhcp6_client_cb_t cb, void *userdata)
120 assert_return(client, -EINVAL);
123 client->userdata = userdata;
128 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
130 assert_return(client, -EINVAL);
131 assert_return(interface_index >= -1, -EINVAL);
133 client->index = interface_index;
138 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
139 const struct ether_addr *mac_addr)
141 assert_return(client, -EINVAL);
144 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
146 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
151 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint8_t *duid,
154 assert_return(client, -EINVAL);
155 assert_return(duid, -EINVAL);
156 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
158 memcpy(&client->duid, duid, duid_len);
159 client->duid_len = duid_len;
164 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
168 assert_return(client, -EINVAL);
169 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
172 case DHCP6_OPTION_DNS_SERVERS:
173 case DHCP6_OPTION_DOMAIN_LIST:
174 case DHCP6_OPTION_SNTP_SERVERS:
175 case DHCP6_OPTION_NTP_SERVER:
182 for (t = 0; t < client->req_opts_len; t++)
183 if (client->req_opts[t] == htobe16(option))
186 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
187 client->req_opts_len + 1))
190 client->req_opts[client->req_opts_len++] = htobe16(option);
195 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
196 assert_return(client, -EINVAL);
197 assert_return(ret, -EINVAL);
202 *ret = sd_dhcp6_lease_ref(client->lease);
207 static void client_notify(sd_dhcp6_client *client, int event) {
209 client->cb(client, event, client->userdata);
212 static int client_reset(sd_dhcp6_client *client) {
213 assert_return(client, -EINVAL);
215 client->receive_message =
216 sd_event_source_unref(client->receive_message);
218 client->fd = safe_close(client->fd);
220 client->transaction_id = 0;
221 client->transaction_start = 0;
223 client->ia_na.timeout_t1 =
224 sd_event_source_unref(client->ia_na.timeout_t1);
225 client->ia_na.timeout_t2 =
226 sd_event_source_unref(client->ia_na.timeout_t2);
228 client->retransmit_time = 0;
229 client->retransmit_count = 0;
230 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
231 client->timeout_resend_expire =
232 sd_event_source_unref(client->timeout_resend_expire);
234 client->state = DHCP6_STATE_STOPPED;
239 static void client_stop(sd_dhcp6_client *client, int error) {
240 DHCP6_CLIENT_DONT_DESTROY(client);
244 client_notify(client, error);
246 client_reset(client);
249 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
250 _cleanup_free_ DHCP6Message *message = NULL;
251 struct in6_addr all_servers =
252 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
253 size_t len, optlen = 512;
259 len = sizeof(DHCP6Message) + optlen;
261 message = malloc0(len);
265 opt = (uint8_t *)(message + 1);
267 message->transaction_id = client->transaction_id;
269 switch(client->state) {
270 case DHCP6_STATE_SOLICITATION:
271 message->type = DHCP6_SOLICIT;
273 r = dhcp6_option_append(&opt, &optlen,
274 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
278 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
284 case DHCP6_STATE_REQUEST:
285 case DHCP6_STATE_RENEW:
287 if (client->state == DHCP6_STATE_REQUEST)
288 message->type = DHCP6_REQUEST;
290 message->type = DHCP6_RENEW;
292 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
293 client->lease->serverid_len,
294 client->lease->serverid);
298 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
304 case DHCP6_STATE_REBIND:
305 message->type = DHCP6_REBIND;
307 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
313 case DHCP6_STATE_STOPPED:
314 case DHCP6_STATE_BOUND:
318 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
319 client->req_opts_len * sizeof(be16_t),
324 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
325 client->duid_len, &client->duid);
329 elapsed_usec = time_now - client->transaction_start;
330 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
331 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
333 elapsed_time = 0xffff;
335 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ELAPSED_TIME,
336 sizeof(elapsed_time), &elapsed_time);
340 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
345 log_dhcp6_client(client, "Sent %s",
346 dhcp6_message_type_to_string(message->type));
351 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
353 sd_dhcp6_client *client = userdata;
355 assert_return(s, -EINVAL);
356 assert_return(client, -EINVAL);
357 assert_return(client->lease, -EINVAL);
359 client->lease->ia.timeout_t2 =
360 sd_event_source_unref(client->lease->ia.timeout_t2);
362 log_dhcp6_client(client, "Timeout T2");
364 client_start(client, DHCP6_STATE_REBIND);
369 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
371 sd_dhcp6_client *client = userdata;
373 assert_return(s, -EINVAL);
374 assert_return(client, -EINVAL);
375 assert_return(client->lease, -EINVAL);
377 client->lease->ia.timeout_t1 =
378 sd_event_source_unref(client->lease->ia.timeout_t1);
380 log_dhcp6_client(client, "Timeout T1");
382 client_start(client, DHCP6_STATE_RENEW);
387 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
389 sd_dhcp6_client *client = userdata;
390 DHCP6_CLIENT_DONT_DESTROY(client);
391 enum DHCP6State state;
395 assert(client->event);
397 state = client->state;
399 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
401 /* RFC 3315, section 18.1.4., says that "...the client may choose to
402 use a Solicit message to locate a new DHCP server..." */
403 if (state == DHCP6_STATE_REBIND)
404 client_start(client, DHCP6_STATE_SOLICITATION);
409 static usec_t client_timeout_compute_random(usec_t val) {
410 return val - val / 10 +
411 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
414 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
417 sd_dhcp6_client *client = userdata;
418 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
419 usec_t max_retransmit_duration = 0;
420 uint8_t max_retransmit_count = 0;
421 char time_string[FORMAT_TIMESPAN_MAX];
426 assert(client->event);
428 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
430 switch (client->state) {
431 case DHCP6_STATE_SOLICITATION:
433 if (client->retransmit_count && client->lease) {
434 client_start(client, DHCP6_STATE_REQUEST);
438 init_retransmit_time = DHCP6_SOL_TIMEOUT;
439 max_retransmit_time = DHCP6_SOL_MAX_RT;
443 case DHCP6_STATE_REQUEST:
444 init_retransmit_time = DHCP6_REQ_TIMEOUT;
445 max_retransmit_time = DHCP6_REQ_MAX_RT;
446 max_retransmit_count = DHCP6_REQ_MAX_RC;
450 case DHCP6_STATE_RENEW:
451 init_retransmit_time = DHCP6_REN_TIMEOUT;
452 max_retransmit_time = DHCP6_REN_MAX_RT;
454 /* RFC 3315, section 18.1.3. says max retransmit duration will
455 be the remaining time until T2. Instead of setting MRD,
456 wait for T2 to trigger with the same end result */
460 case DHCP6_STATE_REBIND:
461 init_retransmit_time = DHCP6_REB_TIMEOUT;
462 max_retransmit_time = DHCP6_REB_MAX_RT;
464 if (!client->timeout_resend_expire) {
465 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
468 client_stop(client, r);
471 max_retransmit_duration = expire * USEC_PER_SEC;
476 case DHCP6_STATE_STOPPED:
477 case DHCP6_STATE_BOUND:
481 if (max_retransmit_count &&
482 client->retransmit_count >= max_retransmit_count) {
483 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
487 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
491 r = client_send_message(client, time_now);
493 client->retransmit_count++;
495 if (!client->retransmit_time) {
496 client->retransmit_time =
497 client_timeout_compute_random(init_retransmit_time);
499 if (client->state == DHCP6_STATE_SOLICITATION)
500 client->retransmit_time += init_retransmit_time / 10;
503 if (max_retransmit_time &&
504 client->retransmit_time > max_retransmit_time / 2)
505 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
507 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
510 log_dhcp6_client(client, "Next retransmission in %s",
511 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
512 client->retransmit_time, 0));
514 r = sd_event_add_time(client->event, &client->timeout_resend,
515 clock_boottime_or_monotonic(),
516 time_now + client->retransmit_time,
517 10 * USEC_PER_MSEC, client_timeout_resend,
522 r = sd_event_source_set_priority(client->timeout_resend,
523 client->event_priority);
527 r = sd_event_source_set_name(client->timeout_resend,
528 "dhcp6-resend-timer");
532 if (max_retransmit_duration && !client->timeout_resend_expire) {
534 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
535 max_retransmit_duration / USEC_PER_SEC);
537 r = sd_event_add_time(client->event,
538 &client->timeout_resend_expire,
539 clock_boottime_or_monotonic(),
540 time_now + max_retransmit_duration,
542 client_timeout_resend_expire, client);
546 r = sd_event_source_set_priority(client->timeout_resend_expire,
547 client->event_priority);
551 r = sd_event_source_set_name(client->timeout_resend_expire,
552 "dhcp6-resend-expire-timer");
559 client_stop(client, r);
564 static int client_ensure_iaid(sd_dhcp6_client *client) {
565 /* name is a pointer to memory in the udev_device struct, so must
566 have the same scope */
567 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
568 const char *name = NULL;
573 if (client->ia_na.id)
576 if (detect_container(NULL) <= 0) {
577 /* not in a container, udev will be around */
578 _cleanup_udev_unref_ struct udev *udev;
579 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
585 sprintf(ifindex_str, "n%d", client->index);
586 device = udev_device_new_from_device_id(udev, ifindex_str);
590 if (udev_device_get_is_initialized(device) <= 0)
594 name = net_get_name(device);
598 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
600 /* fall back to mac address if no predictable name available */
601 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
604 /* fold into 32 bits */
605 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
610 static int client_parse_message(sd_dhcp6_client *client,
611 DHCP6Message *message, size_t len,
612 sd_dhcp6_lease *lease) {
614 uint8_t *optval, *option, *id = NULL;
615 uint16_t optcode, status;
616 size_t optlen, id_len;
617 bool clientid = false;
620 option = (uint8_t *)message + sizeof(DHCP6Message);
621 len -= sizeof(DHCP6Message);
623 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
626 case DHCP6_OPTION_CLIENTID:
628 log_dhcp6_client(client, "%s contains multiple clientids",
629 dhcp6_message_type_to_string(message->type));
633 if (optlen != client->duid_len ||
634 memcmp(&client->duid, optval, optlen) != 0) {
635 log_dhcp6_client(client, "%s DUID does not match",
636 dhcp6_message_type_to_string(message->type));
644 case DHCP6_OPTION_SERVERID:
645 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
647 log_dhcp6_client(client, "%s contains multiple serverids",
648 dhcp6_message_type_to_string(message->type));
652 r = dhcp6_lease_set_serverid(lease, optval, optlen);
658 case DHCP6_OPTION_PREFERENCE:
662 r = dhcp6_lease_set_preference(lease, *optval);
668 case DHCP6_OPTION_STATUS_CODE:
672 status = optval[0] << 8 | optval[1];
674 log_dhcp6_client(client, "%s Status %s",
675 dhcp6_message_type_to_string(message->type),
676 dhcp6_message_status_to_string(status));
682 case DHCP6_OPTION_IA_NA:
683 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
685 if (r < 0 && r != -ENOMSG)
688 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
692 if (client->ia_na.id != iaid_lease) {
693 log_dhcp6_client(client, "%s has wrong IAID",
694 dhcp6_message_type_to_string(message->type));
700 case DHCP6_OPTION_RAPID_COMMIT:
701 r = dhcp6_lease_set_rapid_commit(lease);
709 if ((r < 0 && r != -ENOMSG) || !clientid) {
710 log_dhcp6_client(client, "%s has incomplete options",
711 dhcp6_message_type_to_string(message->type));
715 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
717 log_dhcp6_client(client, "%s has no server id",
718 dhcp6_message_type_to_string(message->type));
723 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
727 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
730 if (reply->type != DHCP6_REPLY)
733 r = dhcp6_lease_new(&lease);
737 r = client_parse_message(client, reply, len, lease);
741 if (client->state == DHCP6_STATE_SOLICITATION) {
742 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
751 dhcp6_lease_clear_timers(&client->lease->ia);
753 client->lease = sd_dhcp6_lease_unref(client->lease);
754 client->lease = lease;
757 return DHCP6_STATE_BOUND;
760 static int client_receive_advertise(sd_dhcp6_client *client,
761 DHCP6Message *advertise, size_t len) {
763 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
764 uint8_t pref_advertise = 0, pref_lease = 0;
766 if (advertise->type != DHCP6_ADVERTISE)
769 r = dhcp6_lease_new(&lease);
773 r = client_parse_message(client, advertise, len, lease);
777 r = dhcp6_lease_get_preference(lease, &pref_advertise);
781 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
782 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
783 sd_dhcp6_lease_unref(client->lease);
784 client->lease = lease;
789 if (pref_advertise == 255 || client->retransmit_count > 1)
790 r = DHCP6_STATE_REQUEST;
795 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
797 sd_dhcp6_client *client = userdata;
798 DHCP6_CLIENT_DONT_DESTROY(client);
799 _cleanup_free_ DHCP6Message *message;
804 assert(client->event);
806 r = ioctl(fd, FIONREAD, &buflen);
807 if (r < 0 || buflen <= 0)
808 buflen = DHCP6_MIN_OPTIONS_SIZE;
810 message = malloc0(buflen);
814 len = read(fd, message, buflen);
815 if ((size_t)len < sizeof(DHCP6Message)) {
816 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
820 switch(message->type) {
828 case DHCP6_INFORMATION_REQUEST:
829 case DHCP6_RELAY_FORW:
830 case DHCP6_RELAY_REPL:
833 case DHCP6_ADVERTISE:
835 case DHCP6_RECONFIGURE:
839 log_dhcp6_client(client, "unknown message type %d",
844 if (client->transaction_id != (message->transaction_id &
845 htobe32(0x00ffffff)))
848 switch (client->state) {
849 case DHCP6_STATE_SOLICITATION:
850 r = client_receive_advertise(client, message, len);
852 if (r == DHCP6_STATE_REQUEST) {
853 client_start(client, r);
858 /* fall through for Soliciation Rapid Commit option check */
859 case DHCP6_STATE_REQUEST:
860 case DHCP6_STATE_RENEW:
861 case DHCP6_STATE_REBIND:
863 r = client_receive_reply(client, message, len);
867 if (r == DHCP6_STATE_BOUND) {
869 r = client_start(client, DHCP6_STATE_BOUND);
871 client_stop(client, r);
875 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
880 case DHCP6_STATE_BOUND:
884 case DHCP6_STATE_STOPPED:
889 log_dhcp6_client(client, "Recv %s",
890 dhcp6_message_type_to_string(message->type));
896 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
899 usec_t timeout, time_now;
900 char time_string[FORMAT_TIMESPAN_MAX];
902 assert_return(client, -EINVAL);
903 assert_return(client->event, -EINVAL);
904 assert_return(client->index > 0, -EINVAL);
905 assert_return(client->state != state, -EINVAL);
907 client->timeout_resend_expire =
908 sd_event_source_unref(client->timeout_resend_expire);
909 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
910 client->retransmit_time = 0;
911 client->retransmit_count = 0;
913 if (client->state == DHCP6_STATE_STOPPED) {
914 time_now = now(clock_boottime_or_monotonic());
916 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
923 case DHCP6_STATE_STOPPED:
924 case DHCP6_STATE_SOLICITATION:
926 r = client_ensure_iaid(client);
930 r = dhcp6_network_bind_udp_socket(client->index, NULL);
936 r = sd_event_add_io(client->event, &client->receive_message,
937 client->fd, EPOLLIN, client_receive_message,
942 r = sd_event_source_set_priority(client->receive_message,
943 client->event_priority);
947 r = sd_event_source_set_name(client->receive_message,
948 "dhcp6-receive-message");
952 client->state = DHCP6_STATE_SOLICITATION;
956 case DHCP6_STATE_REQUEST:
957 case DHCP6_STATE_RENEW:
958 case DHCP6_STATE_REBIND:
960 client->state = state;
964 case DHCP6_STATE_BOUND:
966 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
967 client->lease->ia.lifetime_t2 == 0xffffffff) {
969 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
970 be32toh(client->lease->ia.lifetime_t1),
971 be32toh(client->lease->ia.lifetime_t2));
976 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
978 log_dhcp6_client(client, "T1 expires in %s",
979 format_timespan(time_string,
983 r = sd_event_add_time(client->event,
984 &client->lease->ia.timeout_t1,
985 clock_boottime_or_monotonic(), time_now + timeout,
986 10 * USEC_PER_SEC, client_timeout_t1,
991 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
992 client->event_priority);
996 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
1001 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1003 log_dhcp6_client(client, "T2 expires in %s",
1004 format_timespan(time_string,
1005 FORMAT_TIMESPAN_MAX,
1008 r = sd_event_add_time(client->event,
1009 &client->lease->ia.timeout_t2,
1010 clock_boottime_or_monotonic(), time_now + timeout,
1011 10 * USEC_PER_SEC, client_timeout_t2,
1016 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1017 client->event_priority);
1021 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
1022 "dhcp6-t2-timeout");
1026 client->state = state;
1031 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1032 client->transaction_start = time_now;
1034 r = sd_event_add_time(client->event, &client->timeout_resend,
1035 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1040 r = sd_event_source_set_priority(client->timeout_resend,
1041 client->event_priority);
1045 r = sd_event_source_set_name(client->timeout_resend,
1046 "dhcp6-resend-timeout");
1053 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1055 client_stop(client, DHCP6_EVENT_STOP);
1060 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1064 assert_return(client, -EINVAL);
1065 assert_return(client->event, -EINVAL);
1066 assert_return(client->index > 0, -EINVAL);
1068 r = client_reset(client);
1072 return client_start(client, DHCP6_STATE_SOLICITATION);
1075 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1080 assert_return(client, -EINVAL);
1081 assert_return(!client->event, -EBUSY);
1084 client->event = sd_event_ref(event);
1086 r = sd_event_default(&client->event);
1091 client->event_priority = priority;
1096 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1097 assert_return(client, -EINVAL);
1099 client->event = sd_event_unref(client->event);
1104 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1108 return client->event;
1111 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1113 assert_se(REFCNT_INC(client->n_ref) >= 2);
1118 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1119 if (client && REFCNT_DEC(client->n_ref) <= 0) {
1120 client_reset(client);
1122 sd_dhcp6_client_detach_event(client);
1124 free(client->req_opts);
1134 uint16_t type; /* DHCP6_DUID_EN */
1139 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1141 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1142 struct duid_en *duid;
1143 sd_id128_t machine_id;
1147 assert_return(ret, -EINVAL);
1149 client = new0(sd_dhcp6_client, 1);
1153 client->n_ref = REFCNT_INIT;
1155 client->ia_na.type = DHCP6_OPTION_IA_NA;
1161 /* initialize DUID */
1162 duid = (struct duid_en *) &client->duid;
1163 duid->type = htobe16(DHCP6_DUID_EN);
1164 duid->pen = htobe32(SYSTEMD_PEN);
1166 r = sd_id128_get_machine(&machine_id);
1170 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1172 siphash24(duid->id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1173 client->duid_len = sizeof (struct duid_en);
1175 client->req_opts_len = ELEMENTSOF(default_req_opts);
1177 client->req_opts = new0(be16_t, client->req_opts_len);
1178 if (!client->req_opts)
1181 for (t = 0; t < client->req_opts_len; t++)
1182 client->req_opts[t] = htobe16(default_req_opts[t]);