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:
242 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
243 sizeof(client->duid), &client->duid);
247 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
252 log_dhcp6_client(client, "Sent %s",
253 dhcp6_message_type_to_string(message->type));
258 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
260 sd_dhcp6_client *client = userdata;
264 assert(client->event);
266 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
271 static usec_t client_timeout_compute_random(usec_t val) {
272 return val - val / 10 +
273 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
276 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
279 sd_dhcp6_client *client = userdata;
280 usec_t time_now, init_retransmit_time, max_retransmit_time;
281 usec_t max_retransmit_duration;
282 uint8_t max_retransmit_count;
283 char time_string[FORMAT_TIMESPAN_MAX];
287 assert(client->event);
289 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
291 switch (client->state) {
292 case DHCP6_STATE_SOLICITATION:
294 if (client->retransmit_count && client->lease) {
295 client_start(client, DHCP6_STATE_REQUEST);
299 init_retransmit_time = DHCP6_SOL_TIMEOUT;
300 max_retransmit_time = DHCP6_SOL_MAX_RT;
301 max_retransmit_count = 0;
302 max_retransmit_duration = 0;
306 case DHCP6_STATE_REQUEST:
307 init_retransmit_time = DHCP6_REQ_TIMEOUT;
308 max_retransmit_time = DHCP6_REQ_MAX_RT;
309 max_retransmit_count = DHCP6_REQ_MAX_RC;
310 max_retransmit_duration = 0;
314 case DHCP6_STATE_STOPPED:
319 if (max_retransmit_count &&
320 client->retransmit_count >= max_retransmit_count) {
321 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
325 r = client_send_message(client);
327 client->retransmit_count++;
330 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
334 if (!client->retransmit_time) {
335 client->retransmit_time =
336 client_timeout_compute_random(init_retransmit_time);
338 if (client->state == DHCP6_STATE_SOLICITATION)
339 client->retransmit_time += init_retransmit_time / 10;
342 if (max_retransmit_time &&
343 client->retransmit_time > max_retransmit_time / 2)
344 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
346 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
349 log_dhcp6_client(client, "Next retransmission in %s",
350 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
351 client->retransmit_time, 0));
353 r = sd_event_add_time(client->event, &client->timeout_resend,
355 time_now + client->retransmit_time,
356 10 * USEC_PER_MSEC, client_timeout_resend,
361 r = sd_event_source_set_priority(client->timeout_resend,
362 client->event_priority);
366 if (max_retransmit_duration && !client->timeout_resend_expire) {
368 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
369 max_retransmit_duration / USEC_PER_SEC);
371 r = sd_event_add_time(client->event,
372 &client->timeout_resend_expire,
374 time_now + max_retransmit_duration,
376 client_timeout_resend_expire, client);
380 r = sd_event_source_set_priority(client->timeout_resend_expire,
381 client->event_priority);
388 client_stop(client, r);
393 static int client_ensure_iaid(sd_dhcp6_client *client) {
394 const char *name = NULL;
399 if (client->ia_na.id)
402 if (detect_container(NULL) <= 0) {
403 /* not in a container, udev will be around */
404 _cleanup_udev_unref_ struct udev *udev;
405 _cleanup_udev_device_unref_ struct udev_device *device;
406 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
412 sprintf(ifindex_str, "n%d", client->index);
413 device = udev_device_new_from_device_id(udev, ifindex_str);
417 if (udev_device_get_is_initialized(device) <= 0)
421 name = net_get_name(device);
425 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
427 /* fall back to mac address if no predictable name available */
428 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
431 /* fold into 32 bits */
432 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
437 static int client_parse_message(sd_dhcp6_client *client,
438 DHCP6Message *message, size_t len,
439 sd_dhcp6_lease *lease) {
441 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
442 uint16_t optcode, status;
443 size_t optlen, id_len;
444 bool clientid = false;
447 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
450 case DHCP6_OPTION_CLIENTID:
452 log_dhcp6_client(client, "%s contains multiple clientids",
453 dhcp6_message_type_to_string(message->type));
457 if (optlen != sizeof(client->duid) ||
458 memcmp(&client->duid, optval, optlen) != 0) {
459 log_dhcp6_client(client, "%s DUID does not match",
460 dhcp6_message_type_to_string(message->type));
468 case DHCP6_OPTION_SERVERID:
469 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
471 log_dhcp6_client(client, "%s contains multiple serverids",
472 dhcp6_message_type_to_string(message->type));
476 r = dhcp6_lease_set_serverid(lease, optval, optlen);
482 case DHCP6_OPTION_PREFERENCE:
486 r = dhcp6_lease_set_preference(lease, *optval);
492 case DHCP6_OPTION_STATUS_CODE:
496 status = optval[0] << 8 | optval[1];
498 log_dhcp6_client(client, "%s Status %s",
499 dhcp6_message_type_to_string(message->type),
500 dhcp6_message_status_to_string(status));
506 case DHCP6_OPTION_IA_NA:
507 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
509 if (r < 0 && r != -ENOMSG)
512 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
516 if (client->ia_na.id != iaid_lease) {
517 log_dhcp6_client(client, "%s has wrong IAID",
518 dhcp6_message_type_to_string(message->type));
526 if ((r < 0 && r != -ENOMSG) || !clientid) {
527 log_dhcp6_client(client, "%s has incomplete options",
528 dhcp6_message_type_to_string(message->type));
532 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
534 log_dhcp6_client(client, "%s has no server id",
535 dhcp6_message_type_to_string(message->type));
540 static int client_receive_advertise(sd_dhcp6_client *client,
541 DHCP6Message *advertise, size_t len) {
543 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
544 uint8_t pref_advertise = 0, pref_lease = 0;
546 if (advertise->type != DHCP6_ADVERTISE)
549 r = dhcp6_lease_new(&lease);
553 r = client_parse_message(client, advertise, len, lease);
557 r = dhcp6_lease_get_preference(lease, &pref_advertise);
561 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
562 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
563 sd_dhcp6_lease_unref(client->lease);
564 client->lease = lease;
569 if (pref_advertise == 255 || client->retransmit_count > 1)
570 r = DHCP6_STATE_REQUEST;
575 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
577 sd_dhcp6_client *client = userdata;
578 _cleanup_free_ DHCP6Message *message;
583 assert(client->event);
585 r = ioctl(fd, FIONREAD, &buflen);
586 if (r < 0 || buflen <= 0)
587 buflen = DHCP6_MIN_OPTIONS_SIZE;
589 message = malloc0(buflen);
593 len = read(fd, message, buflen);
594 if ((size_t)len < sizeof(DHCP6Message)) {
595 log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
599 switch(message->type) {
607 case DHCP6_INFORMATION_REQUEST:
608 case DHCP6_RELAY_FORW:
609 case DHCP6_RELAY_REPL:
612 case DHCP6_ADVERTISE:
614 case DHCP6_RECONFIGURE:
618 log_dhcp6_client(client, "unknown message type %d",
623 if (client->transaction_id != (message->transaction_id &
624 htobe32(0x00ffffff)))
627 switch (client->state) {
628 case DHCP6_STATE_SOLICITATION:
629 r = client_receive_advertise(client, message, len);
631 if (r == DHCP6_STATE_REQUEST)
632 client_start(client, r);
636 case DHCP6_STATE_REQUEST:
637 case DHCP6_STATE_STOPPED:
643 log_dhcp6_client(client, "Recv %s",
644 dhcp6_message_type_to_string(message->type));
650 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
654 assert_return(client, -EINVAL);
655 assert_return(client->event, -EINVAL);
656 assert_return(client->index > 0, -EINVAL);
657 assert_return(client->state != state, -EINVAL);
659 client->timeout_resend_expire =
660 sd_event_source_unref(client->timeout_resend_expire);
661 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
662 client->retransmit_time = 0;
663 client->retransmit_count = 0;
666 case DHCP6_STATE_STOPPED:
668 case DHCP6_STATE_SOLICITATION:
670 r = client_ensure_iaid(client);
674 r = dhcp6_network_bind_udp_socket(client->index, NULL);
680 r = sd_event_add_io(client->event, &client->receive_message,
681 client->fd, EPOLLIN, client_receive_message,
686 r = sd_event_source_set_priority(client->receive_message,
687 client->event_priority);
691 client->state = DHCP6_STATE_SOLICITATION;
695 case DHCP6_STATE_REQUEST:
696 client->state = state;
701 client->transaction_id = random_u32() & htobe32(0x00ffffff);
703 r = sd_event_add_time(client->event, &client->timeout_resend,
704 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
709 r = sd_event_source_set_priority(client->timeout_resend,
710 client->event_priority);
717 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
719 client_stop(client, DHCP6_EVENT_STOP);
724 int sd_dhcp6_client_start(sd_dhcp6_client *client)
728 assert_return(client, -EINVAL);
729 assert_return(client->event, -EINVAL);
730 assert_return(client->index > 0, -EINVAL);
732 r = client_initialize(client);
736 return client_start(client, DHCP6_STATE_SOLICITATION);
739 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
744 assert_return(client, -EINVAL);
745 assert_return(!client->event, -EBUSY);
748 client->event = sd_event_ref(event);
750 r = sd_event_default(&client->event);
755 client->event_priority = priority;
760 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
761 assert_return(client, -EINVAL);
763 client->event = sd_event_unref(client->event);
768 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
772 return client->event;
775 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
777 assert_se(REFCNT_INC(client->n_ref) >= 2);
782 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
783 if (client && REFCNT_DEC(client->n_ref) <= 0) {
784 client_initialize(client);
786 sd_dhcp6_client_detach_event(client);
796 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
797 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
799 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
801 _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
802 sd_id128_t machine_id;
805 assert_return(ret, -EINVAL);
807 client = new0(sd_dhcp6_client, 1);
811 client->n_ref = REFCNT_INIT;
813 client->ia_na.type = DHCP6_OPTION_IA_NA;
817 /* initialize DUID */
818 client->duid.type = htobe16(DHCP6_DUID_EN);
819 client->duid.pen = htobe32(SYSTEMD_PEN);
821 r = sd_id128_get_machine(&machine_id);
825 /* a bit of snake-oil perhaps, but no need to expose the machine-id
827 siphash24(client->duid.id, &machine_id, sizeof(machine_id),