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/>.
26 #include "udev-util.h"
28 #include "siphash24.h"
32 #include "network-internal.h"
33 #include "sd-dhcp6-client.h"
34 #include "dhcp6-protocol.h"
35 #include "dhcp6-internal.h"
37 #define SYSTEMD_PEN 43793
38 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
40 struct sd_dhcp6_client {
43 enum DHCP6State state;
47 struct ether_addr mac_addr;
49 be32_t transaction_id;
51 sd_event_source *receive_message;
52 usec_t retransmit_time;
53 uint8_t retransmit_count;
54 sd_event_source *timeout_resend;
55 sd_event_source *timeout_resend_expire;
56 sd_dhcp6_client_cb_t cb;
60 uint16_t type; /* DHCP6_DUID_EN */
66 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
67 [DHCP6_SOLICIT] = "SOLICIT",
68 [DHCP6_ADVERTISE] = "ADVERTISE",
69 [DHCP6_REQUEST] = "REQUEST",
70 [DHCP6_CONFIRM] = "CONFIRM",
71 [DHCP6_RENEW] = "RENEW",
72 [DHCP6_REBIND] = "REBIND",
73 [DHCP6_REPLY] = "REPLY",
74 [DHCP6_RELEASE] = "RELEASE",
75 [DHCP6_DECLINE] = "DECLINE",
76 [DHCP6_RECONFIGURE] = "RECONFIGURE",
77 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
78 [DHCP6_RELAY_FORW] = "RELAY-FORW",
79 [DHCP6_RELAY_REPL] = "RELAY-REPL",
82 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
84 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
85 sd_dhcp6_client_cb_t cb, void *userdata)
87 assert_return(client, -EINVAL);
90 client->userdata = userdata;
95 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
97 assert_return(client, -EINVAL);
98 assert_return(interface_index >= -1, -EINVAL);
100 client->index = interface_index;
105 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
106 const struct ether_addr *mac_addr)
108 assert_return(client, -EINVAL);
111 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
113 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
118 static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
120 client = sd_dhcp6_client_ref(client);
121 client->cb(client, event, client->userdata);
122 client = sd_dhcp6_client_unref(client);
128 static int client_initialize(sd_dhcp6_client *client)
130 assert_return(client, -EINVAL);
132 client->receive_message =
133 sd_event_source_unref(client->receive_message);
136 client->fd = safe_close(client->fd);
138 client->transaction_id = random_u32() & 0x00ffffff;
140 client->ia_na.timeout_t1 =
141 sd_event_source_unref(client->ia_na.timeout_t1);
142 client->ia_na.timeout_t2 =
143 sd_event_source_unref(client->ia_na.timeout_t2);
145 client->retransmit_time = 0;
146 client->retransmit_count = 0;
147 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
148 client->timeout_resend_expire =
149 sd_event_source_unref(client->timeout_resend_expire);
151 client->state = DHCP6_STATE_STOPPED;
156 static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
157 assert_return(client, NULL);
159 client = client_notify(client, error);
161 client_initialize(client);
166 static int client_send_message(sd_dhcp6_client *client) {
167 _cleanup_free_ DHCP6Message *message = NULL;
168 struct in6_addr all_servers =
169 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
170 size_t len, optlen = 512;
174 len = sizeof(DHCP6Message) + optlen;
176 message = malloc0(len);
180 opt = (uint8_t *)(message + 1);
182 message->transaction_id = client->transaction_id;
184 switch(client->state) {
185 case DHCP6_STATE_SOLICITATION:
186 message->type = DHCP6_SOLICIT;
188 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
189 sizeof(client->duid), &client->duid);
193 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
199 case DHCP6_STATE_STOPPED:
204 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
209 log_dhcp6_client(client, "Sent %s",
210 dhcp6_message_type_to_string(message->type));
215 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
217 sd_dhcp6_client *client = userdata;
221 assert(client->event);
223 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
228 static usec_t client_timeout_compute_random(usec_t val) {
229 return val - val / 10 +
230 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
233 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
236 sd_dhcp6_client *client = userdata;
237 usec_t time_now, init_retransmit_time, max_retransmit_time;
238 usec_t max_retransmit_duration;
239 uint8_t max_retransmit_count;
240 char time_string[FORMAT_TIMESPAN_MAX];
244 assert(client->event);
246 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
248 switch (client->state) {
249 case DHCP6_STATE_SOLICITATION:
250 init_retransmit_time = DHCP6_SOL_TIMEOUT;
251 max_retransmit_time = DHCP6_SOL_MAX_RT;
252 max_retransmit_count = 0;
253 max_retransmit_duration = 0;
257 case DHCP6_STATE_STOPPED:
262 if (max_retransmit_count &&
263 client->retransmit_count >= max_retransmit_count) {
264 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
268 r = client_send_message(client);
270 client->retransmit_count++;
273 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
277 if (!client->retransmit_time) {
278 client->retransmit_time =
279 client_timeout_compute_random(init_retransmit_time);
281 if (client->state == DHCP6_STATE_SOLICITATION)
282 client->retransmit_time += init_retransmit_time / 10;
285 if (max_retransmit_time &&
286 client->retransmit_time > max_retransmit_time / 2)
287 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
289 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
292 log_dhcp6_client(client, "Next retransmission in %s",
293 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
294 client->retransmit_time, 0));
296 r = sd_event_add_time(client->event, &client->timeout_resend,
298 time_now + client->retransmit_time,
299 10 * USEC_PER_MSEC, client_timeout_resend,
304 r = sd_event_source_set_priority(client->timeout_resend,
305 client->event_priority);
309 if (max_retransmit_duration && !client->timeout_resend_expire) {
311 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
312 max_retransmit_duration / USEC_PER_SEC);
314 r = sd_event_add_time(client->event,
315 &client->timeout_resend_expire,
317 time_now + max_retransmit_duration,
319 client_timeout_resend_expire, client);
323 r = sd_event_source_set_priority(client->timeout_resend_expire,
324 client->event_priority);
331 client_stop(client, r);
336 static int client_ensure_iaid(sd_dhcp6_client *client) {
337 const char *name = NULL;
342 if (client->ia_na.id)
345 if (detect_container(NULL) <= 0) {
346 /* not in a container, udev will be around */
347 _cleanup_udev_unref_ struct udev *udev;
348 _cleanup_udev_device_unref_ struct udev_device *device;
349 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
355 sprintf(ifindex_str, "n%d", client->index);
356 device = udev_device_new_from_device_id(udev, ifindex_str);
360 if (udev_device_get_is_initialized(device) <= 0)
364 name = net_get_name(device);
368 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
370 /* fall back to mac address if no predictable name available */
371 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
374 /* fold into 32 bits */
375 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
380 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
386 static int client_start(sd_dhcp6_client *client)
390 assert_return(client, -EINVAL);
391 assert_return(client->event, -EINVAL);
392 assert_return(client->index > 0, -EINVAL);
394 r = client_ensure_iaid(client);
398 r = dhcp6_network_bind_udp_socket(client->index, NULL);
404 r = sd_event_add_io(client->event, &client->receive_message,
405 client->fd, EPOLLIN, client_receive_message,
410 r = sd_event_source_set_priority(client->receive_message,
411 client->event_priority);
415 client->state = DHCP6_STATE_SOLICITATION;
417 r = sd_event_add_time(client->event, &client->timeout_resend,
418 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
423 r = sd_event_source_set_priority(client->timeout_resend,
424 client->event_priority);
431 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
433 client_stop(client, DHCP6_EVENT_STOP);
438 int sd_dhcp6_client_start(sd_dhcp6_client *client)
442 assert_return(client, -EINVAL);
443 assert_return(client->event, -EINVAL);
444 assert_return(client->index > 0, -EINVAL);
446 r = client_initialize(client);
450 return client_start(client);
453 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
458 assert_return(client, -EINVAL);
459 assert_return(!client->event, -EBUSY);
462 client->event = sd_event_ref(event);
464 r = sd_event_default(&client->event);
469 client->event_priority = priority;
474 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
475 assert_return(client, -EINVAL);
477 client->event = sd_event_unref(client->event);
482 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
486 return client->event;
489 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
491 assert_se(REFCNT_INC(client->n_ref) >= 2);
496 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
497 if (client && REFCNT_DEC(client->n_ref) <= 0) {
498 client_initialize(client);
500 sd_dhcp6_client_detach_event(client);
510 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
511 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
513 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
515 _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
516 sd_id128_t machine_id;
519 assert_return(ret, -EINVAL);
521 client = new0(sd_dhcp6_client, 1);
525 client->n_ref = REFCNT_INIT;
527 client->ia_na.type = DHCP6_OPTION_IA_NA;
531 /* initialize DUID */
532 client->duid.type = htobe16(DHCP6_DUID_EN);
533 client->duid.pen = htobe32(SYSTEMD_PEN);
535 r = sd_id128_get_machine(&machine_id);
539 /* a bit of snake-oil perhaps, but no need to expose the machine-id
541 siphash24(client->duid.id, &machine_id, sizeof(machine_id),