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