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