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