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