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