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