]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
Merge pull request #30513 from rpigott/resolved-ede
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <errno.h>
7 #include <sys/ioctl.h>
8 #include <linux/if_arp.h>
9 #include <linux/if_infiniband.h>
10
11 #include "sd-dhcp6-client.h"
12
13 #include "alloc-util.h"
14 #include "device-util.h"
15 #include "dhcp-duid-internal.h"
16 #include "dhcp6-internal.h"
17 #include "dhcp6-lease-internal.h"
18 #include "dns-domain.h"
19 #include "event-util.h"
20 #include "fd-util.h"
21 #include "hostname-util.h"
22 #include "in-addr-util.h"
23 #include "iovec-util.h"
24 #include "random-util.h"
25 #include "socket-util.h"
26 #include "sort-util.h"
27 #include "strv.h"
28 #include "web-util.h"
29
30 #define DHCP6_CLIENT_DONT_DESTROY(client) \
31 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
32
33 static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state);
34
35 int sd_dhcp6_client_set_callback(
36 sd_dhcp6_client *client,
37 sd_dhcp6_client_callback_t cb,
38 void *userdata) {
39
40 assert_return(client, -EINVAL);
41
42 client->callback = cb;
43 client->userdata = userdata;
44
45 return 0;
46 }
47
48 int dhcp6_client_set_state_callback(
49 sd_dhcp6_client *client,
50 sd_dhcp6_client_callback_t cb,
51 void *userdata) {
52
53 assert_return(client, -EINVAL);
54
55 client->state_callback = cb;
56 client->state_userdata = userdata;
57
58 return 0;
59 }
60
61 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
62 assert_return(client, -EINVAL);
63 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
64 assert_return(ifindex > 0, -EINVAL);
65
66 client->ifindex = ifindex;
67 return 0;
68 }
69
70 int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) {
71 assert_return(client, -EINVAL);
72 assert_return(ifname, -EINVAL);
73
74 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
75 return -EINVAL;
76
77 return free_and_strdup(&client->ifname, ifname);
78 }
79
80 int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret) {
81 int r;
82
83 assert_return(client, -EINVAL);
84
85 r = get_ifname(client->ifindex, &client->ifname);
86 if (r < 0)
87 return r;
88
89 if (ret)
90 *ret = client->ifname;
91
92 return 0;
93 }
94
95 int sd_dhcp6_client_set_local_address(
96 sd_dhcp6_client *client,
97 const struct in6_addr *local_address) {
98
99 assert_return(client, -EINVAL);
100 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
101 assert_return(local_address, -EINVAL);
102 assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
103
104 client->local_address = *local_address;
105
106 return 0;
107 }
108
109 int sd_dhcp6_client_set_mac(
110 sd_dhcp6_client *client,
111 const uint8_t *addr,
112 size_t addr_len,
113 uint16_t arp_type) {
114
115 assert_return(client, -EINVAL);
116 assert_return(addr, -EINVAL);
117 assert_return(addr_len <= sizeof(client->hw_addr.bytes), -EINVAL);
118
119 /* Unlike the other setters, it is OK to set a new MAC address while the client is running,
120 * as the MAC address is used only when setting DUID or IAID. */
121
122 if (arp_type == ARPHRD_ETHER)
123 assert_return(addr_len == ETH_ALEN, -EINVAL);
124 else if (arp_type == ARPHRD_INFINIBAND)
125 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
126 else {
127 client->arp_type = ARPHRD_NONE;
128 client->hw_addr.length = 0;
129 return 0;
130 }
131
132 client->arp_type = arp_type;
133 hw_addr_set(&client->hw_addr, addr, addr_len);
134
135 return 0;
136 }
137
138 int sd_dhcp6_client_set_prefix_delegation_hint(
139 sd_dhcp6_client *client,
140 uint8_t prefixlen,
141 const struct in6_addr *pd_prefix) {
142
143 _cleanup_free_ DHCP6Address *prefix = NULL;
144
145 assert_return(client, -EINVAL);
146 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
147
148 if (!pd_prefix) {
149 /* clear previous assignments. */
150 dhcp6_ia_clear_addresses(&client->ia_pd);
151 return 0;
152 }
153
154 assert_return(prefixlen > 0 && prefixlen <= 128, -EINVAL);
155
156 prefix = new(DHCP6Address, 1);
157 if (!prefix)
158 return -ENOMEM;
159
160 *prefix = (DHCP6Address) {
161 .iapdprefix.address = *pd_prefix,
162 .iapdprefix.prefixlen = prefixlen,
163 };
164
165 LIST_PREPEND(addresses, client->ia_pd.addresses, TAKE_PTR(prefix));
166 return 1;
167 }
168
169 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
170 int r;
171
172 assert_return(client, -EINVAL);
173 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
174
175 if (!v) {
176 /* Clear the previous assignments. */
177 ordered_set_clear(client->vendor_options);
178 return 0;
179 }
180
181 r = ordered_set_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v);
182 if (r < 0)
183 return r;
184
185 sd_dhcp6_option_ref(v);
186
187 return 1;
188 }
189
190 static int client_ensure_duid(sd_dhcp6_client *client) {
191 assert(client);
192
193 if (sd_dhcp_duid_is_set(&client->duid))
194 return 0;
195
196 return sd_dhcp6_client_set_duid_en(client);
197 }
198
199 /**
200 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
201 * without further modification. Otherwise, if duid_type is supported, DUID
202 * is set based on that type. Otherwise, an error is returned.
203 */
204 int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
205 int r;
206
207 assert_return(client, -EINVAL);
208 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
209
210 r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
211 if (r < 0)
212 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
213
214 return 0;
215 }
216
217 int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
218 int r;
219
220 assert_return(client, -EINVAL);
221 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
222
223 r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
224 if (r < 0)
225 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
226
227 return 0;
228 }
229
230 int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
231 int r;
232
233 assert_return(client, -EINVAL);
234 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
235
236 r = sd_dhcp_duid_set_en(&client->duid);
237 if (r < 0)
238 return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
239
240 return 0;
241 }
242
243 int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
244 int r;
245
246 assert_return(client, -EINVAL);
247 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
248
249 r = sd_dhcp_duid_set_uuid(&client->duid);
250 if (r < 0)
251 return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
252
253 return 0;
254 }
255
256 int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) {
257 int r;
258
259 assert_return(client, -EINVAL);
260 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
261 assert_return(duid || duid_len == 0, -EINVAL);
262
263 r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
264 if (r < 0)
265 return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
266
267 return 0;
268 }
269
270 int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
271 assert_return(client, -EINVAL);
272 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
273 assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
274
275 client->duid = *duid;
276 return 0;
277 }
278
279 int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
280 assert_return(client, -EINVAL);
281 assert_return(ret, -EINVAL);
282
283 if (!sd_dhcp_duid_is_set(&client->duid))
284 return -ENODATA;
285
286 *ret = &client->duid;
287 return 0;
288 }
289
290 int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
291 assert_return(client, -EINVAL);
292 assert_return(ret, -EINVAL);
293
294 if (!sd_dhcp_duid_is_set(&client->duid))
295 return -ENODATA;
296
297 return sd_dhcp_duid_to_string(&client->duid, ret);
298 }
299
300 int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
301 assert_return(client, -EINVAL);
302 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
303
304 client->ia_na.header.id = htobe32(iaid);
305 client->ia_pd.header.id = htobe32(iaid);
306 client->iaid_set = true;
307
308 return 0;
309 }
310
311 static int client_ensure_iaid(sd_dhcp6_client *client) {
312 int r;
313 uint32_t iaid;
314
315 assert(client);
316
317 if (client->iaid_set)
318 return 0;
319
320 r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
321 /* legacy_unstable_byteorder = */ true,
322 &iaid);
323 if (r < 0)
324 return r;
325
326 client->ia_na.header.id = iaid;
327 client->ia_pd.header.id = iaid;
328 client->iaid_set = true;
329
330 return 0;
331 }
332
333 int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
334 assert_return(client, -EINVAL);
335 assert_return(iaid, -EINVAL);
336
337 if (!client->iaid_set)
338 return -ENODATA;
339
340 *iaid = be32toh(client->ia_na.header.id);
341
342 return 0;
343 }
344
345 int sd_dhcp6_client_set_fqdn(
346 sd_dhcp6_client *client,
347 const char *fqdn) {
348
349 assert_return(client, -EINVAL);
350 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
351
352 /* Make sure FQDN qualifies as DNS and as Linux hostname */
353 if (fqdn &&
354 !(hostname_is_valid(fqdn, 0) && dns_name_is_valid(fqdn) > 0))
355 return -EINVAL;
356
357 return free_and_strdup(&client->fqdn, fqdn);
358 }
359
360 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
361 assert_return(client, -EINVAL);
362 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
363
364 client->information_request = enabled;
365
366 return 0;
367 }
368
369 int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
370 assert_return(client, -EINVAL);
371 assert_return(enabled, -EINVAL);
372
373 *enabled = client->information_request;
374
375 return 0;
376 }
377
378 static int be16_compare_func(const be16_t *a, const be16_t *b) {
379 return CMP(be16toh(*a), be16toh(*b));
380 }
381
382 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
383 be16_t opt;
384
385 assert_return(client, -EINVAL);
386 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
387
388 if (!dhcp6_option_can_request(option))
389 return -EINVAL;
390
391 opt = htobe16(option);
392 if (typesafe_bsearch(&opt, client->req_opts, client->n_req_opts, be16_compare_func))
393 return -EEXIST;
394
395 if (!GREEDY_REALLOC(client->req_opts, client->n_req_opts + 1))
396 return -ENOMEM;
397
398 client->req_opts[client->n_req_opts++] = opt;
399
400 /* Sort immediately to make the above binary search will work for the next time. */
401 typesafe_qsort(client->req_opts, client->n_req_opts, be16_compare_func);
402 return 0;
403 }
404
405 int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
406 assert_return(client, -EINVAL);
407 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
408 assert_return(mudurl, -EINVAL);
409 assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
410 assert_return(http_url_is_valid(mudurl), -EINVAL);
411
412 return free_and_strdup(&client->mudurl, mudurl);
413 }
414
415 int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) {
416 char **s;
417
418 assert_return(client, -EINVAL);
419 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
420 assert_return(!strv_isempty(user_class), -EINVAL);
421
422 STRV_FOREACH(p, user_class) {
423 size_t len = strlen(*p);
424
425 if (len > UINT16_MAX || len == 0)
426 return -EINVAL;
427 }
428
429 s = strv_copy(user_class);
430 if (!s)
431 return -ENOMEM;
432
433 return strv_free_and_replace(client->user_class, s);
434 }
435
436 int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) {
437 char **s;
438
439 assert_return(client, -EINVAL);
440 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
441 assert_return(!strv_isempty(vendor_class), -EINVAL);
442
443 STRV_FOREACH(p, vendor_class) {
444 size_t len = strlen(*p);
445
446 if (len > UINT16_MAX || len == 0)
447 return -EINVAL;
448 }
449
450 s = strv_copy(vendor_class);
451 if (!s)
452 return -ENOMEM;
453
454 return strv_free_and_replace(client->vendor_class, s);
455 }
456
457 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
458 assert_return(client, -EINVAL);
459 assert_return(delegation, -EINVAL);
460
461 *delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
462
463 return 0;
464 }
465
466 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
467 assert_return(client, -EINVAL);
468 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
469
470 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
471
472 return 0;
473 }
474
475 int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
476 assert_return(client, -EINVAL);
477 assert_return(request, -EINVAL);
478
479 *request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
480
481 return 0;
482 }
483
484 int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
485 assert_return(client, -EINVAL);
486 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
487
488 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
489
490 return 0;
491 }
492
493 int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
494 assert(client);
495 assert_se(network_test_mode_enabled());
496
497 /* This is for tests or fuzzers. */
498
499 client->transaction_id = transaction_id & htobe32(0x00ffffff);
500
501 return 0;
502 }
503
504 int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
505 assert_return(client, -EINVAL);
506 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
507
508 client->rapid_commit = enable;
509 return 0;
510 }
511
512 int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
513 assert_return(client, -EINVAL);
514 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
515
516 client->send_release = enable;
517 return 0;
518 }
519
520 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
521 assert_return(client, -EINVAL);
522
523 if (!client->lease)
524 return -ENOMSG;
525
526 if (ret)
527 *ret = client->lease;
528
529 return 0;
530 }
531
532 int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
533 int r;
534
535 assert_return(client, -EINVAL);
536 assert_return(v, -EINVAL);
537
538 r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp6_option_hash_ops, UINT_TO_PTR(v->option), v);
539 if (r < 0)
540 return r;
541
542 sd_dhcp6_option_ref(v);
543 return 0;
544 }
545
546 static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
547 assert(client);
548
549 if (client->state == state)
550 return;
551
552 log_dhcp6_client(client, "State changed: %s -> %s",
553 dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
554
555 client->state = state;
556
557 if (client->state_callback)
558 client->state_callback(client, state, client->state_userdata);
559 }
560
561 int dhcp6_client_get_state(sd_dhcp6_client *client) {
562 assert_return(client, -EINVAL);
563
564 return client->state;
565 }
566
567 static void client_notify(sd_dhcp6_client *client, int event) {
568 assert(client);
569
570 if (client->callback)
571 client->callback(client, event, client->userdata);
572 }
573
574 static void client_cleanup(sd_dhcp6_client *client) {
575 assert(client);
576
577 client->lease = sd_dhcp6_lease_unref(client->lease);
578
579 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
580 * even though the lease is freed below. */
581 client->information_request_time_usec = 0;
582 client->information_refresh_time_usec = 0;
583
584 (void) event_source_disable(client->receive_message);
585 (void) event_source_disable(client->timeout_resend);
586 (void) event_source_disable(client->timeout_expire);
587 (void) event_source_disable(client->timeout_t1);
588 (void) event_source_disable(client->timeout_t2);
589
590 client_set_state(client, DHCP6_STATE_STOPPED);
591 }
592
593 static void client_stop(sd_dhcp6_client *client, int error) {
594 DHCP6_CLIENT_DONT_DESTROY(client);
595
596 assert(client);
597
598 client_notify(client, error);
599
600 client_cleanup(client);
601 }
602
603 static int client_append_common_options_in_managed_mode(
604 sd_dhcp6_client *client,
605 uint8_t **buf,
606 size_t *offset,
607 const DHCP6IA *ia_na,
608 const DHCP6IA *ia_pd) {
609
610 int r;
611
612 assert(client);
613 assert(IN_SET(client->state,
614 DHCP6_STATE_SOLICITATION,
615 DHCP6_STATE_REQUEST,
616 DHCP6_STATE_RENEW,
617 DHCP6_STATE_REBIND,
618 DHCP6_STATE_STOPPING));
619 assert(buf);
620 assert(*buf);
621 assert(offset);
622
623 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) {
624 r = dhcp6_option_append_ia(buf, offset, ia_na);
625 if (r < 0)
626 return r;
627 }
628
629 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) {
630 r = dhcp6_option_append_ia(buf, offset, ia_pd);
631 if (r < 0)
632 return r;
633 }
634
635 if (client->state != DHCP6_STATE_STOPPING) {
636 r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
637 if (r < 0)
638 return r;
639 }
640
641 r = dhcp6_option_append_user_class(buf, offset, client->user_class);
642 if (r < 0)
643 return r;
644
645 r = dhcp6_option_append_vendor_class(buf, offset, client->vendor_class);
646 if (r < 0)
647 return r;
648
649 r = dhcp6_option_append_vendor_option(buf, offset, client->vendor_options);
650 if (r < 0)
651 return r;
652
653 return 0;
654 }
655
656 static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) {
657 assert(client);
658
659 switch (client->state) {
660 case DHCP6_STATE_INFORMATION_REQUEST:
661 return DHCP6_MESSAGE_INFORMATION_REQUEST;
662 case DHCP6_STATE_SOLICITATION:
663 return DHCP6_MESSAGE_SOLICIT;
664 case DHCP6_STATE_REQUEST:
665 return DHCP6_MESSAGE_REQUEST;
666 case DHCP6_STATE_RENEW:
667 return DHCP6_MESSAGE_RENEW;
668 case DHCP6_STATE_REBIND:
669 return DHCP6_MESSAGE_REBIND;
670 case DHCP6_STATE_STOPPING:
671 return DHCP6_MESSAGE_RELEASE;
672 default:
673 assert_not_reached();
674 }
675 }
676
677 static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
678 _cleanup_free_ be16_t *p = NULL;
679 be16_t *req_opts;
680 size_t n;
681
682 assert(client);
683 assert(buf);
684 assert(*buf);
685 assert(offset);
686
687 switch (client->state) {
688 case DHCP6_STATE_INFORMATION_REQUEST:
689 n = client->n_req_opts;
690 p = new(be16_t, n + 2);
691 if (!p)
692 return -ENOMEM;
693
694 memcpy_safe(p, client->req_opts, n * sizeof(be16_t));
695 p[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */
696 p[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */
697
698 typesafe_qsort(p, n, be16_compare_func);
699 req_opts = p;
700 break;
701
702 case DHCP6_STATE_SOLICITATION:
703 n = client->n_req_opts;
704 p = new(be16_t, n + 1);
705 if (!p)
706 return -ENOMEM;
707
708 memcpy_safe(p, client->req_opts, n * sizeof(be16_t));
709 p[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */
710
711 typesafe_qsort(p, n, be16_compare_func);
712 req_opts = p;
713 break;
714
715 case DHCP6_STATE_STOPPING:
716 return 0;
717
718 default:
719 n = client->n_req_opts;
720 req_opts = client->req_opts;
721 }
722
723 if (n == 0)
724 return 0;
725
726 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
727 }
728
729 static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
730 assert(client);
731 assert(buf);
732 assert(*buf);
733 assert(offset);
734
735 if (!client->mudurl)
736 return 0;
737
738 if (client->state == DHCP6_STATE_STOPPING)
739 return 0;
740
741 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_MUD_URL_V6,
742 strlen(client->mudurl), client->mudurl);
743 }
744
745 int dhcp6_client_send_message(sd_dhcp6_client *client) {
746 _cleanup_free_ uint8_t *buf = NULL;
747 struct in6_addr all_servers =
748 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
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, &all_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 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
1297 struct iovec iov;
1298 union sockaddr_union sa = {};
1299 struct msghdr msg = {
1300 .msg_name = &sa.sa,
1301 .msg_namelen = sizeof(sa),
1302 .msg_iov = &iov,
1303 .msg_iovlen = 1,
1304 .msg_control = &control,
1305 .msg_controllen = sizeof(control),
1306 };
1307 triple_timestamp t;
1308 _cleanup_free_ DHCP6Message *message = NULL;
1309 struct in6_addr *server_address = NULL;
1310 ssize_t buflen, len;
1311
1312 buflen = next_datagram_size_fd(fd);
1313 if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
1314 return 0;
1315 if (buflen < 0) {
1316 log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
1317 return 0;
1318 }
1319
1320 message = malloc(buflen);
1321 if (!message)
1322 return -ENOMEM;
1323
1324 iov = IOVEC_MAKE(message, buflen);
1325
1326 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
1327 if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
1328 return 0;
1329 if (len < 0) {
1330 log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
1331 return 0;
1332 }
1333 if ((size_t) len < sizeof(DHCP6Message)) {
1334 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
1335 return 0;
1336 }
1337
1338 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1339 if (msg.msg_namelen > 0) {
1340 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
1341 log_dhcp6_client(client, "Received message from invalid source, ignoring.");
1342 return 0;
1343 }
1344
1345 server_address = &sa.in6.sin6_addr;
1346 }
1347
1348 triple_timestamp_from_cmsg(&t, &msg);
1349
1350 if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
1351 return 0;
1352
1353 switch (client->state) {
1354 case DHCP6_STATE_INFORMATION_REQUEST:
1355 if (client_process_information(client, message, len, &t, server_address) < 0)
1356 return 0;
1357 break;
1358
1359 case DHCP6_STATE_SOLICITATION:
1360 if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0)
1361 return 0;
1362 break;
1363
1364 case DHCP6_STATE_REQUEST:
1365 case DHCP6_STATE_RENEW:
1366 case DHCP6_STATE_REBIND:
1367 if (client_process_reply(client, message, len, &t, server_address) < 0)
1368 return 0;
1369 break;
1370
1371 case DHCP6_STATE_BOUND:
1372 case DHCP6_STATE_STOPPED:
1373 case DHCP6_STATE_STOPPING:
1374 default:
1375 assert_not_reached();
1376 }
1377
1378 return 0;
1379 }
1380
1381 static int client_send_release(sd_dhcp6_client *client) {
1382 sd_dhcp6_lease *lease;
1383
1384 assert(client);
1385
1386 if (!client->send_release)
1387 return 0;
1388
1389 if (sd_dhcp6_client_get_lease(client, &lease) < 0)
1390 return 0;
1391
1392 if (!lease->ia_na && !lease->ia_pd)
1393 return 0;
1394
1395 client_set_state(client, DHCP6_STATE_STOPPING);
1396 return dhcp6_client_send_message(client);
1397 }
1398
1399 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1400 int r;
1401
1402 if (!client)
1403 return 0;
1404
1405 /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
1406 * engine is about to release its UDP socket unconditionally. */
1407 r = client_send_release(client);
1408 if (r < 0)
1409 log_dhcp6_client_errno(client, r,
1410 "Failed to send DHCP6 release message, ignoring: %m");
1411
1412 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1413
1414 client->receive_message = sd_event_source_unref(client->receive_message);
1415 client->fd = safe_close(client->fd);
1416
1417 return 0;
1418 }
1419
1420 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1421 assert_return(client, -EINVAL);
1422
1423 return client->state != DHCP6_STATE_STOPPED;
1424 }
1425
1426 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1427 DHCP6State state = DHCP6_STATE_SOLICITATION;
1428 int r;
1429
1430 assert_return(client, -EINVAL);
1431 assert_return(client->event, -EINVAL);
1432 assert_return(client->ifindex > 0, -EINVAL);
1433 assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
1434 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1435 assert_return(client->information_request || client->request_ia != 0, -EINVAL);
1436
1437 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1438 * request may be stored. */
1439 client->lease = sd_dhcp6_lease_unref(client->lease);
1440
1441 r = client_ensure_iaid(client);
1442 if (r < 0)
1443 return r;
1444
1445 r = client_ensure_duid(client);
1446 if (r < 0)
1447 return r;
1448
1449 if (client->fd < 0) {
1450 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1451 if (r < 0)
1452 return log_dhcp6_client_errno(client, r,
1453 "Failed to bind to UDP socket at address %s: %m",
1454 IN6_ADDR_TO_STRING(&client->local_address));
1455
1456 client->fd = r;
1457 }
1458
1459 if (!client->receive_message) {
1460 _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL;
1461
1462 r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client);
1463 if (r < 0)
1464 return r;
1465
1466 r = sd_event_source_set_priority(s, client->event_priority);
1467 if (r < 0)
1468 return r;
1469
1470 r = sd_event_source_set_description(s, "dhcp6-receive-message");
1471 if (r < 0)
1472 return r;
1473
1474 client->receive_message = TAKE_PTR(s);
1475 }
1476
1477 if (client->information_request) {
1478 usec_t t = now(CLOCK_MONOTONIC);
1479
1480 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1481 return 0;
1482
1483 client->information_request_time_usec = t;
1484 state = DHCP6_STATE_INFORMATION_REQUEST;
1485 }
1486
1487 log_dhcp6_client(client, "Starting in %s mode",
1488 client->information_request ? "Information request" : "Solicit");
1489
1490 return client_start_transaction(client, state);
1491 }
1492
1493 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
1494 int r;
1495
1496 assert_return(client, -EINVAL);
1497 assert_return(!client->event, -EBUSY);
1498 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1499
1500 if (event)
1501 client->event = sd_event_ref(event);
1502 else {
1503 r = sd_event_default(&client->event);
1504 if (r < 0)
1505 return 0;
1506 }
1507
1508 client->event_priority = priority;
1509
1510 return 0;
1511 }
1512
1513 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1514 assert_return(client, -EINVAL);
1515 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1516
1517 client->event = sd_event_unref(client->event);
1518
1519 return 0;
1520 }
1521
1522 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1523 assert_return(client, NULL);
1524
1525 return client->event;
1526 }
1527
1528 int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev) {
1529 assert_return(client, -EINVAL);
1530
1531 return device_unref_and_replace(client->dev, dev);
1532 }
1533
1534 static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1535 if (!client)
1536 return NULL;
1537
1538 sd_dhcp6_lease_unref(client->lease);
1539
1540 sd_event_source_disable_unref(client->receive_message);
1541 sd_event_source_disable_unref(client->timeout_resend);
1542 sd_event_source_disable_unref(client->timeout_expire);
1543 sd_event_source_disable_unref(client->timeout_t1);
1544 sd_event_source_disable_unref(client->timeout_t2);
1545 sd_event_unref(client->event);
1546
1547 client->fd = safe_close(client->fd);
1548
1549 sd_device_unref(client->dev);
1550
1551 free(client->req_opts);
1552 free(client->fqdn);
1553 free(client->mudurl);
1554 dhcp6_ia_clear_addresses(&client->ia_pd);
1555 ordered_hashmap_free(client->extra_options);
1556 ordered_set_free(client->vendor_options);
1557 strv_free(client->user_class);
1558 strv_free(client->vendor_class);
1559 free(client->ifname);
1560
1561 return mfree(client);
1562 }
1563
1564 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1565
1566 int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1567 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1568
1569 assert_return(ret, -EINVAL);
1570
1571 client = new(sd_dhcp6_client, 1);
1572 if (!client)
1573 return -ENOMEM;
1574
1575 *client = (sd_dhcp6_client) {
1576 .n_ref = 1,
1577 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1578 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1579 .ifindex = -1,
1580 .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
1581 .fd = -EBADF,
1582 .rapid_commit = true,
1583 };
1584
1585 *ret = TAKE_PTR(client);
1586
1587 return 0;
1588 }