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_initialize(sd_dhcp6_client *client)
158 assert_return(client, -EINVAL);
160 client->receive_message =
161 sd_event_source_unref(client->receive_message);
164 client->fd = safe_close(client->fd);
166 client->transaction_id = 0;
168 client->ia_na.timeout_t1 =
169 sd_event_source_unref(client->ia_na.timeout_t1);
170 client->ia_na.timeout_t2 =
171 sd_event_source_unref(client->ia_na.timeout_t2);
173 client->retransmit_time = 0;
174 client->retransmit_count = 0;
175 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
176 client->timeout_resend_expire =
177 sd_event_source_unref(client->timeout_resend_expire);
179 client->state = DHCP6_STATE_STOPPED;
184 static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
185 assert_return(client, NULL);
187 client = client_notify(client, error);
189 client_initialize(client);
194 static int client_send_message(sd_dhcp6_client *client) {
195 _cleanup_free_ DHCP6Message *message = NULL;
196 struct in6_addr all_servers =
197 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
198 size_t len, optlen = 512;
202 len = sizeof(DHCP6Message) + optlen;
204 message = malloc0(len);
208 opt = (uint8_t *)(message + 1);
210 message->transaction_id = client->transaction_id;
212 switch(client->state) {
213 case DHCP6_STATE_SOLICITATION:
214 message->type = DHCP6_SOLICIT;
216 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
222 case DHCP6_STATE_REQUEST:
223 message->type = DHCP6_REQUEST;
225 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
226 client->lease->serverid_len,
227 client->lease->serverid);
231 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
237 case DHCP6_STATE_STOPPED:
239 case DHCP6_STATE_BOUND:
243 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
244 sizeof(client->duid), &client->duid);
248 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
253 log_dhcp6_client(client, "Sent %s",
254 dhcp6_message_type_to_string(message->type));
259 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
261 sd_dhcp6_client *client = userdata;
263 assert_return(s, -EINVAL);
264 assert_return(client, -EINVAL);
265 assert_return(client->lease, -EINVAL);
267 client->lease->ia.timeout_t2 =
268 sd_event_source_unref(client->lease->ia.timeout_t2);
270 log_dhcp6_client(client, "Timeout T2");
275 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
277 sd_dhcp6_client *client = userdata;
279 assert_return(s, -EINVAL);
280 assert_return(client, -EINVAL);
281 assert_return(client->lease, -EINVAL);
283 client->lease->ia.timeout_t1 =
284 sd_event_source_unref(client->lease->ia.timeout_t1);
286 log_dhcp6_client(client, "Timeout T1");
291 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
293 sd_dhcp6_client *client = userdata;
297 assert(client->event);
299 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
304 static usec_t client_timeout_compute_random(usec_t val) {
305 return val - val / 10 +
306 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
309 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
312 sd_dhcp6_client *client = userdata;
313 usec_t time_now, init_retransmit_time, max_retransmit_time;
314 usec_t max_retransmit_duration;
315 uint8_t max_retransmit_count;
316 char time_string[FORMAT_TIMESPAN_MAX];
320 assert(client->event);
322 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
324 switch (client->state) {
325 case DHCP6_STATE_SOLICITATION:
327 if (client->retransmit_count && client->lease) {
328 client_start(client, DHCP6_STATE_REQUEST);
332 init_retransmit_time = DHCP6_SOL_TIMEOUT;
333 max_retransmit_time = DHCP6_SOL_MAX_RT;
334 max_retransmit_count = 0;
335 max_retransmit_duration = 0;
339 case DHCP6_STATE_REQUEST:
340 init_retransmit_time = DHCP6_REQ_TIMEOUT;
341 max_retransmit_time = DHCP6_REQ_MAX_RT;
342 max_retransmit_count = DHCP6_REQ_MAX_RC;
343 max_retransmit_duration = 0;
347 case DHCP6_STATE_STOPPED:
349 case DHCP6_STATE_BOUND:
353 if (max_retransmit_count &&
354 client->retransmit_count >= max_retransmit_count) {
355 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
359 r = client_send_message(client);
361 client->retransmit_count++;
364 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
368 if (!client->retransmit_time) {
369 client->retransmit_time =
370 client_timeout_compute_random(init_retransmit_time);
372 if (client->state == DHCP6_STATE_SOLICITATION)
373 client->retransmit_time += init_retransmit_time / 10;
376 if (max_retransmit_time &&
377 client->retransmit_time > max_retransmit_time / 2)
378 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
380 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
383 log_dhcp6_client(client, "Next retransmission in %s",
384 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
385 client->retransmit_time, 0));
387 r = sd_event_add_time(client->event, &client->timeout_resend,
389 time_now + client->retransmit_time,
390 10 * USEC_PER_MSEC, client_timeout_resend,
395 r = sd_event_source_set_priority(client->timeout_resend,
396 client->event_priority);
400 if (max_retransmit_duration && !client->timeout_resend_expire) {
402 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
403 max_retransmit_duration / USEC_PER_SEC);
405 r = sd_event_add_time(client->event,
406 &client->timeout_resend_expire,
408 time_now + max_retransmit_duration,
410 client_timeout_resend_expire, client);
414 r = sd_event_source_set_priority(client->timeout_resend_expire,
415 client->event_priority);
422 client_stop(client, r);
427 static int client_ensure_iaid(sd_dhcp6_client *client) {
428 const char *name = NULL;
433 if (client->ia_na.id)
436 if (detect_container(NULL) <= 0) {
437 /* not in a container, udev will be around */
438 _cleanup_udev_unref_ struct udev *udev;
439 _cleanup_udev_device_unref_ struct udev_device *device;
440 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
446 sprintf(ifindex_str, "n%d", client->index);
447 device = udev_device_new_from_device_id(udev, ifindex_str);
451 if (udev_device_get_is_initialized(device) <= 0)
455 name = net_get_name(device);
459 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
461 /* fall back to mac address if no predictable name available */
462 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
465 /* fold into 32 bits */
466 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
471 static int client_parse_message(sd_dhcp6_client *client,
472 DHCP6Message *message, size_t len,
473 sd_dhcp6_lease *lease) {
475 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
476 uint16_t optcode, status;
477 size_t optlen, id_len;
478 bool clientid = false;
481 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
484 case DHCP6_OPTION_CLIENTID:
486 log_dhcp6_client(client, "%s contains multiple clientids",
487 dhcp6_message_type_to_string(message->type));
491 if (optlen != sizeof(client->duid) ||
492 memcmp(&client->duid, optval, optlen) != 0) {
493 log_dhcp6_client(client, "%s DUID does not match",
494 dhcp6_message_type_to_string(message->type));
502 case DHCP6_OPTION_SERVERID:
503 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
505 log_dhcp6_client(client, "%s contains multiple serverids",
506 dhcp6_message_type_to_string(message->type));
510 r = dhcp6_lease_set_serverid(lease, optval, optlen);
516 case DHCP6_OPTION_PREFERENCE:
520 r = dhcp6_lease_set_preference(lease, *optval);
526 case DHCP6_OPTION_STATUS_CODE:
530 status = optval[0] << 8 | optval[1];
532 log_dhcp6_client(client, "%s Status %s",
533 dhcp6_message_type_to_string(message->type),
534 dhcp6_message_status_to_string(status));
540 case DHCP6_OPTION_IA_NA:
541 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
543 if (r < 0 && r != -ENOMSG)
546 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
550 if (client->ia_na.id != iaid_lease) {
551 log_dhcp6_client(client, "%s has wrong IAID",
552 dhcp6_message_type_to_string(message->type));
560 if ((r < 0 && r != -ENOMSG) || !clientid) {
561 log_dhcp6_client(client, "%s has incomplete options",
562 dhcp6_message_type_to_string(message->type));
566 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
568 log_dhcp6_client(client, "%s has no server id",
569 dhcp6_message_type_to_string(message->type));
574 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
578 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
580 if (reply->type != DHCP6_REPLY)
583 r = dhcp6_lease_new(&lease);
587 r = client_parse_message(client, reply, len, lease);
591 dhcp6_lease_clear_timers(&client->lease->ia);
593 client->lease = sd_dhcp6_lease_unref(client->lease);
594 client->lease = lease;
597 return DHCP6_STATE_BOUND;
600 static int client_receive_advertise(sd_dhcp6_client *client,
601 DHCP6Message *advertise, size_t len) {
603 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
604 uint8_t pref_advertise = 0, pref_lease = 0;
606 if (advertise->type != DHCP6_ADVERTISE)
609 r = dhcp6_lease_new(&lease);
613 r = client_parse_message(client, advertise, len, lease);
617 r = dhcp6_lease_get_preference(lease, &pref_advertise);
621 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
622 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
623 sd_dhcp6_lease_unref(client->lease);
624 client->lease = lease;
629 if (pref_advertise == 255 || client->retransmit_count > 1)
630 r = DHCP6_STATE_REQUEST;
635 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
637 sd_dhcp6_client *client = userdata;
638 _cleanup_free_ DHCP6Message *message;
643 assert(client->event);
645 r = ioctl(fd, FIONREAD, &buflen);
646 if (r < 0 || buflen <= 0)
647 buflen = DHCP6_MIN_OPTIONS_SIZE;
649 message = malloc0(buflen);
653 len = read(fd, message, buflen);
654 if ((size_t)len < sizeof(DHCP6Message)) {
655 log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
659 switch(message->type) {
667 case DHCP6_INFORMATION_REQUEST:
668 case DHCP6_RELAY_FORW:
669 case DHCP6_RELAY_REPL:
672 case DHCP6_ADVERTISE:
674 case DHCP6_RECONFIGURE:
678 log_dhcp6_client(client, "unknown message type %d",
683 if (client->transaction_id != (message->transaction_id &
684 htobe32(0x00ffffff)))
687 switch (client->state) {
688 case DHCP6_STATE_SOLICITATION:
689 r = client_receive_advertise(client, message, len);
691 if (r == DHCP6_STATE_REQUEST)
692 client_start(client, r);
696 case DHCP6_STATE_REQUEST:
697 r = client_receive_reply(client, message, len);
701 if (r == DHCP6_STATE_BOUND) {
703 r = client_start(client, DHCP6_STATE_BOUND);
705 client_stop(client, r);
709 client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
716 case DHCP6_STATE_BOUND:
720 case DHCP6_STATE_STOPPED:
726 log_dhcp6_client(client, "Recv %s",
727 dhcp6_message_type_to_string(message->type));
733 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
736 usec_t timeout, time_now;
737 char time_string[FORMAT_TIMESPAN_MAX];
739 assert_return(client, -EINVAL);
740 assert_return(client->event, -EINVAL);
741 assert_return(client->index > 0, -EINVAL);
742 assert_return(client->state != state, -EINVAL);
744 client->timeout_resend_expire =
745 sd_event_source_unref(client->timeout_resend_expire);
746 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
747 client->retransmit_time = 0;
748 client->retransmit_count = 0;
751 case DHCP6_STATE_STOPPED:
753 case DHCP6_STATE_SOLICITATION:
755 r = client_ensure_iaid(client);
759 r = dhcp6_network_bind_udp_socket(client->index, NULL);
765 r = sd_event_add_io(client->event, &client->receive_message,
766 client->fd, EPOLLIN, client_receive_message,
771 r = sd_event_source_set_priority(client->receive_message,
772 client->event_priority);
776 client->state = DHCP6_STATE_SOLICITATION;
780 case DHCP6_STATE_REQUEST:
782 client->state = state;
786 case DHCP6_STATE_BOUND:
788 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
792 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
793 client->lease->ia.lifetime_t2 == 0xffffffff) {
795 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
796 be32toh(client->lease->ia.lifetime_t1),
797 be32toh(client->lease->ia.lifetime_t2));
802 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
804 log_dhcp6_client(client, "T1 expires in %s",
805 format_timespan(time_string,
809 r = sd_event_add_time(client->event,
810 &client->lease->ia.timeout_t1,
811 CLOCK_MONOTONIC, time_now + timeout,
812 10 * USEC_PER_SEC, client_timeout_t1,
817 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
818 client->event_priority);
822 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
824 log_dhcp6_client(client, "T2 expires in %s",
825 format_timespan(time_string,
829 r = sd_event_add_time(client->event,
830 &client->lease->ia.timeout_t2,
831 CLOCK_MONOTONIC, time_now + timeout,
832 10 * USEC_PER_SEC, client_timeout_t2,
837 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
838 client->event_priority);
845 client->transaction_id = random_u32() & htobe32(0x00ffffff);
847 r = sd_event_add_time(client->event, &client->timeout_resend,
848 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
853 r = sd_event_source_set_priority(client->timeout_resend,
854 client->event_priority);
861 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
863 client_stop(client, DHCP6_EVENT_STOP);
868 int sd_dhcp6_client_start(sd_dhcp6_client *client)
872 assert_return(client, -EINVAL);
873 assert_return(client->event, -EINVAL);
874 assert_return(client->index > 0, -EINVAL);
876 r = client_initialize(client);
880 return client_start(client, DHCP6_STATE_SOLICITATION);
883 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
888 assert_return(client, -EINVAL);
889 assert_return(!client->event, -EBUSY);
892 client->event = sd_event_ref(event);
894 r = sd_event_default(&client->event);
899 client->event_priority = priority;
904 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
905 assert_return(client, -EINVAL);
907 client->event = sd_event_unref(client->event);
912 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
916 return client->event;
919 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
921 assert_se(REFCNT_INC(client->n_ref) >= 2);
926 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
927 if (client && REFCNT_DEC(client->n_ref) <= 0) {
928 client_initialize(client);
930 sd_dhcp6_client_detach_event(client);
940 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
941 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
943 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
945 _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
946 sd_id128_t machine_id;
949 assert_return(ret, -EINVAL);
951 client = new0(sd_dhcp6_client, 1);
955 client->n_ref = REFCNT_INIT;
957 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),