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