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