]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
networkd: fix alignment of gperf source
[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);
257
7246333c 258 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
a9aff361
PF
259 if (r < 0)
260 return r;
261
7246333c
PF
262 break;
263
264 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
265 case DHCP6_STATE_RENEW:
266
267 if (client->state == DHCP6_STATE_REQUEST)
268 message->type = DHCP6_REQUEST;
269 else
270 message->type = DHCP6_RENEW;
7246333c
PF
271
272 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
273 client->lease->serverid_len,
274 client->lease->serverid);
275 if (r < 0)
276 return r;
277
278 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
a9aff361
PF
279 if (r < 0)
280 return r;
281
282 break;
283
3dc34fcc
PF
284 case DHCP6_STATE_REBIND:
285 message->type = DHCP6_REBIND;
286
287 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
288 if (r < 0)
289 return r;
290
291 break;
292
a9aff361
PF
293 case DHCP6_STATE_STOPPED:
294 case DHCP6_STATE_RS:
a34b57c0 295 case DHCP6_STATE_BOUND:
a9aff361
PF
296 return -EINVAL;
297 }
298
da6fe470
PF
299 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
300 client->req_opts_len * sizeof(be16_t),
301 client->req_opts);
302 if (r < 0)
303 return r;
304
7246333c
PF
305 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
306 sizeof(client->duid), &client->duid);
307 if (r < 0)
308 return r;
309
a9aff361
PF
310 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
311 len - optlen);
312 if (r < 0)
313 return r;
314
315 log_dhcp6_client(client, "Sent %s",
316 dhcp6_message_type_to_string(message->type));
317
318 return 0;
319}
320
a34b57c0
PF
321static int client_timeout_t2(sd_event_source *s, uint64_t usec,
322 void *userdata) {
323 sd_dhcp6_client *client = userdata;
324
325 assert_return(s, -EINVAL);
326 assert_return(client, -EINVAL);
327 assert_return(client->lease, -EINVAL);
328
329 client->lease->ia.timeout_t2 =
330 sd_event_source_unref(client->lease->ia.timeout_t2);
331
332 log_dhcp6_client(client, "Timeout T2");
333
3dc34fcc
PF
334 client_start(client, DHCP6_STATE_REBIND);
335
a34b57c0
PF
336 return 0;
337}
338
339static int client_timeout_t1(sd_event_source *s, uint64_t usec,
340 void *userdata) {
341 sd_dhcp6_client *client = userdata;
342
343 assert_return(s, -EINVAL);
344 assert_return(client, -EINVAL);
345 assert_return(client->lease, -EINVAL);
346
347 client->lease->ia.timeout_t1 =
348 sd_event_source_unref(client->lease->ia.timeout_t1);
349
350 log_dhcp6_client(client, "Timeout T1");
351
3dc34fcc
PF
352 client_start(client, DHCP6_STATE_RENEW);
353
a34b57c0
PF
354 return 0;
355}
356
d1b0afe3
PF
357static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
358 void *userdata) {
359 sd_dhcp6_client *client = userdata;
3dc34fcc
PF
360 DHCP6_CLIENT_DONT_DESTROY(client);
361 enum DHCP6State state;
d1b0afe3
PF
362
363 assert(s);
364 assert(client);
365 assert(client->event);
366
3dc34fcc
PF
367 state = client->state;
368
d1b0afe3
PF
369 client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
370
3dc34fcc
PF
371 /* RFC 3315, section 18.1.4., says that "...the client may choose to
372 use a Solicit message to locate a new DHCP server..." */
373 if (state == DHCP6_STATE_REBIND)
374 client_start(client, DHCP6_STATE_SOLICITATION);
375
d1b0afe3
PF
376 return 0;
377}
378
379static usec_t client_timeout_compute_random(usec_t val) {
380 return val - val / 10 +
381 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
382}
383
384static int client_timeout_resend(sd_event_source *s, uint64_t usec,
385 void *userdata) {
386 int r = 0;
387 sd_dhcp6_client *client = userdata;
388 usec_t time_now, init_retransmit_time, max_retransmit_time;
389 usec_t max_retransmit_duration;
513a6fa8 390 uint8_t max_retransmit_count = 0;
d1b0afe3 391 char time_string[FORMAT_TIMESPAN_MAX];
3dc34fcc 392 uint32_t expire = 0;
d1b0afe3
PF
393
394 assert(s);
395 assert(client);
396 assert(client->event);
397
398 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
399
400 switch (client->state) {
401 case DHCP6_STATE_SOLICITATION:
7246333c
PF
402
403 if (client->retransmit_count && client->lease) {
404 client_start(client, DHCP6_STATE_REQUEST);
405 return 0;
406 }
407
d1b0afe3
PF
408 init_retransmit_time = DHCP6_SOL_TIMEOUT;
409 max_retransmit_time = DHCP6_SOL_MAX_RT;
410 max_retransmit_count = 0;
411 max_retransmit_duration = 0;
412
413 break;
414
7246333c
PF
415 case DHCP6_STATE_REQUEST:
416 init_retransmit_time = DHCP6_REQ_TIMEOUT;
417 max_retransmit_time = DHCP6_REQ_MAX_RT;
418 max_retransmit_count = DHCP6_REQ_MAX_RC;
419 max_retransmit_duration = 0;
420
421 break;
422
3dc34fcc
PF
423 case DHCP6_STATE_RENEW:
424 init_retransmit_time = DHCP6_REN_TIMEOUT;
425 max_retransmit_time = DHCP6_REN_MAX_RT;
426 max_retransmit_count = 0;
427
428 /* RFC 3315, section 18.1.3. says max retransmit duration will
429 be the remaining time until T2. Instead of setting MRD,
430 wait for T2 to trigger with the same end result */
431 max_retransmit_duration = 0;
432
433 break;
434
435 case DHCP6_STATE_REBIND:
436 init_retransmit_time = DHCP6_REB_TIMEOUT;
437 max_retransmit_time = DHCP6_REB_MAX_RT;
438 max_retransmit_count = 0;
439
440 max_retransmit_duration = 0;
441
442 if (!client->timeout_resend_expire) {
443 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
444 &expire);
445 if (r < 0) {
446 client_stop(client, r);
447 return 0;
448 }
449 max_retransmit_duration = expire * USEC_PER_SEC;
450 }
451
452 break;
453
d1b0afe3
PF
454 case DHCP6_STATE_STOPPED:
455 case DHCP6_STATE_RS:
a34b57c0 456 case DHCP6_STATE_BOUND:
d1b0afe3
PF
457 return 0;
458 }
459
460 if (max_retransmit_count &&
461 client->retransmit_count >= max_retransmit_count) {
462 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
463 return 0;
464 }
465
a9aff361
PF
466 r = client_send_message(client);
467 if (r >= 0)
468 client->retransmit_count++;
469
470
d1b0afe3
PF
471 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
472 if (r < 0)
473 goto error;
474
475 if (!client->retransmit_time) {
476 client->retransmit_time =
477 client_timeout_compute_random(init_retransmit_time);
a9aff361
PF
478
479 if (client->state == DHCP6_STATE_SOLICITATION)
480 client->retransmit_time += init_retransmit_time / 10;
481
d1b0afe3
PF
482 } else {
483 if (max_retransmit_time &&
484 client->retransmit_time > max_retransmit_time / 2)
485 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
486 else
487 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
488 }
489
490 log_dhcp6_client(client, "Next retransmission in %s",
491 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
492 client->retransmit_time, 0));
493
494 r = sd_event_add_time(client->event, &client->timeout_resend,
495 CLOCK_MONOTONIC,
496 time_now + client->retransmit_time,
497 10 * USEC_PER_MSEC, client_timeout_resend,
498 client);
499 if (r < 0)
500 goto error;
501
502 r = sd_event_source_set_priority(client->timeout_resend,
503 client->event_priority);
504 if (r < 0)
505 goto error;
506
507 if (max_retransmit_duration && !client->timeout_resend_expire) {
508
509 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
510 max_retransmit_duration / USEC_PER_SEC);
511
512 r = sd_event_add_time(client->event,
513 &client->timeout_resend_expire,
514 CLOCK_MONOTONIC,
515 time_now + max_retransmit_duration,
516 USEC_PER_SEC,
517 client_timeout_resend_expire, client);
518 if (r < 0)
519 goto error;
520
521 r = sd_event_source_set_priority(client->timeout_resend_expire,
522 client->event_priority);
523 if (r < 0)
524 goto error;
525 }
526
527error:
528 if (r < 0)
529 client_stop(client, r);
530
531 return 0;
532}
533
f12abb48 534static int client_ensure_iaid(sd_dhcp6_client *client) {
54d61deb
TG
535 /* name is a pointer to memory in the udev_device struct, so must
536 have the same scope */
537 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
f12abb48
PF
538 const char *name = NULL;
539 uint64_t id;
540
541 assert(client);
542
543 if (client->ia_na.id)
544 return 0;
545
546 if (detect_container(NULL) <= 0) {
547 /* not in a container, udev will be around */
548 _cleanup_udev_unref_ struct udev *udev;
f12abb48
PF
549 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
550
551 udev = udev_new();
552 if (!udev)
553 return -ENOMEM;
554
555 sprintf(ifindex_str, "n%d", client->index);
556 device = udev_device_new_from_device_id(udev, ifindex_str);
557 if (!device)
558 return -errno;
559
560 if (udev_device_get_is_initialized(device) <= 0)
561 /* not yet ready */
562 return -EBUSY;
563
564 name = net_get_name(device);
565 }
566
567 if (name)
568 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
569 else
570 /* fall back to mac address if no predictable name available */
571 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
572 HASH_KEY.bytes);
573
574 /* fold into 32 bits */
575 client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
576
577 return 0;
578}
579
631bbe71
PF
580static int client_parse_message(sd_dhcp6_client *client,
581 DHCP6Message *message, size_t len,
582 sd_dhcp6_lease *lease) {
583 int r;
584 uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
585 uint16_t optcode, status;
586 size_t optlen, id_len;
587 bool clientid = false;
588 be32_t iaid_lease;
589
590 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
591 &optval)) >= 0) {
592 switch (optcode) {
593 case DHCP6_OPTION_CLIENTID:
594 if (clientid) {
595 log_dhcp6_client(client, "%s contains multiple clientids",
596 dhcp6_message_type_to_string(message->type));
597 return -EINVAL;
598 }
599
600 if (optlen != sizeof(client->duid) ||
601 memcmp(&client->duid, optval, optlen) != 0) {
602 log_dhcp6_client(client, "%s DUID does not match",
603 dhcp6_message_type_to_string(message->type));
604
605 return -EINVAL;
606 }
607 clientid = true;
608
609 break;
610
611 case DHCP6_OPTION_SERVERID:
612 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
613 if (r >= 0 && id) {
614 log_dhcp6_client(client, "%s contains multiple serverids",
615 dhcp6_message_type_to_string(message->type));
616 return -EINVAL;
617 }
618
619 r = dhcp6_lease_set_serverid(lease, optval, optlen);
620 if (r < 0)
621 return r;
622
623 break;
624
625 case DHCP6_OPTION_PREFERENCE:
626 if (optlen != 1)
627 return -EINVAL;
628
629 r = dhcp6_lease_set_preference(lease, *optval);
630 if (r < 0)
631 return r;
632
633 break;
634
635 case DHCP6_OPTION_STATUS_CODE:
636 if (optlen < 2)
637 return -EINVAL;
638
639 status = optval[0] << 8 | optval[1];
640 if (status) {
641 log_dhcp6_client(client, "%s Status %s",
642 dhcp6_message_type_to_string(message->type),
643 dhcp6_message_status_to_string(status));
644 return -EINVAL;
645 }
646
647 break;
648
649 case DHCP6_OPTION_IA_NA:
650 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
651 &lease->ia);
652 if (r < 0 && r != -ENOMSG)
653 return r;
654
655 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
656 if (r < 0)
657 return r;
658
659 if (client->ia_na.id != iaid_lease) {
660 log_dhcp6_client(client, "%s has wrong IAID",
661 dhcp6_message_type_to_string(message->type));
662 return -EINVAL;
663 }
664
665 break;
ed6ee219
PF
666
667 case DHCP6_OPTION_RAPID_COMMIT:
668 r = dhcp6_lease_set_rapid_commit(lease);
669 if (r < 0)
670 return r;
671
672 break;
631bbe71
PF
673 }
674 }
675
676 if ((r < 0 && r != -ENOMSG) || !clientid) {
677 log_dhcp6_client(client, "%s has incomplete options",
678 dhcp6_message_type_to_string(message->type));
679 return -EINVAL;
680 }
681
682 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
683 if (r < 0)
684 log_dhcp6_client(client, "%s has no server id",
685 dhcp6_message_type_to_string(message->type));
686
687 return r;
688}
689
a34b57c0
PF
690static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
691 size_t len)
692{
693 int r;
694 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
ed6ee219 695 bool rapid_commit;
a34b57c0
PF
696
697 if (reply->type != DHCP6_REPLY)
ed6ee219 698 return 0;
a34b57c0
PF
699
700 r = dhcp6_lease_new(&lease);
701 if (r < 0)
702 return -ENOMEM;
703
704 r = client_parse_message(client, reply, len, lease);
705 if (r < 0)
706 return r;
707
ed6ee219
PF
708 if (client->state == DHCP6_STATE_SOLICITATION) {
709 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
710 if (r < 0)
711 return r;
712
713 if (!rapid_commit)
714 return 0;
715 }
716
a34b57c0
PF
717 dhcp6_lease_clear_timers(&client->lease->ia);
718
719 client->lease = sd_dhcp6_lease_unref(client->lease);
720 client->lease = lease;
721 lease = NULL;
722
723 return DHCP6_STATE_BOUND;
724}
725
631bbe71
PF
726static int client_receive_advertise(sd_dhcp6_client *client,
727 DHCP6Message *advertise, size_t len) {
728 int r;
729 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
730 uint8_t pref_advertise = 0, pref_lease = 0;
731
732 if (advertise->type != DHCP6_ADVERTISE)
ed6ee219 733 return 0;
631bbe71
PF
734
735 r = dhcp6_lease_new(&lease);
736 if (r < 0)
737 return r;
738
739 r = client_parse_message(client, advertise, len, lease);
740 if (r < 0)
741 return r;
742
743 r = dhcp6_lease_get_preference(lease, &pref_advertise);
744 if (r < 0)
745 return r;
746
747 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
748 if (!client->lease || r < 0 || pref_advertise > pref_lease) {
749 sd_dhcp6_lease_unref(client->lease);
750 client->lease = lease;
751 lease = NULL;
752 r = 0;
753 }
754
7246333c
PF
755 if (pref_advertise == 255 || client->retransmit_count > 1)
756 r = DHCP6_STATE_REQUEST;
757
631bbe71
PF
758 return r;
759}
760
a9aff361 761static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
631bbe71
PF
762 void *userdata) {
763 sd_dhcp6_client *client = userdata;
3f0c075f 764 DHCP6_CLIENT_DONT_DESTROY(client);
631bbe71
PF
765 _cleanup_free_ DHCP6Message *message;
766 int r, buflen, len;
767
768 assert(s);
769 assert(client);
770 assert(client->event);
771
772 r = ioctl(fd, FIONREAD, &buflen);
773 if (r < 0 || buflen <= 0)
774 buflen = DHCP6_MIN_OPTIONS_SIZE;
775
776 message = malloc0(buflen);
777 if (!message)
778 return -ENOMEM;
779
780 len = read(fd, message, buflen);
781 if ((size_t)len < sizeof(DHCP6Message)) {
6ec60d20 782 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
631bbe71
PF
783 return 0;
784 }
785
786 switch(message->type) {
787 case DHCP6_SOLICIT:
788 case DHCP6_REQUEST:
789 case DHCP6_CONFIRM:
790 case DHCP6_RENEW:
791 case DHCP6_REBIND:
792 case DHCP6_RELEASE:
793 case DHCP6_DECLINE:
794 case DHCP6_INFORMATION_REQUEST:
795 case DHCP6_RELAY_FORW:
796 case DHCP6_RELAY_REPL:
797 return 0;
798
799 case DHCP6_ADVERTISE:
800 case DHCP6_REPLY:
801 case DHCP6_RECONFIGURE:
802 break;
803
804 default:
805 log_dhcp6_client(client, "unknown message type %d",
806 message->type);
807 return 0;
808 }
809
810 if (client->transaction_id != (message->transaction_id &
811 htobe32(0x00ffffff)))
812 return 0;
813
814 switch (client->state) {
815 case DHCP6_STATE_SOLICITATION:
816 r = client_receive_advertise(client, message, len);
817
ed6ee219 818 if (r == DHCP6_STATE_REQUEST) {
7246333c
PF
819 client_start(client, r);
820
ed6ee219
PF
821 break;
822 }
631bbe71 823
ed6ee219 824 /* fall through for Soliciation Rapid Commit option check */
7246333c 825 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
826 case DHCP6_STATE_RENEW:
827 case DHCP6_STATE_REBIND:
828
a34b57c0
PF
829 r = client_receive_reply(client, message, len);
830 if (r < 0)
831 return 0;
832
833 if (r == DHCP6_STATE_BOUND) {
834
835 r = client_start(client, DHCP6_STATE_BOUND);
836 if (r < 0) {
837 client_stop(client, r);
838 return 0;
839 }
840
3f0c075f 841 client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
a34b57c0
PF
842 }
843
844 break;
845
846 case DHCP6_STATE_BOUND:
847
848 break;
849
631bbe71
PF
850 case DHCP6_STATE_STOPPED:
851 case DHCP6_STATE_RS:
852 return 0;
853 }
854
855 if (r >= 0) {
856 log_dhcp6_client(client, "Recv %s",
857 dhcp6_message_type_to_string(message->type));
858 }
859
a9aff361
PF
860 return 0;
861}
862
c3e2adea 863static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
f12abb48
PF
864{
865 int r;
a34b57c0
PF
866 usec_t timeout, time_now;
867 char time_string[FORMAT_TIMESPAN_MAX];
f12abb48
PF
868
869 assert_return(client, -EINVAL);
870 assert_return(client->event, -EINVAL);
871 assert_return(client->index > 0, -EINVAL);
c3e2adea 872 assert_return(client->state != state, -EINVAL);
f12abb48 873
c3e2adea
PF
874 client->timeout_resend_expire =
875 sd_event_source_unref(client->timeout_resend_expire);
876 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
877 client->retransmit_time = 0;
878 client->retransmit_count = 0;
f12abb48 879
c3e2adea
PF
880 switch (state) {
881 case DHCP6_STATE_STOPPED:
882 case DHCP6_STATE_RS:
883 case DHCP6_STATE_SOLICITATION:
a9aff361 884
c3e2adea
PF
885 r = client_ensure_iaid(client);
886 if (r < 0)
887 return r;
a9aff361 888
c3e2adea
PF
889 r = dhcp6_network_bind_udp_socket(client->index, NULL);
890 if (r < 0)
891 return r;
a9aff361 892
c3e2adea
PF
893 client->fd = r;
894
895 r = sd_event_add_io(client->event, &client->receive_message,
896 client->fd, EPOLLIN, client_receive_message,
897 client);
898 if (r < 0)
899 return r;
900
901 r = sd_event_source_set_priority(client->receive_message,
902 client->event_priority);
903 if (r < 0)
904 return r;
905
906 client->state = DHCP6_STATE_SOLICITATION;
907
7246333c
PF
908 break;
909
910 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
911 case DHCP6_STATE_RENEW:
912 case DHCP6_STATE_REBIND:
a34b57c0 913
7246333c
PF
914 client->state = state;
915
c3e2adea 916 break;
a34b57c0
PF
917
918 case DHCP6_STATE_BOUND:
919
920 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
921 if (r < 0)
922 return r;
923
924 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
925 client->lease->ia.lifetime_t2 == 0xffffffff) {
926
927 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
928 be32toh(client->lease->ia.lifetime_t1),
929 be32toh(client->lease->ia.lifetime_t2));
930
931 return 0;
932 }
933
934 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
935
936 log_dhcp6_client(client, "T1 expires in %s",
937 format_timespan(time_string,
938 FORMAT_TIMESPAN_MAX,
939 timeout, 0));
940
941 r = sd_event_add_time(client->event,
942 &client->lease->ia.timeout_t1,
943 CLOCK_MONOTONIC, time_now + timeout,
944 10 * USEC_PER_SEC, client_timeout_t1,
945 client);
946 if (r < 0)
947 return r;
948
949 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
950 client->event_priority);
951 if (r < 0)
952 return r;
953
954 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
955
956 log_dhcp6_client(client, "T2 expires in %s",
957 format_timespan(time_string,
958 FORMAT_TIMESPAN_MAX,
959 timeout, 0));
960
961 r = sd_event_add_time(client->event,
962 &client->lease->ia.timeout_t2,
963 CLOCK_MONOTONIC, time_now + timeout,
964 10 * USEC_PER_SEC, client_timeout_t2,
965 client);
966 if (r < 0)
967 return r;
968
969 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
970 client->event_priority);
971 if (r < 0)
972 return r;
973
3dc34fcc
PF
974 client->state = state;
975
a34b57c0 976 return 0;
c3e2adea 977 }
a9aff361 978
c3e2adea 979 client->transaction_id = random_u32() & htobe32(0x00ffffff);
d1b0afe3
PF
980
981 r = sd_event_add_time(client->event, &client->timeout_resend,
982 CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
983 client);
984 if (r < 0)
985 return r;
986
987 r = sd_event_source_set_priority(client->timeout_resend,
988 client->event_priority);
989 if (r < 0)
990 return r;
991
f12abb48
PF
992 return 0;
993}
994
139b011a
PF
995int sd_dhcp6_client_stop(sd_dhcp6_client *client)
996{
997 client_stop(client, DHCP6_EVENT_STOP);
998
999 return 0;
1000}
1001
1002int sd_dhcp6_client_start(sd_dhcp6_client *client)
1003{
1004 int r = 0;
1005
1006 assert_return(client, -EINVAL);
1007 assert_return(client->event, -EINVAL);
1008 assert_return(client->index > 0, -EINVAL);
1009
c806ffb9 1010 r = client_reset(client);
f12abb48
PF
1011 if (r < 0)
1012 return r;
1013
c3e2adea 1014 return client_start(client, DHCP6_STATE_SOLICITATION);
139b011a
PF
1015}
1016
1017int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1018 int priority)
1019{
1020 int r;
1021
1022 assert_return(client, -EINVAL);
1023 assert_return(!client->event, -EBUSY);
1024
1025 if (event)
1026 client->event = sd_event_ref(event);
1027 else {
1028 r = sd_event_default(&client->event);
1029 if (r < 0)
1030 return 0;
1031 }
1032
1033 client->event_priority = priority;
1034
1035 return 0;
1036}
1037
1038int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1039 assert_return(client, -EINVAL);
1040
1041 client->event = sd_event_unref(client->event);
1042
1043 return 0;
1044}
1045
1046sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1047 if (!client)
1048 return NULL;
1049
1050 return client->event;
1051}
1052
1053sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1054 if (client)
1055 assert_se(REFCNT_INC(client->n_ref) >= 2);
1056
1057 return client;
1058}
1059
1060sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1061 if (client && REFCNT_DEC(client->n_ref) <= 0) {
c806ffb9 1062 client_reset(client);
139b011a
PF
1063
1064 sd_dhcp6_client_detach_event(client);
1065
da6fe470 1066 free(client->req_opts);
139b011a
PF
1067 free(client);
1068
1069 return NULL;
1070 }
1071
1072 return client;
1073}
1074
139b011a
PF
1075int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1076{
3f0c075f 1077 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
a276e6d6
TG
1078 sd_id128_t machine_id;
1079 int r;
da6fe470 1080 size_t t;
139b011a
PF
1081
1082 assert_return(ret, -EINVAL);
1083
1084 client = new0(sd_dhcp6_client, 1);
1085 if (!client)
1086 return -ENOMEM;
1087
1088 client->n_ref = REFCNT_INIT;
1089
f12abb48
PF
1090 client->ia_na.type = DHCP6_OPTION_IA_NA;
1091
139b011a
PF
1092 client->index = -1;
1093
c806ffb9
ZJS
1094 client->fd = -1;
1095
a276e6d6
TG
1096 /* initialize DUID */
1097 client->duid.type = htobe16(DHCP6_DUID_EN);
1098 client->duid.pen = htobe32(SYSTEMD_PEN);
1099
1100 r = sd_id128_get_machine(&machine_id);
1101 if (r < 0)
1102 return r;
1103
1104 /* a bit of snake-oil perhaps, but no need to expose the machine-id
1105 directly */
1106 siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1107 HASH_KEY.bytes);
1108
da6fe470
PF
1109 client->req_opts_len = ELEMENTSOF(default_req_opts);
1110
1111 client->req_opts = new0(be16_t, client->req_opts_len);
1112 if (!client->req_opts)
1113 return -ENOMEM;
1114
1115 for (t = 0; t < client->req_opts_len; t++)
1116 client->req_opts[t] = htobe16(default_req_opts[t]);
1117
139b011a
PF
1118 *ret = client;
1119 client = NULL;
1120
1121 return 0;
1122}