]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
udev - drop print_kmsg
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
CommitLineData
139b011a
PF
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright (C) 2014 Intel Corporation. All rights reserved.
7
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.
12
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.
17
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/>.
20***/
21
22#include <errno.h>
23#include <string.h>
631bbe71 24#include <sys/ioctl.h>
139b011a 25
f12abb48
PF
26#include "udev.h"
27#include "udev-util.h"
28#include "virt.h"
a276e6d6 29#include "siphash24.h"
139b011a
PF
30#include "util.h"
31#include "refcnt.h"
32
f12abb48 33#include "network-internal.h"
139b011a
PF
34#include "sd-dhcp6-client.h"
35#include "dhcp6-protocol.h"
f12abb48 36#include "dhcp6-internal.h"
631bbe71 37#include "dhcp6-lease-internal.h"
139b011a 38
a276e6d6
TG
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)
41
139b011a
PF
42struct sd_dhcp6_client {
43 RefCount n_ref;
44
45 enum DHCP6State state;
46 sd_event *event;
47 int event_priority;
48 int index;
49 struct ether_addr mac_addr;
f12abb48 50 DHCP6IA ia_na;
a9aff361 51 be32_t transaction_id;
631bbe71 52 struct sd_dhcp6_lease *lease;
a9aff361 53 int fd;
da6fe470
PF
54 be16_t *req_opts;
55 size_t req_opts_allocated;
56 size_t req_opts_len;
a9aff361 57 sd_event_source *receive_message;
d1b0afe3
PF
58 usec_t retransmit_time;
59 uint8_t retransmit_count;
60 sd_event_source *timeout_resend;
61 sd_event_source *timeout_resend_expire;
139b011a
PF
62 sd_dhcp6_client_cb_t cb;
63 void *userdata;
a276e6d6
TG
64
65 struct duid_en {
66 uint16_t type; /* DHCP6_DUID_EN */
67 uint32_t pen;
68 uint8_t id[8];
69 } _packed_ duid;
139b011a
PF
70};
71
da6fe470
PF
72static const uint16_t default_req_opts[] = {
73 DHCP6_OPTION_DNS_SERVERS,
74 DHCP6_OPTION_DOMAIN_LIST,
75 DHCP6_OPTION_NTP_SERVER,
76};
77
a9aff361
PF
78const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
79 [DHCP6_SOLICIT] = "SOLICIT",
80 [DHCP6_ADVERTISE] = "ADVERTISE",
81 [DHCP6_REQUEST] = "REQUEST",
82 [DHCP6_CONFIRM] = "CONFIRM",
83 [DHCP6_RENEW] = "RENEW",
84 [DHCP6_REBIND] = "REBIND",
85 [DHCP6_REPLY] = "REPLY",
86 [DHCP6_RELEASE] = "RELEASE",
87 [DHCP6_DECLINE] = "DECLINE",
88 [DHCP6_RECONFIGURE] = "RECONFIGURE",
89 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
90 [DHCP6_RELAY_FORW] = "RELAY-FORW",
91 [DHCP6_RELAY_REPL] = "RELAY-REPL",
92};
93
94DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
95
631bbe71
PF
96const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
97 [DHCP6_STATUS_SUCCESS] = "Success",
98 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
99 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
100 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
101 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
102 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
103};
104
105DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
106
3f0c075f
PF
107DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
108#define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
109
110#define DHCP6_CLIENT_DONT_DESTROY(client) \
111 _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
112
c3e2adea
PF
113static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
114
139b011a
PF
115int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
116 sd_dhcp6_client_cb_t cb, void *userdata)
117{
118 assert_return(client, -EINVAL);
119
120 client->cb = cb;
121 client->userdata = userdata;
122
123 return 0;
124}
125
126int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
127{
128 assert_return(client, -EINVAL);
129 assert_return(interface_index >= -1, -EINVAL);
130
131 client->index = interface_index;
132
133 return 0;
134}
135
136int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
137 const struct ether_addr *mac_addr)
138{
139 assert_return(client, -EINVAL);
140
141 if (mac_addr)
142 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
143 else
144 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
145
146 return 0;
147}
148
da6fe470
PF
149int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
150 uint16_t option) {
151 size_t t;
152
153 assert_return(client, -EINVAL);
154 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
155
156 switch(option) {
157 case DHCP6_OPTION_DNS_SERVERS:
158 case DHCP6_OPTION_DOMAIN_LIST:
159 case DHCP6_OPTION_SNTP_SERVERS:
160 case DHCP6_OPTION_NTP_SERVER:
161 break;
162
163 default:
164 return -EINVAL;
165 }
166
167 for (t = 0; t < client->req_opts_len; t++)
168 if (client->req_opts[t] == htobe16(option))
169 return -EEXIST;
170
171 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
172 client->req_opts_len + 1))
173 return -ENOMEM;
174
175 client->req_opts[client->req_opts_len++] = htobe16(option);
176
177 return 0;
178}
179
ea3b3a75
PF
180int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
181 assert_return(client, -EINVAL);
182 assert_return(ret, -EINVAL);
183
184 if (!client->lease)
185 return -ENOMSG;
186
187 *ret = sd_dhcp6_lease_ref(client->lease);
188
189 return 0;
190}
191
3f0c075f
PF
192static void client_notify(sd_dhcp6_client *client, int event) {
193 if (client->cb)
139b011a 194 client->cb(client, event, client->userdata);
139b011a
PF
195}
196
c806ffb9 197static int client_reset(sd_dhcp6_client *client) {
139b011a
PF
198 assert_return(client, -EINVAL);
199
a9aff361
PF
200 client->receive_message =
201 sd_event_source_unref(client->receive_message);
202
c806ffb9 203 client->fd = safe_close(client->fd);
a9aff361 204
c3e2adea 205 client->transaction_id = 0;
a9aff361 206
f12abb48
PF
207 client->ia_na.timeout_t1 =
208 sd_event_source_unref(client->ia_na.timeout_t1);
209 client->ia_na.timeout_t2 =
210 sd_event_source_unref(client->ia_na.timeout_t2);
211
d1b0afe3
PF
212 client->retransmit_time = 0;
213 client->retransmit_count = 0;
214 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
215 client->timeout_resend_expire =
216 sd_event_source_unref(client->timeout_resend_expire);
217
139b011a
PF
218 client->state = DHCP6_STATE_STOPPED;
219
220 return 0;
221}
222
3f0c075f
PF
223static void client_stop(sd_dhcp6_client *client, int error) {
224 DHCP6_CLIENT_DONT_DESTROY(client);
139b011a 225
3f0c075f 226 assert(client);
139b011a 227
3f0c075f
PF
228 client_notify(client, error);
229
230 client_reset(client);
139b011a
PF
231}
232
a9aff361
PF
233static int client_send_message(sd_dhcp6_client *client) {
234 _cleanup_free_ DHCP6Message *message = NULL;
235 struct in6_addr all_servers =
236 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
237 size_t len, optlen = 512;
238 uint8_t *opt;
239 int r;
240
241 len = sizeof(DHCP6Message) + optlen;
242
243 message = malloc0(len);
244 if (!message)
245 return -ENOMEM;
246
247 opt = (uint8_t *)(message + 1);
248
249 message->transaction_id = client->transaction_id;
250
251 switch(client->state) {
252 case DHCP6_STATE_SOLICITATION:
253 message->type = DHCP6_SOLICIT;
254
ed6ee219
PF
255 r = dhcp6_option_append(&opt, &optlen,
256 DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
926695f1
TA
257 if (r < 0)
258 return r;
ed6ee219 259
7246333c 260 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
a9aff361
PF
261 if (r < 0)
262 return r;
263
7246333c
PF
264 break;
265
266 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
267 case DHCP6_STATE_RENEW:
268
269 if (client->state == DHCP6_STATE_REQUEST)
270 message->type = DHCP6_REQUEST;
271 else
272 message->type = DHCP6_RENEW;
7246333c
PF
273
274 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
275 client->lease->serverid_len,
276 client->lease->serverid);
277 if (r < 0)
278 return r;
279
280 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
a9aff361
PF
281 if (r < 0)
282 return r;
283
284 break;
285
3dc34fcc
PF
286 case DHCP6_STATE_REBIND:
287 message->type = DHCP6_REBIND;
288
289 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
290 if (r < 0)
291 return r;
292
293 break;
294
a9aff361 295 case DHCP6_STATE_STOPPED:
a34b57c0 296 case DHCP6_STATE_BOUND:
a9aff361
PF
297 return -EINVAL;
298 }
299
da6fe470
PF
300 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
301 client->req_opts_len * sizeof(be16_t),
302 client->req_opts);
303 if (r < 0)
304 return r;
305
7246333c
PF
306 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
307 sizeof(client->duid), &client->duid);
308 if (r < 0)
309 return r;
310
a9aff361
PF
311 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
312 len - optlen);
313 if (r < 0)
314 return r;
315
316 log_dhcp6_client(client, "Sent %s",
317 dhcp6_message_type_to_string(message->type));
318
319 return 0;
320}
321
a34b57c0
PF
322static int client_timeout_t2(sd_event_source *s, uint64_t usec,
323 void *userdata) {
324 sd_dhcp6_client *client = userdata;
325
326 assert_return(s, -EINVAL);
327 assert_return(client, -EINVAL);
328 assert_return(client->lease, -EINVAL);
329
330 client->lease->ia.timeout_t2 =
331 sd_event_source_unref(client->lease->ia.timeout_t2);
332
333 log_dhcp6_client(client, "Timeout T2");
334
3dc34fcc
PF
335 client_start(client, DHCP6_STATE_REBIND);
336
a34b57c0
PF
337 return 0;
338}
339
340static int client_timeout_t1(sd_event_source *s, uint64_t usec,
341 void *userdata) {
342 sd_dhcp6_client *client = userdata;
343
344 assert_return(s, -EINVAL);
345 assert_return(client, -EINVAL);
346 assert_return(client->lease, -EINVAL);
347
348 client->lease->ia.timeout_t1 =
349 sd_event_source_unref(client->lease->ia.timeout_t1);
350
351 log_dhcp6_client(client, "Timeout T1");
352
3dc34fcc
PF
353 client_start(client, DHCP6_STATE_RENEW);
354
a34b57c0
PF
355 return 0;
356}
357
d1b0afe3
PF
358static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
359 void *userdata) {
360 sd_dhcp6_client *client = userdata;
3dc34fcc
PF
361 DHCP6_CLIENT_DONT_DESTROY(client);
362 enum DHCP6State state;
d1b0afe3
PF
363
364 assert(s);
365 assert(client);
366 assert(client->event);
367
3dc34fcc
PF
368 state = client->state;
369
d1b0afe3
PF
370 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
371
3dc34fcc
PF
372 /* RFC 3315, section 18.1.4., says that "...the client may choose to
373 use a Solicit message to locate a new DHCP server..." */
374 if (state == DHCP6_STATE_REBIND)
375 client_start(client, DHCP6_STATE_SOLICITATION);
376
d1b0afe3
PF
377 return 0;
378}
379
380static usec_t client_timeout_compute_random(usec_t val) {
381 return val - val / 10 +
382 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
383}
384
385static int client_timeout_resend(sd_event_source *s, uint64_t usec,
386 void *userdata) {
387 int r = 0;
388 sd_dhcp6_client *client = userdata;
4b4923e6 389 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
5e913450 390 usec_t max_retransmit_duration = 0;
513a6fa8 391 uint8_t max_retransmit_count = 0;
d1b0afe3 392 char time_string[FORMAT_TIMESPAN_MAX];
3dc34fcc 393 uint32_t expire = 0;
d1b0afe3
PF
394
395 assert(s);
396 assert(client);
397 assert(client->event);
398
399 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
400
401 switch (client->state) {
402 case DHCP6_STATE_SOLICITATION:
7246333c
PF
403
404 if (client->retransmit_count && client->lease) {
405 client_start(client, DHCP6_STATE_REQUEST);
406 return 0;
407 }
408
d1b0afe3
PF
409 init_retransmit_time = DHCP6_SOL_TIMEOUT;
410 max_retransmit_time = DHCP6_SOL_MAX_RT;
d1b0afe3
PF
411
412 break;
413
7246333c
PF
414 case DHCP6_STATE_REQUEST:
415 init_retransmit_time = DHCP6_REQ_TIMEOUT;
416 max_retransmit_time = DHCP6_REQ_MAX_RT;
417 max_retransmit_count = DHCP6_REQ_MAX_RC;
7246333c
PF
418
419 break;
420
3dc34fcc
PF
421 case DHCP6_STATE_RENEW:
422 init_retransmit_time = DHCP6_REN_TIMEOUT;
423 max_retransmit_time = DHCP6_REN_MAX_RT;
3dc34fcc
PF
424
425 /* RFC 3315, section 18.1.3. says max retransmit duration will
426 be the remaining time until T2. Instead of setting MRD,
427 wait for T2 to trigger with the same end result */
3dc34fcc
PF
428
429 break;
430
431 case DHCP6_STATE_REBIND:
432 init_retransmit_time = DHCP6_REB_TIMEOUT;
433 max_retransmit_time = DHCP6_REB_MAX_RT;
3dc34fcc
PF
434
435 if (!client->timeout_resend_expire) {
436 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
437 &expire);
438 if (r < 0) {
439 client_stop(client, r);
440 return 0;
441 }
442 max_retransmit_duration = expire * USEC_PER_SEC;
443 }
444
445 break;
446
d1b0afe3 447 case DHCP6_STATE_STOPPED:
a34b57c0 448 case DHCP6_STATE_BOUND:
d1b0afe3
PF
449 return 0;
450 }
451
452 if (max_retransmit_count &&
453 client->retransmit_count >= max_retransmit_count) {
454 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
455 return 0;
456 }
457
a9aff361
PF
458 r = client_send_message(client);
459 if (r >= 0)
460 client->retransmit_count++;
461
462
fa94c34b 463 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d1b0afe3
PF
464 if (r < 0)
465 goto error;
466
467 if (!client->retransmit_time) {
468 client->retransmit_time =
469 client_timeout_compute_random(init_retransmit_time);
a9aff361
PF
470
471 if (client->state == DHCP6_STATE_SOLICITATION)
472 client->retransmit_time += init_retransmit_time / 10;
473
d1b0afe3
PF
474 } else {
475 if (max_retransmit_time &&
476 client->retransmit_time > max_retransmit_time / 2)
477 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
478 else
479 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
480 }
481
482 log_dhcp6_client(client, "Next retransmission in %s",
483 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
484 client->retransmit_time, 0));
485
486 r = sd_event_add_time(client->event, &client->timeout_resend,
fa94c34b 487 clock_boottime_or_monotonic(),
d1b0afe3
PF
488 time_now + client->retransmit_time,
489 10 * USEC_PER_MSEC, client_timeout_resend,
490 client);
491 if (r < 0)
492 goto error;
493
494 r = sd_event_source_set_priority(client->timeout_resend,
495 client->event_priority);
496 if (r < 0)
497 goto error;
498
9021bb9f
TG
499 r = sd_event_source_set_name(client->timeout_resend,
500 "dhcp6-resend-timer");
501 if (r < 0)
502 goto error;
503
d1b0afe3
PF
504 if (max_retransmit_duration && !client->timeout_resend_expire) {
505
506 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
507 max_retransmit_duration / USEC_PER_SEC);
508
509 r = sd_event_add_time(client->event,
510 &client->timeout_resend_expire,
fa94c34b 511 clock_boottime_or_monotonic(),
d1b0afe3
PF
512 time_now + max_retransmit_duration,
513 USEC_PER_SEC,
514 client_timeout_resend_expire, client);
515 if (r < 0)
516 goto error;
517
518 r = sd_event_source_set_priority(client->timeout_resend_expire,
519 client->event_priority);
520 if (r < 0)
521 goto error;
9021bb9f
TG
522
523 r = sd_event_source_set_name(client->timeout_resend_expire,
524 "dhcp6-resend-expire-timer");
525 if (r < 0)
526 goto error;
d1b0afe3
PF
527 }
528
529error:
530 if (r < 0)
531 client_stop(client, r);
532
533 return 0;
534}
535
f12abb48 536static int client_ensure_iaid(sd_dhcp6_client *client) {
54d61deb
TG
537 /* name is a pointer to memory in the udev_device struct, so must
538 have the same scope */
539 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
f12abb48
PF
540 const char *name = NULL;
541 uint64_t id;
542
543 assert(client);
544
545 if (client->ia_na.id)
546 return 0;
547
548 if (detect_container(NULL) <= 0) {
549 /* not in a container, udev will be around */
550 _cleanup_udev_unref_ struct udev *udev;
f12abb48
PF
551 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
552
553 udev = udev_new();
554 if (!udev)
555 return -ENOMEM;
556
557 sprintf(ifindex_str, "n%d", client->index);
558 device = udev_device_new_from_device_id(udev, ifindex_str);
559 if (!device)
560 return -errno;
561
562 if (udev_device_get_is_initialized(device) <= 0)
563 /* not yet ready */
564 return -EBUSY;
565
566 name = net_get_name(device);
567 }
568
569 if (name)
570 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
571 else
572 /* fall back to mac address if no predictable name available */
573 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
574 HASH_KEY.bytes);
575
576 /* fold into 32 bits */
577 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
578
579 return 0;
580}
581
631bbe71
PF
582static int client_parse_message(sd_dhcp6_client *client,
583 DHCP6Message *message, size_t len,
584 sd_dhcp6_lease *lease) {
585 int r;
44481a8b 586 uint8_t *optval, *option, *id = NULL;
631bbe71
PF
587 uint16_t optcode, status;
588 size_t optlen, id_len;
589 bool clientid = false;
590 be32_t iaid_lease;
591
44481a8b
ZJS
592 option = (uint8_t *)message + sizeof(DHCP6Message);
593 len -= sizeof(DHCP6Message);
594
631bbe71
PF
595 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
596 &optval)) >= 0) {
597 switch (optcode) {
598 case DHCP6_OPTION_CLIENTID:
599 if (clientid) {
600 log_dhcp6_client(client, "%s contains multiple clientids",
601 dhcp6_message_type_to_string(message->type));
602 return -EINVAL;
603 }
604
605 if (optlen != sizeof(client->duid) ||
606 memcmp(&client->duid, optval, optlen) != 0) {
607 log_dhcp6_client(client, "%s DUID does not match",
608 dhcp6_message_type_to_string(message->type));
609
610 return -EINVAL;
611 }
612 clientid = true;
613
614 break;
615
616 case DHCP6_OPTION_SERVERID:
617 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
618 if (r >= 0 && id) {
619 log_dhcp6_client(client, "%s contains multiple serverids",
620 dhcp6_message_type_to_string(message->type));
621 return -EINVAL;
622 }
623
624 r = dhcp6_lease_set_serverid(lease, optval, optlen);
625 if (r < 0)
626 return r;
627
628 break;
629
630 case DHCP6_OPTION_PREFERENCE:
631 if (optlen != 1)
632 return -EINVAL;
633
634 r = dhcp6_lease_set_preference(lease, *optval);
635 if (r < 0)
636 return r;
637
638 break;
639
640 case DHCP6_OPTION_STATUS_CODE:
641 if (optlen < 2)
642 return -EINVAL;
643
644 status = optval[0] << 8 | optval[1];
645 if (status) {
646 log_dhcp6_client(client, "%s Status %s",
647 dhcp6_message_type_to_string(message->type),
648 dhcp6_message_status_to_string(status));
649 return -EINVAL;
650 }
651
652 break;
653
654 case DHCP6_OPTION_IA_NA:
655 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
656 &lease->ia);
657 if (r < 0 && r != -ENOMSG)
658 return r;
659
660 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
661 if (r < 0)
662 return r;
663
664 if (client->ia_na.id != iaid_lease) {
665 log_dhcp6_client(client, "%s has wrong IAID",
666 dhcp6_message_type_to_string(message->type));
667 return -EINVAL;
668 }
669
670 break;
ed6ee219
PF
671
672 case DHCP6_OPTION_RAPID_COMMIT:
673 r = dhcp6_lease_set_rapid_commit(lease);
674 if (r < 0)
675 return r;
676
677 break;
631bbe71
PF
678 }
679 }
680
681 if ((r < 0 && r != -ENOMSG) || !clientid) {
682 log_dhcp6_client(client, "%s has incomplete options",
683 dhcp6_message_type_to_string(message->type));
684 return -EINVAL;
685 }
686
687 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
688 if (r < 0)
689 log_dhcp6_client(client, "%s has no server id",
690 dhcp6_message_type_to_string(message->type));
691
692 return r;
693}
694
a34b57c0
PF
695static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
696 size_t len)
697{
698 int r;
699 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
ed6ee219 700 bool rapid_commit;
a34b57c0
PF
701
702 if (reply->type != DHCP6_REPLY)
ed6ee219 703 return 0;
a34b57c0
PF
704
705 r = dhcp6_lease_new(&lease);
706 if (r < 0)
707 return -ENOMEM;
708
709 r = client_parse_message(client, reply, len, lease);
710 if (r < 0)
711 return r;
712
ed6ee219
PF
713 if (client->state == DHCP6_STATE_SOLICITATION) {
714 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
715 if (r < 0)
716 return r;
717
718 if (!rapid_commit)
719 return 0;
720 }
721
b1e1238f 722 if (client->lease)
5364f729 723 dhcp6_lease_clear_timers(&client->lease->ia);
a34b57c0
PF
724
725 client->lease = sd_dhcp6_lease_unref(client->lease);
726 client->lease = lease;
727 lease = NULL;
728
729 return DHCP6_STATE_BOUND;
730}
731
631bbe71
PF
732static int client_receive_advertise(sd_dhcp6_client *client,
733 DHCP6Message *advertise, size_t len) {
734 int r;
735 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
736 uint8_t pref_advertise = 0, pref_lease = 0;
737
738 if (advertise->type != DHCP6_ADVERTISE)
ed6ee219 739 return 0;
631bbe71
PF
740
741 r = dhcp6_lease_new(&lease);
742 if (r < 0)
743 return r;
744
745 r = client_parse_message(client, advertise, len, lease);
746 if (r < 0)
747 return r;
748
749 r = dhcp6_lease_get_preference(lease, &pref_advertise);
750 if (r < 0)
751 return r;
752
753 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
754 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
755 sd_dhcp6_lease_unref(client->lease);
756 client->lease = lease;
757 lease = NULL;
758 r = 0;
759 }
760
7246333c
PF
761 if (pref_advertise == 255 || client->retransmit_count > 1)
762 r = DHCP6_STATE_REQUEST;
763
631bbe71
PF
764 return r;
765}
766
a9aff361 767static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
631bbe71
PF
768 void *userdata) {
769 sd_dhcp6_client *client = userdata;
3f0c075f 770 DHCP6_CLIENT_DONT_DESTROY(client);
631bbe71
PF
771 _cleanup_free_ DHCP6Message *message;
772 int r, buflen, len;
773
774 assert(s);
775 assert(client);
776 assert(client->event);
777
778 r = ioctl(fd, FIONREAD, &buflen);
779 if (r < 0 || buflen <= 0)
780 buflen = DHCP6_MIN_OPTIONS_SIZE;
781
782 message = malloc0(buflen);
783 if (!message)
784 return -ENOMEM;
785
786 len = read(fd, message, buflen);
787 if ((size_t)len < sizeof(DHCP6Message)) {
6ec60d20 788 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
631bbe71
PF
789 return 0;
790 }
791
792 switch(message->type) {
793 case DHCP6_SOLICIT:
794 case DHCP6_REQUEST:
795 case DHCP6_CONFIRM:
796 case DHCP6_RENEW:
797 case DHCP6_REBIND:
798 case DHCP6_RELEASE:
799 case DHCP6_DECLINE:
800 case DHCP6_INFORMATION_REQUEST:
801 case DHCP6_RELAY_FORW:
802 case DHCP6_RELAY_REPL:
803 return 0;
804
805 case DHCP6_ADVERTISE:
806 case DHCP6_REPLY:
807 case DHCP6_RECONFIGURE:
808 break;
809
810 default:
811 log_dhcp6_client(client, "unknown message type %d",
812 message->type);
813 return 0;
814 }
815
816 if (client->transaction_id != (message->transaction_id &
817 htobe32(0x00ffffff)))
818 return 0;
819
820 switch (client->state) {
821 case DHCP6_STATE_SOLICITATION:
822 r = client_receive_advertise(client, message, len);
823
ed6ee219 824 if (r == DHCP6_STATE_REQUEST) {
7246333c
PF
825 client_start(client, r);
826
ed6ee219
PF
827 break;
828 }
631bbe71 829
ed6ee219 830 /* fall through for Soliciation Rapid Commit option check */
7246333c 831 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
832 case DHCP6_STATE_RENEW:
833 case DHCP6_STATE_REBIND:
834
a34b57c0
PF
835 r = client_receive_reply(client, message, len);
836 if (r < 0)
837 return 0;
838
839 if (r == DHCP6_STATE_BOUND) {
840
841 r = client_start(client, DHCP6_STATE_BOUND);
842 if (r < 0) {
843 client_stop(client, r);
844 return 0;
845 }
846
3f0c075f 847 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
a34b57c0
PF
848 }
849
850 break;
851
852 case DHCP6_STATE_BOUND:
853
854 break;
855
631bbe71 856 case DHCP6_STATE_STOPPED:
631bbe71
PF
857 return 0;
858 }
859
860 if (r >= 0) {
861 log_dhcp6_client(client, "Recv %s",
862 dhcp6_message_type_to_string(message->type));
863 }
864
a9aff361
PF
865 return 0;
866}
867
c3e2adea 868static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
f12abb48
PF
869{
870 int r;
a34b57c0
PF
871 usec_t timeout, time_now;
872 char time_string[FORMAT_TIMESPAN_MAX];
f12abb48
PF
873
874 assert_return(client, -EINVAL);
875 assert_return(client->event, -EINVAL);
876 assert_return(client->index > 0, -EINVAL);
c3e2adea 877 assert_return(client->state != state, -EINVAL);
f12abb48 878
c3e2adea
PF
879 client->timeout_resend_expire =
880 sd_event_source_unref(client->timeout_resend_expire);
881 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
882 client->retransmit_time = 0;
883 client->retransmit_count = 0;
f12abb48 884
c3e2adea
PF
885 switch (state) {
886 case DHCP6_STATE_STOPPED:
c3e2adea 887 case DHCP6_STATE_SOLICITATION:
a9aff361 888
c3e2adea
PF
889 r = client_ensure_iaid(client);
890 if (r < 0)
891 return r;
a9aff361 892
c3e2adea
PF
893 r = dhcp6_network_bind_udp_socket(client->index, NULL);
894 if (r < 0)
895 return r;
a9aff361 896
c3e2adea
PF
897 client->fd = r;
898
899 r = sd_event_add_io(client->event, &client->receive_message,
900 client->fd, EPOLLIN, client_receive_message,
901 client);
902 if (r < 0)
903 return r;
904
905 r = sd_event_source_set_priority(client->receive_message,
906 client->event_priority);
907 if (r < 0)
908 return r;
909
9021bb9f
TG
910 r = sd_event_source_set_name(client->receive_message,
911 "dhcp6-receive-message");
912 if (r < 0)
913 return r;
914
c3e2adea
PF
915 client->state = DHCP6_STATE_SOLICITATION;
916
7246333c
PF
917 break;
918
919 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
920 case DHCP6_STATE_RENEW:
921 case DHCP6_STATE_REBIND:
a34b57c0 922
7246333c
PF
923 client->state = state;
924
c3e2adea 925 break;
a34b57c0
PF
926
927 case DHCP6_STATE_BOUND:
928
fa94c34b 929 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
a34b57c0
PF
930 if (r < 0)
931 return r;
932
933 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
934 client->lease->ia.lifetime_t2 == 0xffffffff) {
935
936 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
937 be32toh(client->lease->ia.lifetime_t1),
938 be32toh(client->lease->ia.lifetime_t2));
939
940 return 0;
941 }
942
943 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
944
945 log_dhcp6_client(client, "T1 expires in %s",
946 format_timespan(time_string,
947 FORMAT_TIMESPAN_MAX,
948 timeout, 0));
949
950 r = sd_event_add_time(client->event,
951 &client->lease->ia.timeout_t1,
fa94c34b 952 clock_boottime_or_monotonic(), time_now + timeout,
a34b57c0
PF
953 10 * USEC_PER_SEC, client_timeout_t1,
954 client);
955 if (r < 0)
956 return r;
957
958 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
959 client->event_priority);
960 if (r < 0)
961 return r;
962
9021bb9f
TG
963 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
964 "dhcp6-t1-timeout");
965 if (r < 0)
966 return r;
967
a34b57c0
PF
968 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
969
970 log_dhcp6_client(client, "T2 expires in %s",
971 format_timespan(time_string,
972 FORMAT_TIMESPAN_MAX,
973 timeout, 0));
974
975 r = sd_event_add_time(client->event,
976 &client->lease->ia.timeout_t2,
fa94c34b 977 clock_boottime_or_monotonic(), time_now + timeout,
a34b57c0
PF
978 10 * USEC_PER_SEC, client_timeout_t2,
979 client);
980 if (r < 0)
981 return r;
982
983 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
984 client->event_priority);
985 if (r < 0)
986 return r;
987
9021bb9f
TG
988 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
989 "dhcp6-t2-timeout");
990 if (r < 0)
991 return r;
992
3dc34fcc
PF
993 client->state = state;
994
a34b57c0 995 return 0;
c3e2adea 996 }
a9aff361 997
c3e2adea 998 client->transaction_id = random_u32() & htobe32(0x00ffffff);
d1b0afe3
PF
999
1000 r = sd_event_add_time(client->event, &client->timeout_resend,
fa94c34b 1001 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
d1b0afe3
PF
1002 client);
1003 if (r < 0)
1004 return r;
1005
1006 r = sd_event_source_set_priority(client->timeout_resend,
1007 client->event_priority);
1008 if (r < 0)
1009 return r;
1010
9021bb9f
TG
1011 r = sd_event_source_set_name(client->timeout_resend,
1012 "dhcp6-resend-timeout");
1013 if (r < 0)
1014 return r;
1015
f12abb48
PF
1016 return 0;
1017}
1018
139b011a
PF
1019int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1020{
1021 client_stop(client, DHCP6_EVENT_STOP);
1022
1023 return 0;
1024}
1025
1026int sd_dhcp6_client_start(sd_dhcp6_client *client)
1027{
1028 int r = 0;
1029
1030 assert_return(client, -EINVAL);
1031 assert_return(client->event, -EINVAL);
1032 assert_return(client->index > 0, -EINVAL);
1033
c806ffb9 1034 r = client_reset(client);
f12abb48
PF
1035 if (r < 0)
1036 return r;
1037
c3e2adea 1038 return client_start(client, DHCP6_STATE_SOLICITATION);
139b011a
PF
1039}
1040
1041int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1042 int priority)
1043{
1044 int r;
1045
1046 assert_return(client, -EINVAL);
1047 assert_return(!client->event, -EBUSY);
1048
1049 if (event)
1050 client->event = sd_event_ref(event);
1051 else {
1052 r = sd_event_default(&client->event);
1053 if (r < 0)
1054 return 0;
1055 }
1056
1057 client->event_priority = priority;
1058
1059 return 0;
1060}
1061
1062int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1063 assert_return(client, -EINVAL);
1064
1065 client->event = sd_event_unref(client->event);
1066
1067 return 0;
1068}
1069
1070sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1071 if (!client)
1072 return NULL;
1073
1074 return client->event;
1075}
1076
1077sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1078 if (client)
1079 assert_se(REFCNT_INC(client->n_ref) >= 2);
1080
1081 return client;
1082}
1083
1084sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1085 if (client && REFCNT_DEC(client->n_ref) <= 0) {
c806ffb9 1086 client_reset(client);
139b011a
PF
1087
1088 sd_dhcp6_client_detach_event(client);
1089
da6fe470 1090 free(client->req_opts);
139b011a
PF
1091 free(client);
1092
1093 return NULL;
1094 }
1095
1096 return client;
1097}
1098
139b011a
PF
1099int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1100{
3f0c075f 1101 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
a276e6d6
TG
1102 sd_id128_t machine_id;
1103 int r;
da6fe470 1104 size_t t;
139b011a
PF
1105
1106 assert_return(ret, -EINVAL);
1107
1108 client = new0(sd_dhcp6_client, 1);
1109 if (!client)
1110 return -ENOMEM;
1111
1112 client->n_ref = REFCNT_INIT;
1113
f12abb48
PF
1114 client->ia_na.type = DHCP6_OPTION_IA_NA;
1115
139b011a
PF
1116 client->index = -1;
1117
c806ffb9
ZJS
1118 client->fd = -1;
1119
a276e6d6
TG
1120 /* initialize DUID */
1121 client->duid.type = htobe16(DHCP6_DUID_EN);
1122 client->duid.pen = htobe32(SYSTEMD_PEN);
1123
1124 r = sd_id128_get_machine(&machine_id);
1125 if (r < 0)
1126 return r;
1127
1128 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1129 directly */
1130 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1131 HASH_KEY.bytes);
1132
da6fe470
PF
1133 client->req_opts_len = ELEMENTSOF(default_req_opts);
1134
1135 client->req_opts = new0(be16_t, client->req_opts_len);
1136 if (!client->req_opts)
1137 return -ENOMEM;
1138
1139 for (t = 0; t < client->req_opts_len; t++)
1140 client->req_opts[t] = htobe16(default_req_opts[t]);
1141
139b011a
PF
1142 *ret = client;
1143 client = NULL;
1144
1145 return 0;
1146}