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