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;
54 sd_event_source *receive_message;
55 usec_t retransmit_time;
56 uint8_t retransmit_count;
57 sd_event_source *timeout_resend;
58 sd_event_source *timeout_resend_expire;
59 sd_dhcp6_client_cb_t cb;
63 uint16_t type; /* DHCP6_DUID_EN */
69 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
70 [DHCP6_SOLICIT] = "SOLICIT",
71 [DHCP6_ADVERTISE] = "ADVERTISE",
72 [DHCP6_REQUEST] = "REQUEST",
73 [DHCP6_CONFIRM] = "CONFIRM",
74 [DHCP6_RENEW] = "RENEW",
75 [DHCP6_REBIND] = "REBIND",
76 [DHCP6_REPLY] = "REPLY",
77 [DHCP6_RELEASE] = "RELEASE",
78 [DHCP6_DECLINE] = "DECLINE",
79 [DHCP6_RECONFIGURE] = "RECONFIGURE",
80 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
81 [DHCP6_RELAY_FORW] = "RELAY-FORW",
82 [DHCP6_RELAY_REPL] = "RELAY-REPL",
85 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
87 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
88 [DHCP6_STATUS_SUCCESS] = "Success",
89 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
90 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
91 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
92 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
93 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
96 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
98 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
100 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
101 sd_dhcp6_client_cb_t cb, void *userdata)
103 assert_return(client, -EINVAL);
106 client->userdata = userdata;
111 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
113 assert_return(client, -EINVAL);
114 assert_return(interface_index >= -1, -EINVAL);
116 client->index = interface_index;
121 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
122 const struct ether_addr *mac_addr)
124 assert_return(client, -EINVAL);
127 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
129 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
134 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
135 assert_return(client, -EINVAL);
136 assert_return(ret, -EINVAL);
141 *ret = sd_dhcp6_lease_ref(client->lease);
146 static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
148 client = sd_dhcp6_client_ref(client);
149 client->cb(client, event, client->userdata);
150 client = sd_dhcp6_client_unref(client);
156 static int client_reset(sd_dhcp6_client *client) {
157 assert_return(client, -EINVAL);
159 client->receive_message =
160 sd_event_source_unref(client->receive_message);
162 client->fd = safe_close(client->fd);
164 client->transaction_id = 0;
166 client->ia_na.timeout_t1 =
167 sd_event_source_unref(client->ia_na.timeout_t1);
168 client->ia_na.timeout_t2 =
169 sd_event_source_unref(client->ia_na.timeout_t2);
171 client->retransmit_time = 0;
172 client->retransmit_count = 0;
173 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
174 client->timeout_resend_expire =
175 sd_event_source_unref(client->timeout_resend_expire);
177 client->state = DHCP6_STATE_STOPPED;
182 static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
183 assert_return(client, NULL);
185 client = client_notify(client, error);
187 client_reset(client);
192 static int client_send_message(sd_dhcp6_client *client) {
193 _cleanup_free_ DHCP6Message *message = NULL;
194 struct in6_addr all_servers =
195 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
196 size_t len, optlen = 512;
200 len = sizeof(DHCP6Message) + optlen;
202 message = malloc0(len);
206 opt = (uint8_t *)(message + 1);
208 message->transaction_id = client->transaction_id;
210 switch(client->state) {
211 case DHCP6_STATE_SOLICITATION:
212 message->type = DHCP6_SOLICIT;
214 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
220 case DHCP6_STATE_REQUEST:
221 message->type = DHCP6_REQUEST;
223 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
224 client->lease->serverid_len,
225 client->lease->serverid);
229 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
235 case DHCP6_STATE_STOPPED:
237 case DHCP6_STATE_BOUND:
241 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
242 sizeof(client->duid), &client->duid);
246 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
251 log_dhcp6_client(client, "Sent %s",
252 dhcp6_message_type_to_string(message->type));
257 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
259 sd_dhcp6_client *client = userdata;
261 assert_return(s, -EINVAL);
262 assert_return(client, -EINVAL);
263 assert_return(client->lease, -EINVAL);
265 client->lease->ia.timeout_t2 =
266 sd_event_source_unref(client->lease->ia.timeout_t2);
268 log_dhcp6_client(client, "Timeout T2");
273 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
275 sd_dhcp6_client *client = userdata;
277 assert_return(s, -EINVAL);
278 assert_return(client, -EINVAL);
279 assert_return(client->lease, -EINVAL);
281 client->lease->ia.timeout_t1 =
282 sd_event_source_unref(client->lease->ia.timeout_t1);
284 log_dhcp6_client(client, "Timeout T1");
289 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
291 sd_dhcp6_client *client = userdata;
295 assert(client->event);
297 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
302 static usec_t client_timeout_compute_random(usec_t val) {
303 return val - val / 10 +
304 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
307 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
310 sd_dhcp6_client *client = userdata;
311 usec_t time_now, init_retransmit_time, max_retransmit_time;
312 usec_t max_retransmit_duration;
313 uint8_t max_retransmit_count = 0;
314 char time_string[FORMAT_TIMESPAN_MAX];
318 assert(client->event);
320 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
322 switch (client->state) {
323 case DHCP6_STATE_SOLICITATION:
325 if (client->retransmit_count && client->lease) {
326 client_start(client, DHCP6_STATE_REQUEST);
330 init_retransmit_time = DHCP6_SOL_TIMEOUT;
331 max_retransmit_time = DHCP6_SOL_MAX_RT;
332 max_retransmit_count = 0;
333 max_retransmit_duration = 0;
337 case DHCP6_STATE_REQUEST:
338 init_retransmit_time = DHCP6_REQ_TIMEOUT;
339 max_retransmit_time = DHCP6_REQ_MAX_RT;
340 max_retransmit_count = DHCP6_REQ_MAX_RC;
341 max_retransmit_duration = 0;
345 case DHCP6_STATE_STOPPED:
347 case DHCP6_STATE_BOUND:
351 if (max_retransmit_count &&
352 client->retransmit_count >= max_retransmit_count) {
353 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
357 r = client_send_message(client);
359 client->retransmit_count++;
362 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
366 if (!client->retransmit_time) {
367 client->retransmit_time =
368 client_timeout_compute_random(init_retransmit_time);
370 if (client->state == DHCP6_STATE_SOLICITATION)
371 client->retransmit_time += init_retransmit_time / 10;
374 if (max_retransmit_time &&
375 client->retransmit_time > max_retransmit_time / 2)
376 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
378 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
381 log_dhcp6_client(client, "Next retransmission in %s",
382 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
383 client->retransmit_time, 0));
385 r = sd_event_add_time(client->event, &client->timeout_resend,
387 time_now + client->retransmit_time,
388 10 * USEC_PER_MSEC, client_timeout_resend,
393 r = sd_event_source_set_priority(client->timeout_resend,
394 client->event_priority);
398 if (max_retransmit_duration && !client->timeout_resend_expire) {
400 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
401 max_retransmit_duration / USEC_PER_SEC);
403 r = sd_event_add_time(client->event,
404 &client->timeout_resend_expire,
406 time_now + max_retransmit_duration,
408 client_timeout_resend_expire, client);
412 r = sd_event_source_set_priority(client->timeout_resend_expire,
413 client->event_priority);
420 client_stop(client, r);
425 static int client_ensure_iaid(sd_dhcp6_client *client) {
426 const char *name = NULL;
431 if (client->ia_na.id)
434 if (detect_container(NULL) <= 0) {
435 /* not in a container, udev will be around */
436 _cleanup_udev_unref_ struct udev *udev;
437 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
438 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
444 sprintf(ifindex_str, "n%d", client->index);
445 device = udev_device_new_from_device_id(udev, ifindex_str);
449 if (udev_device_get_is_initialized(device) <= 0)
453 name = net_get_name(device);
457 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
459 /* fall back to mac address if no predictable name available */
460 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
463 /* fold into 32 bits */
464 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
469 static int client_parse_message(sd_dhcp6_client *client,
470 DHCP6Message *message, size_t len,
471 sd_dhcp6_lease *lease) {
473 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
474 uint16_t optcode, status;
475 size_t optlen, id_len;
476 bool clientid = false;
479 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
482 case DHCP6_OPTION_CLIENTID:
484 log_dhcp6_client(client, "%s contains multiple clientids",
485 dhcp6_message_type_to_string(message->type));
489 if (optlen != sizeof(client->duid) ||
490 memcmp(&client->duid, optval, optlen) != 0) {
491 log_dhcp6_client(client, "%s DUID does not match",
492 dhcp6_message_type_to_string(message->type));
500 case DHCP6_OPTION_SERVERID:
501 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
503 log_dhcp6_client(client, "%s contains multiple serverids",
504 dhcp6_message_type_to_string(message->type));
508 r = dhcp6_lease_set_serverid(lease, optval, optlen);
514 case DHCP6_OPTION_PREFERENCE:
518 r = dhcp6_lease_set_preference(lease, *optval);
524 case DHCP6_OPTION_STATUS_CODE:
528 status = optval[0] << 8 | optval[1];
530 log_dhcp6_client(client, "%s Status %s",
531 dhcp6_message_type_to_string(message->type),
532 dhcp6_message_status_to_string(status));
538 case DHCP6_OPTION_IA_NA:
539 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
541 if (r < 0 && r != -ENOMSG)
544 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
548 if (client->ia_na.id != iaid_lease) {
549 log_dhcp6_client(client, "%s has wrong IAID",
550 dhcp6_message_type_to_string(message->type));
558 if ((r < 0 && r != -ENOMSG) || !clientid) {
559 log_dhcp6_client(client, "%s has incomplete options",
560 dhcp6_message_type_to_string(message->type));
564 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
566 log_dhcp6_client(client, "%s has no server id",
567 dhcp6_message_type_to_string(message->type));
572 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
576 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
578 if (reply->type != DHCP6_REPLY)
581 r = dhcp6_lease_new(&lease);
585 r = client_parse_message(client, reply, len, lease);
589 dhcp6_lease_clear_timers(&client->lease->ia);
591 client->lease = sd_dhcp6_lease_unref(client->lease);
592 client->lease = lease;
595 return DHCP6_STATE_BOUND;
598 static int client_receive_advertise(sd_dhcp6_client *client,
599 DHCP6Message *advertise, size_t len) {
601 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
602 uint8_t pref_advertise = 0, pref_lease = 0;
604 if (advertise->type != DHCP6_ADVERTISE)
607 r = dhcp6_lease_new(&lease);
611 r = client_parse_message(client, advertise, len, lease);
615 r = dhcp6_lease_get_preference(lease, &pref_advertise);
619 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
620 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
621 sd_dhcp6_lease_unref(client->lease);
622 client->lease = lease;
627 if (pref_advertise == 255 || client->retransmit_count > 1)
628 r = DHCP6_STATE_REQUEST;
633 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
635 sd_dhcp6_client *client = userdata;
636 _cleanup_free_ DHCP6Message *message;
641 assert(client->event);
643 r = ioctl(fd, FIONREAD, &buflen);
644 if (r < 0 || buflen <= 0)
645 buflen = DHCP6_MIN_OPTIONS_SIZE;
647 message = malloc0(buflen);
651 len = read(fd, message, buflen);
652 if ((size_t)len < sizeof(DHCP6Message)) {
653 log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
657 switch(message->type) {
665 case DHCP6_INFORMATION_REQUEST:
666 case DHCP6_RELAY_FORW:
667 case DHCP6_RELAY_REPL:
670 case DHCP6_ADVERTISE:
672 case DHCP6_RECONFIGURE:
676 log_dhcp6_client(client, "unknown message type %d",
681 if (client->transaction_id != (message->transaction_id &
682 htobe32(0x00ffffff)))
685 switch (client->state) {
686 case DHCP6_STATE_SOLICITATION:
687 r = client_receive_advertise(client, message, len);
689 if (r == DHCP6_STATE_REQUEST)
690 client_start(client, r);
694 case DHCP6_STATE_REQUEST:
695 r = client_receive_reply(client, message, len);
699 if (r == DHCP6_STATE_BOUND) {
701 r = client_start(client, DHCP6_STATE_BOUND);
703 client_stop(client, r);
707 client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
714 case DHCP6_STATE_BOUND:
718 case DHCP6_STATE_STOPPED:
724 log_dhcp6_client(client, "Recv %s",
725 dhcp6_message_type_to_string(message->type));
731 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
734 usec_t timeout, time_now;
735 char time_string[FORMAT_TIMESPAN_MAX];
737 assert_return(client, -EINVAL);
738 assert_return(client->event, -EINVAL);
739 assert_return(client->index > 0, -EINVAL);
740 assert_return(client->state != state, -EINVAL);
742 client->timeout_resend_expire =
743 sd_event_source_unref(client->timeout_resend_expire);
744 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
745 client->retransmit_time = 0;
746 client->retransmit_count = 0;
749 case DHCP6_STATE_STOPPED:
751 case DHCP6_STATE_SOLICITATION:
753 r = client_ensure_iaid(client);
757 r = dhcp6_network_bind_udp_socket(client->index, NULL);
763 r = sd_event_add_io(client->event, &client->receive_message,
764 client->fd, EPOLLIN, client_receive_message,
769 r = sd_event_source_set_priority(client->receive_message,
770 client->event_priority);
774 client->state = DHCP6_STATE_SOLICITATION;
778 case DHCP6_STATE_REQUEST:
780 client->state = state;
784 case DHCP6_STATE_BOUND:
786 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
790 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
791 client->lease->ia.lifetime_t2 == 0xffffffff) {
793 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
794 be32toh(client->lease->ia.lifetime_t1),
795 be32toh(client->lease->ia.lifetime_t2));
800 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
802 log_dhcp6_client(client, "T1 expires in %s",
803 format_timespan(time_string,
807 r = sd_event_add_time(client->event,
808 &client->lease->ia.timeout_t1,
809 CLOCK_MONOTONIC, time_now + timeout,
810 10 * USEC_PER_SEC, client_timeout_t1,
815 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
816 client->event_priority);
820 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
822 log_dhcp6_client(client, "T2 expires in %s",
823 format_timespan(time_string,
827 r = sd_event_add_time(client->event,
828 &client->lease->ia.timeout_t2,
829 CLOCK_MONOTONIC, time_now + timeout,
830 10 * USEC_PER_SEC, client_timeout_t2,
835 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
836 client->event_priority);
843 client->transaction_id = random_u32() & htobe32(0x00ffffff);
845 r = sd_event_add_time(client->event, &client->timeout_resend,
846 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
851 r = sd_event_source_set_priority(client->timeout_resend,
852 client->event_priority);
859 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
861 client_stop(client, DHCP6_EVENT_STOP);
866 int sd_dhcp6_client_start(sd_dhcp6_client *client)
870 assert_return(client, -EINVAL);
871 assert_return(client->event, -EINVAL);
872 assert_return(client->index > 0, -EINVAL);
874 r = client_reset(client);
878 return client_start(client, DHCP6_STATE_SOLICITATION);
881 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
886 assert_return(client, -EINVAL);
887 assert_return(!client->event, -EBUSY);
890 client->event = sd_event_ref(event);
892 r = sd_event_default(&client->event);
897 client->event_priority = priority;
902 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
903 assert_return(client, -EINVAL);
905 client->event = sd_event_unref(client->event);
910 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
914 return client->event;
917 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
919 assert_se(REFCNT_INC(client->n_ref) >= 2);
924 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
925 if (client && REFCNT_DEC(client->n_ref) <= 0) {
926 client_reset(client);
928 sd_dhcp6_client_detach_event(client);
938 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
939 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
941 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
943 _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
944 sd_id128_t machine_id;
947 assert_return(ret, -EINVAL);
949 client = new0(sd_dhcp6_client, 1);
953 client->n_ref = REFCNT_INIT;
955 client->ia_na.type = DHCP6_OPTION_IA_NA;
961 /* initialize DUID */
962 client->duid.type = htobe16(DHCP6_DUID_EN);
963 client->duid.pen = htobe32(SYSTEMD_PEN);
965 r = sd_id128_get_machine(&machine_id);
969 /* a bit of snake-oil perhaps, but no need to expose the machine-id
971 siphash24(client->duid.id, &machine_id, sizeof(machine_id),