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