]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
Merge pull request #1668 from ssahani/net1
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 2014-2015 Intel Corporation. All rights reserved.
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <linux/if_infiniband.h>
26
27 #include "sd-dhcp6-client.h"
28
29 #include "dhcp-identifier.h"
30 #include "dhcp6-internal.h"
31 #include "dhcp6-lease-internal.h"
32 #include "dhcp6-protocol.h"
33 #include "fd-util.h"
34 #include "network-internal.h"
35 #include "random-util.h"
36 #include "util.h"
37
38 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
39
40 struct sd_dhcp6_client {
41 unsigned n_ref;
42
43 enum DHCP6State state;
44 sd_event *event;
45 int event_priority;
46 int index;
47 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
48 size_t mac_addr_len;
49 uint16_t arp_type;
50 DHCP6IA ia_na;
51 be32_t transaction_id;
52 usec_t transaction_start;
53 struct sd_dhcp6_lease *lease;
54 int fd;
55 bool information_request;
56 be16_t *req_opts;
57 size_t req_opts_allocated;
58 size_t req_opts_len;
59 sd_event_source *receive_message;
60 usec_t retransmit_time;
61 uint8_t retransmit_count;
62 sd_event_source *timeout_resend;
63 sd_event_source *timeout_resend_expire;
64 sd_dhcp6_client_cb_t cb;
65 void *userdata;
66 struct duid duid;
67 size_t duid_len;
68 };
69
70 static const uint16_t default_req_opts[] = {
71 DHCP6_OPTION_DNS_SERVERS,
72 DHCP6_OPTION_DOMAIN_LIST,
73 DHCP6_OPTION_NTP_SERVER,
74 DHCP6_OPTION_SNTP_SERVERS,
75 };
76
77 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
78 [DHCP6_SOLICIT] = "SOLICIT",
79 [DHCP6_ADVERTISE] = "ADVERTISE",
80 [DHCP6_REQUEST] = "REQUEST",
81 [DHCP6_CONFIRM] = "CONFIRM",
82 [DHCP6_RENEW] = "RENEW",
83 [DHCP6_REBIND] = "REBIND",
84 [DHCP6_REPLY] = "REPLY",
85 [DHCP6_RELEASE] = "RELEASE",
86 [DHCP6_DECLINE] = "DECLINE",
87 [DHCP6_RECONFIGURE] = "RECONFIGURE",
88 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
89 [DHCP6_RELAY_FORW] = "RELAY-FORW",
90 [DHCP6_RELAY_REPL] = "RELAY-REPL",
91 };
92
93 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
94
95 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
96 [DHCP6_STATUS_SUCCESS] = "Success",
97 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
98 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
99 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
100 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
101 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
102 };
103
104 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
105
106 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
107 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
108
109 #define DHCP6_CLIENT_DONT_DESTROY(client) \
110 _cleanup_dhcp6_client_unref_ _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_cb_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_mac(
135 sd_dhcp6_client *client,
136 const uint8_t *addr, size_t addr_len,
137 uint16_t arp_type) {
138
139 assert_return(client, -EINVAL);
140 assert_return(addr, -EINVAL);
141 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
142 assert_return(arp_type > 0, -EINVAL);
143
144 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
145
146 if (arp_type == ARPHRD_ETHER)
147 assert_return(addr_len == ETH_ALEN, -EINVAL);
148 else if (arp_type == ARPHRD_INFINIBAND)
149 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
150 else
151 return -EINVAL;
152
153 if (client->mac_addr_len == addr_len &&
154 memcmp(&client->mac_addr, addr, addr_len) == 0)
155 return 0;
156
157 memcpy(&client->mac_addr, addr, addr_len);
158 client->mac_addr_len = addr_len;
159 client->arp_type = arp_type;
160
161 return 0;
162 }
163
164 static int client_ensure_duid(sd_dhcp6_client *client) {
165 if (client->duid_len != 0)
166 return 0;
167
168 return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
169 }
170
171 int sd_dhcp6_client_set_duid(
172 sd_dhcp6_client *client,
173 uint16_t type,
174 uint8_t *duid, size_t duid_len) {
175 assert_return(client, -EINVAL);
176 assert_return(duid, -EINVAL);
177 assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL);
178
179 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
180
181 switch (type) {
182 case DHCP6_DUID_LLT:
183 if (duid_len <= sizeof(client->duid.llt))
184 return -EINVAL;
185 break;
186 case DHCP6_DUID_EN:
187 if (duid_len != sizeof(client->duid.en))
188 return -EINVAL;
189 break;
190 case DHCP6_DUID_LL:
191 if (duid_len <= sizeof(client->duid.ll))
192 return -EINVAL;
193 break;
194 case DHCP6_DUID_UUID:
195 if (duid_len != sizeof(client->duid.uuid))
196 return -EINVAL;
197 break;
198 default:
199 /* accept unknown type in order to be forward compatible */
200 break;
201 }
202
203 client->duid.type = htobe16(type);
204 memcpy(&client->duid.raw.data, duid, duid_len);
205 client->duid_len = duid_len + sizeof(client->duid.type);
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 DHCP6_OPTION_DNS_SERVERS:
236 case DHCP6_OPTION_DOMAIN_LIST:
237 case DHCP6_OPTION_SNTP_SERVERS:
238 case 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 assert_return(ret, -EINVAL);
261
262 if (!client->lease)
263 return -ENOMSG;
264
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 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, 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, 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, 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, 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 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 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 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 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 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 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 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 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 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 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_dhcp6_lease_free_ 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_dhcp6_lease_free_ 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;
883 int r, buflen, len;
884
885 assert(s);
886 assert(client);
887 assert(client->event);
888
889 r = ioctl(fd, FIONREAD, &buflen);
890 if (r < 0 || buflen <= 0)
891 buflen = DHCP6_MIN_OPTIONS_SIZE;
892
893 message = malloc0(buflen);
894 if (!message)
895 return -ENOMEM;
896
897 len = read(fd, message, buflen);
898 if ((size_t)len < sizeof(DHCP6Message)) {
899 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
900 return 0;
901 }
902
903 switch(message->type) {
904 case DHCP6_SOLICIT:
905 case DHCP6_REQUEST:
906 case DHCP6_CONFIRM:
907 case DHCP6_RENEW:
908 case DHCP6_REBIND:
909 case DHCP6_RELEASE:
910 case DHCP6_DECLINE:
911 case DHCP6_INFORMATION_REQUEST:
912 case DHCP6_RELAY_FORW:
913 case DHCP6_RELAY_REPL:
914 return 0;
915
916 case DHCP6_ADVERTISE:
917 case DHCP6_REPLY:
918 case DHCP6_RECONFIGURE:
919 break;
920
921 default:
922 log_dhcp6_client(client, "unknown message type %d",
923 message->type);
924 return 0;
925 }
926
927 if (client->transaction_id != (message->transaction_id &
928 htobe32(0x00ffffff)))
929 return 0;
930
931 switch (client->state) {
932 case DHCP6_STATE_INFORMATION_REQUEST:
933 r = client_receive_reply(client, message, len);
934 if (r < 0)
935 return 0;
936
937 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
938
939 client_start(client, DHCP6_STATE_STOPPED);
940
941 break;
942
943 case DHCP6_STATE_SOLICITATION:
944 r = client_receive_advertise(client, message, len);
945
946 if (r == DHCP6_STATE_REQUEST) {
947 client_start(client, r);
948
949 break;
950 }
951
952 /* fall through for Soliciation Rapid Commit option check */
953 case DHCP6_STATE_REQUEST:
954 case DHCP6_STATE_RENEW:
955 case DHCP6_STATE_REBIND:
956
957 r = client_receive_reply(client, message, len);
958 if (r < 0)
959 return 0;
960
961 if (r == DHCP6_STATE_BOUND) {
962
963 r = client_start(client, DHCP6_STATE_BOUND);
964 if (r < 0) {
965 client_stop(client, r);
966 return 0;
967 }
968
969 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
970 }
971
972 break;
973
974 case DHCP6_STATE_BOUND:
975
976 break;
977
978 case DHCP6_STATE_STOPPED:
979 return 0;
980 }
981
982 if (r >= 0) {
983 log_dhcp6_client(client, "Recv %s",
984 dhcp6_message_type_to_string(message->type));
985 }
986
987 return 0;
988 }
989
990 static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
991 int r;
992 usec_t timeout, time_now;
993 char time_string[FORMAT_TIMESPAN_MAX];
994
995 assert_return(client, -EINVAL);
996 assert_return(client->event, -EINVAL);
997 assert_return(client->index > 0, -EINVAL);
998 assert_return(client->state != state, -EINVAL);
999
1000 client->timeout_resend_expire =
1001 sd_event_source_unref(client->timeout_resend_expire);
1002 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1003 client->retransmit_time = 0;
1004 client->retransmit_count = 0;
1005
1006 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1007 if (r < 0)
1008 return r;
1009
1010 switch (state) {
1011 case DHCP6_STATE_STOPPED:
1012 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1013 client->state = DHCP6_STATE_STOPPED;
1014
1015 return 0;
1016 }
1017
1018 /* fall through */
1019 case DHCP6_STATE_SOLICITATION:
1020 client->state = DHCP6_STATE_SOLICITATION;
1021
1022 break;
1023
1024 case DHCP6_STATE_INFORMATION_REQUEST:
1025 case DHCP6_STATE_REQUEST:
1026 case DHCP6_STATE_RENEW:
1027 case DHCP6_STATE_REBIND:
1028
1029 client->state = state;
1030
1031 break;
1032
1033 case DHCP6_STATE_BOUND:
1034
1035 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1036 client->lease->ia.lifetime_t2 == 0xffffffff) {
1037
1038 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1039 be32toh(client->lease->ia.lifetime_t1),
1040 be32toh(client->lease->ia.lifetime_t2));
1041
1042 return 0;
1043 }
1044
1045 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1046
1047 log_dhcp6_client(client, "T1 expires in %s",
1048 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1049
1050 r = sd_event_add_time(client->event,
1051 &client->lease->ia.timeout_t1,
1052 clock_boottime_or_monotonic(), time_now + timeout,
1053 10 * USEC_PER_SEC, client_timeout_t1,
1054 client);
1055 if (r < 0)
1056 return r;
1057
1058 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1059 client->event_priority);
1060 if (r < 0)
1061 return r;
1062
1063 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
1064 if (r < 0)
1065 return r;
1066
1067 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1068
1069 log_dhcp6_client(client, "T2 expires in %s",
1070 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1071
1072 r = sd_event_add_time(client->event,
1073 &client->lease->ia.timeout_t2,
1074 clock_boottime_or_monotonic(), time_now + timeout,
1075 10 * USEC_PER_SEC, client_timeout_t2,
1076 client);
1077 if (r < 0)
1078 return r;
1079
1080 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1081 client->event_priority);
1082 if (r < 0)
1083 return r;
1084
1085 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
1086 if (r < 0)
1087 return r;
1088
1089 client->state = state;
1090
1091 return 0;
1092 }
1093
1094 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1095 client->transaction_start = time_now;
1096
1097 r = sd_event_add_time(client->event, &client->timeout_resend,
1098 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1099 client);
1100 if (r < 0)
1101 return r;
1102
1103 r = sd_event_source_set_priority(client->timeout_resend,
1104 client->event_priority);
1105 if (r < 0)
1106 return r;
1107
1108 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1109 if (r < 0)
1110 return r;
1111
1112 return 0;
1113 }
1114
1115 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1116 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1117
1118 return 0;
1119 }
1120
1121 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1122 int r = 0;
1123 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
1124
1125 assert_return(client, -EINVAL);
1126 assert_return(client->event, -EINVAL);
1127 assert_return(client->index > 0, -EINVAL);
1128
1129 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
1130 return -EALREADY;
1131
1132 r = client_reset(client);
1133 if (r < 0)
1134 return r;
1135
1136 r = client_ensure_iaid(client);
1137 if (r < 0)
1138 return r;
1139
1140 r = client_ensure_duid(client);
1141 if (r < 0)
1142 return r;
1143
1144 r = dhcp6_network_bind_udp_socket(client->index, NULL);
1145 if (r < 0)
1146 return r;
1147
1148 client->fd = r;
1149
1150 r = sd_event_add_io(client->event, &client->receive_message,
1151 client->fd, EPOLLIN, client_receive_message,
1152 client);
1153 if (r < 0)
1154 goto error;
1155
1156 r = sd_event_source_set_priority(client->receive_message,
1157 client->event_priority);
1158 if (r < 0)
1159 goto error;
1160
1161 r = sd_event_source_set_description(client->receive_message,
1162 "dhcp6-receive-message");
1163 if (r < 0)
1164 goto error;
1165
1166 if (client->information_request)
1167 state = DHCP6_STATE_INFORMATION_REQUEST;
1168
1169 log_dhcp6_client(client, "Started in %s mode",
1170 client->information_request? "Information request":
1171 "Managed");
1172
1173 return client_start(client, state);
1174
1175 error:
1176 client_reset(client);
1177 return r;
1178 }
1179
1180 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority) {
1181 int r;
1182
1183 assert_return(client, -EINVAL);
1184 assert_return(!client->event, -EBUSY);
1185
1186 if (event)
1187 client->event = sd_event_ref(event);
1188 else {
1189 r = sd_event_default(&client->event);
1190 if (r < 0)
1191 return 0;
1192 }
1193
1194 client->event_priority = priority;
1195
1196 return 0;
1197 }
1198
1199 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1200 assert_return(client, -EINVAL);
1201
1202 client->event = sd_event_unref(client->event);
1203
1204 return 0;
1205 }
1206
1207 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1208 if (!client)
1209 return NULL;
1210
1211 return client->event;
1212 }
1213
1214 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1215
1216 if (!client)
1217 return NULL;
1218
1219 assert(client->n_ref >= 1);
1220 client->n_ref++;
1221
1222 return client;
1223 }
1224
1225 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1226
1227 if (!client)
1228 return NULL;
1229
1230 assert(client->n_ref >= 1);
1231 client->n_ref--;
1232
1233 if (client->n_ref > 0)
1234 return NULL;
1235
1236 client_reset(client);
1237
1238 sd_dhcp6_client_detach_event(client);
1239
1240 free(client->req_opts);
1241 free(client);
1242
1243 return NULL;
1244 }
1245
1246 int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1247 _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1248 size_t t;
1249
1250 assert_return(ret, -EINVAL);
1251
1252 client = new0(sd_dhcp6_client, 1);
1253 if (!client)
1254 return -ENOMEM;
1255
1256 client->n_ref = 1;
1257
1258 client->ia_na.type = DHCP6_OPTION_IA_NA;
1259
1260 client->index = -1;
1261
1262 client->fd = -1;
1263
1264 client->req_opts_len = ELEMENTSOF(default_req_opts);
1265
1266 client->req_opts = new0(be16_t, client->req_opts_len);
1267 if (!client->req_opts)
1268 return -ENOMEM;
1269
1270 for (t = 0; t < client->req_opts_len; t++)
1271 client->req_opts[t] = htobe16(default_req_opts[t]);
1272
1273 *ret = client;
1274 client = NULL;
1275
1276 return 0;
1277 }