]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <errno.h>
7 #include <sys/ioctl.h>
8 #include <linux/if_arp.h>
9 #include <linux/if_infiniband.h>
10
11 #include "sd-dhcp6-client.h"
12
13 #include "alloc-util.h"
14 #include "device-util.h"
15 #include "dhcp-identifier.h"
16 #include "dhcp6-internal.h"
17 #include "dhcp6-lease-internal.h"
18 #include "dns-domain.h"
19 #include "event-util.h"
20 #include "fd-util.h"
21 #include "hexdecoct.h"
22 #include "hostname-util.h"
23 #include "in-addr-util.h"
24 #include "io-util.h"
25 #include "random-util.h"
26 #include "socket-util.h"
27 #include "sort-util.h"
28 #include "strv.h"
29 #include "web-util.h"
30
31 #define DHCP6_CLIENT_DONT_DESTROY(client) \
32 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
33
34 static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state);
35
36 int sd_dhcp6_client_set_callback(
37 sd_dhcp6_client *client,
38 sd_dhcp6_client_callback_t cb,
39 void *userdata) {
40
41 assert_return(client, -EINVAL);
42
43 client->callback = cb;
44 client->userdata = userdata;
45
46 return 0;
47 }
48
49 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
50 assert_return(client, -EINVAL);
51 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
52 assert_return(ifindex > 0, -EINVAL);
53
54 client->ifindex = ifindex;
55 return 0;
56 }
57
58 int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) {
59 assert_return(client, -EINVAL);
60 assert_return(ifname, -EINVAL);
61
62 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
63 return -EINVAL;
64
65 return free_and_strdup(&client->ifname, ifname);
66 }
67
68 int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret) {
69 int r;
70
71 assert_return(client, -EINVAL);
72
73 r = get_ifname(client->ifindex, &client->ifname);
74 if (r < 0)
75 return r;
76
77 if (ret)
78 *ret = client->ifname;
79
80 return 0;
81 }
82
83 int sd_dhcp6_client_set_local_address(
84 sd_dhcp6_client *client,
85 const struct in6_addr *local_address) {
86
87 assert_return(client, -EINVAL);
88 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
89 assert_return(local_address, -EINVAL);
90 assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
91
92 client->local_address = *local_address;
93
94 return 0;
95 }
96
97 int sd_dhcp6_client_set_mac(
98 sd_dhcp6_client *client,
99 const uint8_t *addr,
100 size_t addr_len,
101 uint16_t arp_type) {
102
103 assert_return(client, -EINVAL);
104 assert_return(addr, -EINVAL);
105 assert_return(addr_len <= sizeof(client->hw_addr.bytes), -EINVAL);
106
107 /* Unlike the other setters, it is OK to set a new MAC address while the client is running,
108 * as the MAC address is used only when setting DUID or IAID. */
109
110 if (arp_type == ARPHRD_ETHER)
111 assert_return(addr_len == ETH_ALEN, -EINVAL);
112 else if (arp_type == ARPHRD_INFINIBAND)
113 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
114 else {
115 client->arp_type = ARPHRD_NONE;
116 client->hw_addr.length = 0;
117 return 0;
118 }
119
120 client->arp_type = arp_type;
121 hw_addr_set(&client->hw_addr, addr, addr_len);
122
123 return 0;
124 }
125
126 int sd_dhcp6_client_set_prefix_delegation_hint(
127 sd_dhcp6_client *client,
128 uint8_t prefixlen,
129 const struct in6_addr *pd_prefix) {
130
131 _cleanup_free_ DHCP6Address *prefix = NULL;
132
133 assert_return(client, -EINVAL);
134 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
135
136 if (!pd_prefix) {
137 /* clear previous assignments. */
138 dhcp6_ia_clear_addresses(&client->ia_pd);
139 return 0;
140 }
141
142 assert_return(prefixlen > 0 && prefixlen <= 128, -EINVAL);
143
144 prefix = new(DHCP6Address, 1);
145 if (!prefix)
146 return -ENOMEM;
147
148 *prefix = (DHCP6Address) {
149 .iapdprefix.address = *pd_prefix,
150 .iapdprefix.prefixlen = prefixlen,
151 };
152
153 LIST_PREPEND(addresses, client->ia_pd.addresses, TAKE_PTR(prefix));
154 return 1;
155 }
156
157 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
158 int r;
159
160 assert_return(client, -EINVAL);
161 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
162
163 if (!v) {
164 /* Clear the previous assignments. */
165 ordered_set_clear(client->vendor_options);
166 return 0;
167 }
168
169 r = ordered_set_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v);
170 if (r < 0)
171 return r;
172
173 sd_dhcp6_option_ref(v);
174
175 return 1;
176 }
177
178 static int client_ensure_duid(sd_dhcp6_client *client) {
179 assert(client);
180
181 if (client->duid_len != 0)
182 return 0;
183
184 return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
185 }
186
187 /**
188 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
189 * without further modification. Otherwise, if duid_type is supported, DUID
190 * is set based on that type. Otherwise, an error is returned.
191 */
192 static int dhcp6_client_set_duid_internal(
193 sd_dhcp6_client *client,
194 DUIDType duid_type,
195 const void *duid,
196 size_t duid_len,
197 usec_t llt_time) {
198 int r;
199
200 assert_return(client, -EINVAL);
201 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
202 assert_return(duid_len == 0 || duid, -EINVAL);
203
204 if (duid) {
205 r = dhcp_validate_duid_len(duid_type, duid_len, true);
206 if (r < 0) {
207 r = dhcp_validate_duid_len(duid_type, duid_len, false);
208 if (r < 0)
209 return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
210
211 log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type);
212 }
213
214 client->duid.type = htobe16(duid_type);
215 memcpy(&client->duid.raw.data, duid, duid_len);
216 client->duid_len = sizeof(client->duid.type) + duid_len;
217
218 } else {
219 r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
220 client->test_mode, &client->duid, &client->duid_len);
221 if (r == -EOPNOTSUPP)
222 return log_dhcp6_client_errno(client, r,
223 "Failed to set %s. MAC address is not set or "
224 "interface type is not supported.",
225 duid_type_to_string(duid_type));
226 if (r < 0)
227 return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
228 duid_type_to_string(duid_type));
229 }
230
231 return 0;
232 }
233
234 int sd_dhcp6_client_set_duid(
235 sd_dhcp6_client *client,
236 uint16_t duid_type,
237 const void *duid,
238 size_t duid_len) {
239 return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
240 }
241
242 int sd_dhcp6_client_set_duid_llt(
243 sd_dhcp6_client *client,
244 usec_t llt_time) {
245 return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
246 }
247
248 int sd_dhcp6_client_duid_as_string(
249 sd_dhcp6_client *client,
250 char **duid) {
251 _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
252 const char *v;
253 int r;
254
255 assert_return(client, -EINVAL);
256 assert_return(client->duid_len > 0, -ENODATA);
257 assert_return(duid, -EINVAL);
258
259 v = duid_type_to_string(be16toh(client->duid.type));
260 if (v) {
261 s = strdup(v);
262 if (!s)
263 return -ENOMEM;
264 } else {
265 r = asprintf(&s, "%0x", client->duid.type);
266 if (r < 0)
267 return -ENOMEM;
268 }
269
270 t = hexmem(&client->duid.raw.data, client->duid_len);
271 if (!t)
272 return -ENOMEM;
273
274 p = strjoin(s, ":", t);
275 if (!p)
276 return -ENOMEM;
277
278 *duid = TAKE_PTR(p);
279
280 return 0;
281 }
282
283 int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
284 assert_return(client, -EINVAL);
285 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
286
287 client->ia_na.header.id = htobe32(iaid);
288 client->ia_pd.header.id = htobe32(iaid);
289 client->iaid_set = true;
290
291 return 0;
292 }
293
294 static int client_ensure_iaid(sd_dhcp6_client *client) {
295 int r;
296 uint32_t iaid;
297
298 assert(client);
299
300 if (client->iaid_set)
301 return 0;
302
303 r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
304 /* legacy_unstable_byteorder = */ true,
305 &iaid);
306 if (r < 0)
307 return r;
308
309 client->ia_na.header.id = iaid;
310 client->ia_pd.header.id = iaid;
311 client->iaid_set = true;
312
313 return 0;
314 }
315
316 int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
317 assert_return(client, -EINVAL);
318 assert_return(iaid, -EINVAL);
319
320 if (!client->iaid_set)
321 return -ENODATA;
322
323 *iaid = be32toh(client->ia_na.header.id);
324
325 return 0;
326 }
327
328 void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) {
329 assert(client);
330
331 client->test_mode = test_mode;
332 }
333
334 int sd_dhcp6_client_set_fqdn(
335 sd_dhcp6_client *client,
336 const char *fqdn) {
337
338 assert_return(client, -EINVAL);
339 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
340
341 /* Make sure FQDN qualifies as DNS and as Linux hostname */
342 if (fqdn &&
343 !(hostname_is_valid(fqdn, 0) && dns_name_is_valid(fqdn) > 0))
344 return -EINVAL;
345
346 return free_and_strdup(&client->fqdn, fqdn);
347 }
348
349 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
350 assert_return(client, -EINVAL);
351 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
352
353 client->information_request = enabled;
354
355 return 0;
356 }
357
358 int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
359 assert_return(client, -EINVAL);
360 assert_return(enabled, -EINVAL);
361
362 *enabled = client->information_request;
363
364 return 0;
365 }
366
367 static int be16_compare_func(const be16_t *a, const be16_t *b) {
368 return CMP(be16toh(*a), be16toh(*b));
369 }
370
371 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
372 be16_t opt;
373
374 assert_return(client, -EINVAL);
375 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
376
377 if (!dhcp6_option_can_request(option))
378 return -EINVAL;
379
380 opt = htobe16(option);
381 if (typesafe_bsearch(&opt, client->req_opts, client->n_req_opts, be16_compare_func))
382 return -EEXIST;
383
384 if (!GREEDY_REALLOC(client->req_opts, client->n_req_opts + 1))
385 return -ENOMEM;
386
387 client->req_opts[client->n_req_opts++] = opt;
388
389 /* Sort immediately to make the above binary search will work for the next time. */
390 typesafe_qsort(client->req_opts, client->n_req_opts, be16_compare_func);
391 return 0;
392 }
393
394 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
395 assert_return(client, -EINVAL);
396 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
397 assert_return(mudurl, -EINVAL);
398 assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
399 assert_return(http_url_is_valid(mudurl), -EINVAL);
400
401 return free_and_strdup(&client->mudurl, mudurl);
402 }
403
404 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) {
405 char **s;
406
407 assert_return(client, -EINVAL);
408 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
409 assert_return(!strv_isempty(user_class), -EINVAL);
410
411 STRV_FOREACH(p, user_class) {
412 size_t len = strlen(*p);
413
414 if (len > UINT16_MAX || len == 0)
415 return -EINVAL;
416 }
417
418 s = strv_copy(user_class);
419 if (!s)
420 return -ENOMEM;
421
422 return strv_free_and_replace(client->user_class, s);
423 }
424
425 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) {
426 char **s;
427
428 assert_return(client, -EINVAL);
429 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
430 assert_return(!strv_isempty(vendor_class), -EINVAL);
431
432 STRV_FOREACH(p, vendor_class) {
433 size_t len = strlen(*p);
434
435 if (len > UINT16_MAX || len == 0)
436 return -EINVAL;
437 }
438
439 s = strv_copy(vendor_class);
440 if (!s)
441 return -ENOMEM;
442
443 return strv_free_and_replace(client->vendor_class, s);
444 }
445
446 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
447 assert_return(client, -EINVAL);
448 assert_return(delegation, -EINVAL);
449
450 *delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
451
452 return 0;
453 }
454
455 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
456 assert_return(client, -EINVAL);
457 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
458
459 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
460
461 return 0;
462 }
463
464 int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
465 assert_return(client, -EINVAL);
466 assert_return(request, -EINVAL);
467
468 *request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
469
470 return 0;
471 }
472
473 int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
474 assert_return(client, -EINVAL);
475 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
476
477 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
478
479 return 0;
480 }
481
482 int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
483 assert(client);
484 assert(client->test_mode);
485
486 /* This is for tests or fuzzers. */
487
488 client->transaction_id = transaction_id & htobe32(0x00ffffff);
489
490 return 0;
491 }
492
493 int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
494 assert_return(client, -EINVAL);
495 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
496
497 client->rapid_commit = enable;
498 return 0;
499 }
500
501 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
502 assert_return(client, -EINVAL);
503
504 if (!client->lease)
505 return -ENOMSG;
506
507 if (ret)
508 *ret = client->lease;
509
510 return 0;
511 }
512
513 int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
514 int r;
515
516 assert_return(client, -EINVAL);
517 assert_return(v, -EINVAL);
518
519 r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp6_option_hash_ops, UINT_TO_PTR(v->option), v);
520 if (r < 0)
521 return r;
522
523 sd_dhcp6_option_ref(v);
524 return 0;
525 }
526
527 static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
528 assert(client);
529
530 if (client->state == state)
531 return;
532
533 log_dhcp6_client(client, "State changed: %s -> %s",
534 dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
535
536 client->state = state;
537 }
538
539 static void client_notify(sd_dhcp6_client *client, int event) {
540 assert(client);
541
542 if (client->callback)
543 client->callback(client, event, client->userdata);
544 }
545
546 static void client_cleanup(sd_dhcp6_client *client) {
547 assert(client);
548
549 client->lease = sd_dhcp6_lease_unref(client->lease);
550
551 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
552 * even though the lease is freed below. */
553 client->information_request_time_usec = 0;
554 client->information_refresh_time_usec = 0;
555
556 (void) event_source_disable(client->receive_message);
557 (void) event_source_disable(client->timeout_resend);
558 (void) event_source_disable(client->timeout_expire);
559 (void) event_source_disable(client->timeout_t1);
560 (void) event_source_disable(client->timeout_t2);
561
562 client_set_state(client, DHCP6_STATE_STOPPED);
563 }
564
565 static void client_stop(sd_dhcp6_client *client, int error) {
566 DHCP6_CLIENT_DONT_DESTROY(client);
567
568 assert(client);
569
570 client_notify(client, error);
571
572 client_cleanup(client);
573 }
574
575 static int client_append_common_options_in_managed_mode(
576 sd_dhcp6_client *client,
577 uint8_t **buf,
578 size_t *offset,
579 const DHCP6IA *ia_na,
580 const DHCP6IA *ia_pd) {
581
582 int r;
583
584 assert(client);
585 assert(IN_SET(client->state,
586 DHCP6_STATE_SOLICITATION,
587 DHCP6_STATE_REQUEST,
588 DHCP6_STATE_RENEW,
589 DHCP6_STATE_REBIND));
590 assert(buf);
591 assert(*buf);
592 assert(offset);
593
594 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) {
595 r = dhcp6_option_append_ia(buf, offset, ia_na);
596 if (r < 0)
597 return r;
598 }
599
600 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) {
601 r = dhcp6_option_append_ia(buf, offset, ia_pd);
602 if (r < 0)
603 return r;
604 }
605
606 r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
607 if (r < 0)
608 return r;
609
610 r = dhcp6_option_append_user_class(buf, offset, client->user_class);
611 if (r < 0)
612 return r;
613
614 r = dhcp6_option_append_vendor_class(buf, offset, client->vendor_class);
615 if (r < 0)
616 return r;
617
618 r = dhcp6_option_append_vendor_option(buf, offset, client->vendor_options);
619 if (r < 0)
620 return r;
621
622 return 0;
623 }
624
625 static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) {
626 assert(client);
627
628 switch (client->state) {
629 case DHCP6_STATE_INFORMATION_REQUEST:
630 return DHCP6_MESSAGE_INFORMATION_REQUEST;
631 case DHCP6_STATE_SOLICITATION:
632 return DHCP6_MESSAGE_SOLICIT;
633 case DHCP6_STATE_REQUEST:
634 return DHCP6_MESSAGE_REQUEST;
635 case DHCP6_STATE_RENEW:
636 return DHCP6_MESSAGE_RENEW;
637 case DHCP6_STATE_REBIND:
638 return DHCP6_MESSAGE_REBIND;
639 default:
640 assert_not_reached();
641 }
642 }
643
644 static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
645 _cleanup_free_ be16_t *p = NULL;
646 be16_t *req_opts;
647 size_t n;
648
649 assert(client);
650 assert(buf);
651 assert(*buf);
652 assert(offset);
653
654 switch (client->state) {
655 case DHCP6_STATE_INFORMATION_REQUEST:
656 n = client->n_req_opts;
657 p = new(be16_t, n + 2);
658 if (!p)
659 return -ENOMEM;
660
661 memcpy_safe(p, client->req_opts, n * sizeof(be16_t));
662 p[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */
663 p[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */
664
665 typesafe_qsort(p, n, be16_compare_func);
666 req_opts = p;
667 break;
668
669 case DHCP6_STATE_SOLICITATION:
670 n = client->n_req_opts;
671 p = new(be16_t, n + 1);
672 if (!p)
673 return -ENOMEM;
674
675 memcpy_safe(p, client->req_opts, n * sizeof(be16_t));
676 p[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */
677
678 typesafe_qsort(p, n, be16_compare_func);
679 req_opts = p;
680 break;
681
682 default:
683 n = client->n_req_opts;
684 req_opts = client->req_opts;
685 }
686
687 if (n == 0)
688 return 0;
689
690 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
691 }
692
693 int dhcp6_client_send_message(sd_dhcp6_client *client) {
694 _cleanup_free_ uint8_t *buf = NULL;
695 struct in6_addr all_servers =
696 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
697 struct sd_dhcp6_option *j;
698 usec_t elapsed_usec, time_now;
699 be16_t elapsed_time;
700 DHCP6Message *message;
701 size_t offset;
702 int r;
703
704 assert(client);
705 assert(client->event);
706
707 r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
708 if (r < 0)
709 return r;
710
711 if (!GREEDY_REALLOC0(buf, offsetof(DHCP6Message, options)))
712 return -ENOMEM;
713
714 message = (DHCP6Message*) buf;
715 message->transaction_id = client->transaction_id;
716 message->type = client_message_type_from_state(client);
717 offset = offsetof(DHCP6Message, options);
718
719 switch (client->state) {
720 case DHCP6_STATE_INFORMATION_REQUEST:
721 break;
722
723 case DHCP6_STATE_SOLICITATION:
724 if (client->rapid_commit) {
725 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
726 if (r < 0)
727 return r;
728 }
729
730 r = client_append_common_options_in_managed_mode(client, &buf, &offset,
731 &client->ia_na, &client->ia_pd);
732 if (r < 0)
733 return r;
734 break;
735
736 case DHCP6_STATE_REQUEST:
737 case DHCP6_STATE_RENEW:
738
739 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID,
740 client->lease->serverid_len,
741 client->lease->serverid);
742 if (r < 0)
743 return r;
744
745 _fallthrough_;
746 case DHCP6_STATE_REBIND:
747
748 assert(client->lease);
749
750 r = client_append_common_options_in_managed_mode(client, &buf, &offset,
751 client->lease->ia_na, client->lease->ia_pd);
752 if (r < 0)
753 return r;
754 break;
755
756 case DHCP6_STATE_STOPPED:
757 case DHCP6_STATE_BOUND:
758 default:
759 assert_not_reached();
760 }
761
762 if (client->mudurl) {
763 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6,
764 strlen(client->mudurl), client->mudurl);
765 if (r < 0)
766 return r;
767 }
768
769 r = client_append_oro(client, &buf, &offset);
770 if (r < 0)
771 return r;
772
773 assert(client->duid_len > 0);
774 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
775 client->duid_len, &client->duid);
776 if (r < 0)
777 return r;
778
779 ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
780 r = dhcp6_option_append(&buf, &offset, j->option, j->length, j->data);
781 if (r < 0)
782 return r;
783 }
784
785 /* RFC 8415 Section 21.9.
786 * A client MUST include an Elapsed Time option in messages to indicate how long the client has
787 * been trying to complete a DHCP message exchange. */
788 elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
789 elapsed_time = htobe16(elapsed_usec);
790 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
791 if (r < 0)
792 return r;
793
794 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset);
795 if (r < 0)
796 return r;
797
798 log_dhcp6_client(client, "Sent %s",
799 dhcp6_message_type_to_string(client_message_type_from_state(client)));
800 return 0;
801 }
802
803 static usec_t client_timeout_compute_random(usec_t val) {
804 return usec_sub_unsigned(val, random_u64_range(val / 10));
805 }
806
807 static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
808 sd_dhcp6_client *client = ASSERT_PTR(userdata);
809 usec_t init_retransmit_time, max_retransmit_time;
810 int r;
811
812 assert(client->event);
813
814 switch (client->state) {
815 case DHCP6_STATE_INFORMATION_REQUEST:
816 init_retransmit_time = DHCP6_INF_TIMEOUT;
817 max_retransmit_time = DHCP6_INF_MAX_RT;
818 break;
819
820 case DHCP6_STATE_SOLICITATION:
821
822 if (client->retransmit_count > 0 && client->lease) {
823 (void) client_start_transaction(client, DHCP6_STATE_REQUEST);
824 return 0;
825 }
826
827 init_retransmit_time = DHCP6_SOL_TIMEOUT;
828 max_retransmit_time = DHCP6_SOL_MAX_RT;
829 break;
830
831 case DHCP6_STATE_REQUEST:
832
833 if (client->retransmit_count >= DHCP6_REQ_MAX_RC) {
834 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
835 return 0;
836 }
837
838 init_retransmit_time = DHCP6_REQ_TIMEOUT;
839 max_retransmit_time = DHCP6_REQ_MAX_RT;
840 break;
841
842 case DHCP6_STATE_RENEW:
843 init_retransmit_time = DHCP6_REN_TIMEOUT;
844 max_retransmit_time = DHCP6_REN_MAX_RT;
845
846 /* RFC 3315, section 18.1.3. says max retransmit duration will
847 be the remaining time until T2. Instead of setting MRD,
848 wait for T2 to trigger with the same end result */
849 break;
850
851 case DHCP6_STATE_REBIND:
852 init_retransmit_time = DHCP6_REB_TIMEOUT;
853 max_retransmit_time = DHCP6_REB_MAX_RT;
854
855 /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */
856 break;
857
858 case DHCP6_STATE_STOPPED:
859 case DHCP6_STATE_BOUND:
860 default:
861 assert_not_reached();
862 }
863
864 r = dhcp6_client_send_message(client);
865 if (r >= 0)
866 client->retransmit_count++;
867
868 if (client->retransmit_time == 0) {
869 client->retransmit_time = client_timeout_compute_random(init_retransmit_time);
870
871 if (client->state == DHCP6_STATE_SOLICITATION)
872 client->retransmit_time += init_retransmit_time / 10;
873
874 } else if (client->retransmit_time > max_retransmit_time / 2)
875 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
876 else
877 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
878
879 log_dhcp6_client(client, "Next retransmission in %s",
880 FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC));
881
882 r = event_reset_time_relative(client->event, &client->timeout_resend,
883 CLOCK_BOOTTIME,
884 client->retransmit_time, 10 * USEC_PER_MSEC,
885 client_timeout_resend, client,
886 client->event_priority, "dhcp6-resend-timer", true);
887 if (r < 0)
888 client_stop(client, r);
889
890 return 0;
891 }
892
893 static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) {
894 int r;
895
896 assert(client);
897 assert(client->event);
898
899 switch (state) {
900 case DHCP6_STATE_INFORMATION_REQUEST:
901 case DHCP6_STATE_SOLICITATION:
902 assert(client->state == DHCP6_STATE_STOPPED);
903 break;
904 case DHCP6_STATE_REQUEST:
905 assert(client->state == DHCP6_STATE_SOLICITATION);
906 break;
907 case DHCP6_STATE_RENEW:
908 assert(client->state == DHCP6_STATE_BOUND);
909 break;
910 case DHCP6_STATE_REBIND:
911 assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW));
912 break;
913 case DHCP6_STATE_STOPPED:
914 case DHCP6_STATE_BOUND:
915 default:
916 assert_not_reached();
917 }
918
919 client_set_state(client, state);
920
921 client->retransmit_time = 0;
922 client->retransmit_count = 0;
923 client->transaction_id = random_u32() & htobe32(0x00ffffff);
924
925 r = sd_event_now(client->event, CLOCK_BOOTTIME, &client->transaction_start);
926 if (r < 0)
927 goto error;
928
929 r = event_reset_time(client->event, &client->timeout_resend,
930 CLOCK_BOOTTIME,
931 0, 0,
932 client_timeout_resend, client,
933 client->event_priority, "dhcp6-resend-timeout", true);
934 if (r < 0)
935 goto error;
936
937 r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON);
938 if (r < 0)
939 goto error;
940
941 return 0;
942
943 error:
944 client_stop(client, r);
945 return r;
946 }
947
948 static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
949 sd_dhcp6_client *client = ASSERT_PTR(userdata);
950 DHCP6_CLIENT_DONT_DESTROY(client);
951 DHCP6State state;
952
953 (void) event_source_disable(client->timeout_expire);
954 (void) event_source_disable(client->timeout_t2);
955 (void) event_source_disable(client->timeout_t1);
956
957 state = client->state;
958
959 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
960
961 /* RFC 3315, section 18.1.4., says that "...the client may choose to
962 use a Solicit message to locate a new DHCP server..." */
963 if (state == DHCP6_STATE_REBIND)
964 (void) client_start_transaction(client, DHCP6_STATE_SOLICITATION);
965
966 return 0;
967 }
968
969 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
970 sd_dhcp6_client *client = ASSERT_PTR(userdata);
971
972 (void) event_source_disable(client->timeout_t2);
973 (void) event_source_disable(client->timeout_t1);
974
975 log_dhcp6_client(client, "Timeout T2");
976
977 (void) client_start_transaction(client, DHCP6_STATE_REBIND);
978
979 return 0;
980 }
981
982 static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
983 sd_dhcp6_client *client = ASSERT_PTR(userdata);
984
985 (void) event_source_disable(client->timeout_t1);
986
987 log_dhcp6_client(client, "Timeout T1");
988
989 (void) client_start_transaction(client, DHCP6_STATE_RENEW);
990
991 return 0;
992 }
993
994 static int client_enter_bound_state(sd_dhcp6_client *client) {
995 usec_t lifetime_t1, lifetime_t2, lifetime_valid;
996 int r;
997
998 assert(client);
999 assert(client->lease);
1000 assert(IN_SET(client->state,
1001 DHCP6_STATE_SOLICITATION,
1002 DHCP6_STATE_REQUEST,
1003 DHCP6_STATE_RENEW,
1004 DHCP6_STATE_REBIND));
1005
1006 (void) event_source_disable(client->receive_message);
1007 (void) event_source_disable(client->timeout_resend);
1008
1009 r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid);
1010 if (r < 0)
1011 goto error;
1012
1013 lifetime_t2 = client_timeout_compute_random(lifetime_t2);
1014 lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
1015
1016 if (lifetime_t1 == USEC_INFINITY) {
1017 log_dhcp6_client(client, "Infinite T1");
1018 event_source_disable(client->timeout_t1);
1019 } else {
1020 log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC));
1021 r = event_reset_time_relative(client->event, &client->timeout_t1,
1022 CLOCK_BOOTTIME,
1023 lifetime_t1, 10 * USEC_PER_SEC,
1024 client_timeout_t1, client,
1025 client->event_priority, "dhcp6-t1-timeout", true);
1026 if (r < 0)
1027 goto error;
1028 }
1029
1030 if (lifetime_t2 == USEC_INFINITY) {
1031 log_dhcp6_client(client, "Infinite T2");
1032 event_source_disable(client->timeout_t2);
1033 } else {
1034 log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC));
1035 r = event_reset_time_relative(client->event, &client->timeout_t2,
1036 CLOCK_BOOTTIME,
1037 lifetime_t2, 10 * USEC_PER_SEC,
1038 client_timeout_t2, client,
1039 client->event_priority, "dhcp6-t2-timeout", true);
1040 if (r < 0)
1041 goto error;
1042 }
1043
1044 if (lifetime_valid == USEC_INFINITY) {
1045 log_dhcp6_client(client, "Infinite valid lifetime");
1046 event_source_disable(client->timeout_expire);
1047 } else {
1048 log_dhcp6_client(client, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid, USEC_PER_SEC));
1049
1050 r = event_reset_time_relative(client->event, &client->timeout_expire,
1051 CLOCK_BOOTTIME,
1052 lifetime_valid, USEC_PER_SEC,
1053 client_timeout_expire, client,
1054 client->event_priority, "dhcp6-lease-expire", true);
1055 if (r < 0)
1056 goto error;
1057 }
1058
1059 client_set_state(client, DHCP6_STATE_BOUND);
1060 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
1061 return 0;
1062
1063 error:
1064 client_stop(client, r);
1065 return r;
1066 }
1067
1068 static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) {
1069 const char *type_str;
1070
1071 assert(client);
1072 assert(message);
1073
1074 type_str = dhcp6_message_type_to_string(message->type);
1075 if (type_str)
1076 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1077 "Received unexpected %s message, ignoring.", type_str);
1078 else
1079 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1080 "Received unsupported message type %u, ignoring.", message->type);
1081 }
1082
1083 static int client_process_information(
1084 sd_dhcp6_client *client,
1085 DHCP6Message *message,
1086 size_t len,
1087 const triple_timestamp *timestamp,
1088 const struct in6_addr *server_address) {
1089
1090 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1091 int r;
1092
1093 assert(client);
1094 assert(message);
1095
1096 if (message->type != DHCP6_MESSAGE_REPLY)
1097 return log_invalid_message_type(client, message);
1098
1099 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1100 if (r < 0)
1101 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
1102
1103 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1104
1105 sd_dhcp6_lease_unref(client->lease);
1106 client->lease = TAKE_PTR(lease);
1107
1108 /* Do not call client_stop() here, as it frees the acquired lease. */
1109 (void) event_source_disable(client->receive_message);
1110 (void) event_source_disable(client->timeout_resend);
1111 client_set_state(client, DHCP6_STATE_STOPPED);
1112
1113 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
1114 return 0;
1115 }
1116
1117 static int client_process_reply(
1118 sd_dhcp6_client *client,
1119 DHCP6Message *message,
1120 size_t len,
1121 const triple_timestamp *timestamp,
1122 const struct in6_addr *server_address) {
1123
1124 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1125 int r;
1126
1127 assert(client);
1128 assert(message);
1129
1130 if (message->type != DHCP6_MESSAGE_REPLY)
1131 return log_invalid_message_type(client, message);
1132
1133 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1134 if (r == -EADDRNOTAVAIL) {
1135
1136 /* If NoBinding status code is received, we cannot request the address anymore.
1137 * Let's restart transaction from the beginning. */
1138
1139 if (client->state == DHCP6_STATE_REQUEST)
1140 /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
1141 client_cleanup(client);
1142 else
1143 /* We need to notify the previous lease was expired. */
1144 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
1145
1146 return client_start_transaction(client, DHCP6_STATE_SOLICITATION);
1147 }
1148 if (r < 0)
1149 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
1150
1151 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1152
1153 sd_dhcp6_lease_unref(client->lease);
1154 client->lease = TAKE_PTR(lease);
1155
1156 return client_enter_bound_state(client);
1157 }
1158
1159 static int client_process_advertise_or_rapid_commit_reply(
1160 sd_dhcp6_client *client,
1161 DHCP6Message *message,
1162 size_t len,
1163 const triple_timestamp *timestamp,
1164 const struct in6_addr *server_address) {
1165
1166 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1167 uint8_t pref_advertise, pref_lease = 0;
1168 int r;
1169
1170 assert(client);
1171 assert(message);
1172
1173 if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY))
1174 return log_invalid_message_type(client, message);
1175
1176 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1177 if (r < 0)
1178 return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m",
1179 dhcp6_message_type_to_string(message->type));
1180
1181 if (message->type == DHCP6_MESSAGE_REPLY) {
1182 bool rapid_commit;
1183
1184 if (!client->rapid_commit)
1185 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1186 "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
1187
1188 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1189 if (r < 0)
1190 return r;
1191
1192 if (!rapid_commit)
1193 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1194 "Received reply message without rapid commit flag, ignoring.");
1195
1196 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1197
1198 sd_dhcp6_lease_unref(client->lease);
1199 client->lease = TAKE_PTR(lease);
1200
1201 return client_enter_bound_state(client);
1202 }
1203
1204 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1205 if (r < 0)
1206 return r;
1207
1208 if (client->lease) {
1209 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
1210 if (r < 0)
1211 return r;
1212 }
1213
1214 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1215
1216 if (!client->lease || pref_advertise > pref_lease) {
1217 /* If this is the first advertise message or has higher preference, then save the lease. */
1218 sd_dhcp6_lease_unref(client->lease);
1219 client->lease = TAKE_PTR(lease);
1220 }
1221
1222 if (pref_advertise == 255 || client->retransmit_count > 1)
1223 (void) client_start_transaction(client, DHCP6_STATE_REQUEST);
1224
1225 return 0;
1226 }
1227
1228 static int client_receive_message(
1229 sd_event_source *s,
1230 int fd, uint32_t
1231 revents,
1232 void *userdata) {
1233
1234 sd_dhcp6_client *client = ASSERT_PTR(userdata);
1235 DHCP6_CLIENT_DONT_DESTROY(client);
1236 /* This needs to be initialized with zero. See #20741. */
1237 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
1238 struct iovec iov;
1239 union sockaddr_union sa = {};
1240 struct msghdr msg = {
1241 .msg_name = &sa.sa,
1242 .msg_namelen = sizeof(sa),
1243 .msg_iov = &iov,
1244 .msg_iovlen = 1,
1245 .msg_control = &control,
1246 .msg_controllen = sizeof(control),
1247 };
1248 struct cmsghdr *cmsg;
1249 triple_timestamp t = {};
1250 _cleanup_free_ DHCP6Message *message = NULL;
1251 struct in6_addr *server_address = NULL;
1252 ssize_t buflen, len;
1253
1254 buflen = next_datagram_size_fd(fd);
1255 if (buflen < 0) {
1256 if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
1257 return 0;
1258
1259 log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
1260 return 0;
1261 }
1262
1263 message = malloc(buflen);
1264 if (!message)
1265 return -ENOMEM;
1266
1267 iov = IOVEC_MAKE(message, buflen);
1268
1269 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
1270 if (len < 0) {
1271 if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
1272 return 0;
1273
1274 log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
1275 return 0;
1276 }
1277 if ((size_t) len < sizeof(DHCP6Message)) {
1278 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
1279 return 0;
1280 }
1281
1282 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1283 if (msg.msg_namelen > 0) {
1284 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
1285 log_dhcp6_client(client, "Received message from invalid source, ignoring.");
1286 return 0;
1287 }
1288
1289 server_address = &sa.in6.sin6_addr;
1290 }
1291
1292 CMSG_FOREACH(cmsg, &msg) {
1293 if (cmsg->cmsg_level == SOL_SOCKET &&
1294 cmsg->cmsg_type == SO_TIMESTAMP &&
1295 cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
1296 triple_timestamp_from_realtime(&t, timeval_load(CMSG_TYPED_DATA(cmsg, struct timeval)));
1297 }
1298
1299 if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
1300 return 0;
1301
1302 switch (client->state) {
1303 case DHCP6_STATE_INFORMATION_REQUEST:
1304 if (client_process_information(client, message, len, &t, server_address) < 0)
1305 return 0;
1306 break;
1307
1308 case DHCP6_STATE_SOLICITATION:
1309 if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0)
1310 return 0;
1311 break;
1312
1313 case DHCP6_STATE_REQUEST:
1314 case DHCP6_STATE_RENEW:
1315 case DHCP6_STATE_REBIND:
1316 if (client_process_reply(client, message, len, &t, server_address) < 0)
1317 return 0;
1318 break;
1319
1320 case DHCP6_STATE_BOUND:
1321 case DHCP6_STATE_STOPPED:
1322 default:
1323 assert_not_reached();
1324 }
1325
1326 return 0;
1327 }
1328
1329 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1330 if (!client)
1331 return 0;
1332
1333 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1334
1335 client->receive_message = sd_event_source_unref(client->receive_message);
1336 client->fd = safe_close(client->fd);
1337
1338 return 0;
1339 }
1340
1341 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1342 assert_return(client, -EINVAL);
1343
1344 return client->state != DHCP6_STATE_STOPPED;
1345 }
1346
1347 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1348 DHCP6State state = DHCP6_STATE_SOLICITATION;
1349 int r;
1350
1351 assert_return(client, -EINVAL);
1352 assert_return(client->event, -EINVAL);
1353 assert_return(client->ifindex > 0, -EINVAL);
1354 assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
1355 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1356 assert_return(client->information_request || client->request_ia != 0, -EINVAL);
1357
1358 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1359 * request may be stored. */
1360 client->lease = sd_dhcp6_lease_unref(client->lease);
1361
1362 r = client_ensure_iaid(client);
1363 if (r < 0)
1364 return r;
1365
1366 r = client_ensure_duid(client);
1367 if (r < 0)
1368 return r;
1369
1370 if (client->fd < 0) {
1371 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1372 if (r < 0)
1373 return log_dhcp6_client_errno(client, r,
1374 "Failed to bind to UDP socket at address %s: %m",
1375 IN6_ADDR_TO_STRING(&client->local_address));
1376
1377 client->fd = r;
1378 }
1379
1380 if (!client->receive_message) {
1381 _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL;
1382
1383 r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client);
1384 if (r < 0)
1385 return r;
1386
1387 r = sd_event_source_set_priority(s, client->event_priority);
1388 if (r < 0)
1389 return r;
1390
1391 r = sd_event_source_set_description(s, "dhcp6-receive-message");
1392 if (r < 0)
1393 return r;
1394
1395 client->receive_message = TAKE_PTR(s);
1396 }
1397
1398 if (client->information_request) {
1399 usec_t t = now(CLOCK_MONOTONIC);
1400
1401 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1402 return 0;
1403
1404 client->information_request_time_usec = t;
1405 state = DHCP6_STATE_INFORMATION_REQUEST;
1406 }
1407
1408 log_dhcp6_client(client, "Starting in %s mode",
1409 client->information_request ? "Information request" : "Solicit");
1410
1411 return client_start_transaction(client, state);
1412 }
1413
1414 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
1415 int r;
1416
1417 assert_return(client, -EINVAL);
1418 assert_return(!client->event, -EBUSY);
1419 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1420
1421 if (event)
1422 client->event = sd_event_ref(event);
1423 else {
1424 r = sd_event_default(&client->event);
1425 if (r < 0)
1426 return 0;
1427 }
1428
1429 client->event_priority = priority;
1430
1431 return 0;
1432 }
1433
1434 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1435 assert_return(client, -EINVAL);
1436 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1437
1438 client->event = sd_event_unref(client->event);
1439
1440 return 0;
1441 }
1442
1443 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1444 assert_return(client, NULL);
1445
1446 return client->event;
1447 }
1448
1449 int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev) {
1450 assert_return(client, -EINVAL);
1451
1452 return device_unref_and_replace(client->dev, dev);
1453 }
1454
1455 static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1456 if (!client)
1457 return NULL;
1458
1459 sd_dhcp6_lease_unref(client->lease);
1460
1461 sd_event_source_disable_unref(client->receive_message);
1462 sd_event_source_disable_unref(client->timeout_resend);
1463 sd_event_source_disable_unref(client->timeout_expire);
1464 sd_event_source_disable_unref(client->timeout_t1);
1465 sd_event_source_disable_unref(client->timeout_t2);
1466 sd_event_unref(client->event);
1467
1468 client->fd = safe_close(client->fd);
1469
1470 sd_device_unref(client->dev);
1471
1472 free(client->req_opts);
1473 free(client->fqdn);
1474 free(client->mudurl);
1475 dhcp6_ia_clear_addresses(&client->ia_pd);
1476 ordered_hashmap_free(client->extra_options);
1477 ordered_set_free(client->vendor_options);
1478 strv_free(client->user_class);
1479 strv_free(client->vendor_class);
1480 free(client->ifname);
1481
1482 return mfree(client);
1483 }
1484
1485 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1486
1487 int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1488 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1489
1490 assert_return(ret, -EINVAL);
1491
1492 client = new(sd_dhcp6_client, 1);
1493 if (!client)
1494 return -ENOMEM;
1495
1496 *client = (sd_dhcp6_client) {
1497 .n_ref = 1,
1498 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1499 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1500 .ifindex = -1,
1501 .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
1502 .fd = -EBADF,
1503 .rapid_commit = true,
1504 };
1505
1506 *ret = TAKE_PTR(client);
1507
1508 return 0;
1509 }