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