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