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