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