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