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