]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
Merge pull request #33386 from yuwata/journal-timestamp
[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
515 client->send_release = enable;
516 return 0;
517 }
518
519 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
520 assert_return(client, -EINVAL);
521
522 if (!client->lease)
523 return -ENOMSG;
524
525 if (ret)
526 *ret = client->lease;
527
528 return 0;
529 }
530
531 int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
532 int r;
533
534 assert_return(client, -EINVAL);
535 assert_return(v, -EINVAL);
536
537 r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp6_option_hash_ops, UINT_TO_PTR(v->option), v);
538 if (r < 0)
539 return r;
540
541 sd_dhcp6_option_ref(v);
542 return 0;
543 }
544
545 static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
546 assert(client);
547
548 if (client->state == state)
549 return;
550
551 log_dhcp6_client(client, "State changed: %s -> %s",
552 dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
553
554 client->state = state;
555
556 if (client->state_callback)
557 client->state_callback(client, state, client->state_userdata);
558 }
559
560 int dhcp6_client_get_state(sd_dhcp6_client *client) {
561 assert_return(client, -EINVAL);
562
563 return client->state;
564 }
565
566 static void client_notify(sd_dhcp6_client *client, int event) {
567 assert(client);
568
569 if (client->callback)
570 client->callback(client, event, client->userdata);
571 }
572
573 static void client_cleanup(sd_dhcp6_client *client) {
574 assert(client);
575
576 client->lease = sd_dhcp6_lease_unref(client->lease);
577
578 /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
579 * even though the lease is freed below. */
580 client->information_request_time_usec = 0;
581 client->information_refresh_time_usec = 0;
582
583 (void) event_source_disable(client->receive_message);
584 (void) event_source_disable(client->timeout_resend);
585 (void) event_source_disable(client->timeout_expire);
586 (void) event_source_disable(client->timeout_t1);
587 (void) event_source_disable(client->timeout_t2);
588
589 client_set_state(client, DHCP6_STATE_STOPPED);
590 }
591
592 static void client_stop(sd_dhcp6_client *client, int error) {
593 DHCP6_CLIENT_DONT_DESTROY(client);
594
595 assert(client);
596
597 client_notify(client, error);
598
599 client_cleanup(client);
600 }
601
602 static int client_append_common_options_in_managed_mode(
603 sd_dhcp6_client *client,
604 uint8_t **buf,
605 size_t *offset,
606 const DHCP6IA *ia_na,
607 const DHCP6IA *ia_pd) {
608
609 int r;
610
611 assert(client);
612 assert(IN_SET(client->state,
613 DHCP6_STATE_SOLICITATION,
614 DHCP6_STATE_REQUEST,
615 DHCP6_STATE_RENEW,
616 DHCP6_STATE_REBIND,
617 DHCP6_STATE_STOPPING));
618 assert(buf);
619 assert(*buf);
620 assert(offset);
621
622 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) {
623 r = dhcp6_option_append_ia(buf, offset, ia_na);
624 if (r < 0)
625 return r;
626 }
627
628 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) {
629 r = dhcp6_option_append_ia(buf, offset, ia_pd);
630 if (r < 0)
631 return r;
632 }
633
634 if (client->state != DHCP6_STATE_STOPPING) {
635 r = dhcp6_option_append_fqdn(buf, offset, client->fqdn);
636 if (r < 0)
637 return r;
638 }
639
640 r = dhcp6_option_append_user_class(buf, offset, client->user_class);
641 if (r < 0)
642 return r;
643
644 r = dhcp6_option_append_vendor_class(buf, offset, client->vendor_class);
645 if (r < 0)
646 return r;
647
648 r = dhcp6_option_append_vendor_option(buf, offset, client->vendor_options);
649 if (r < 0)
650 return r;
651
652 return 0;
653 }
654
655 static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) {
656 assert(client);
657
658 switch (client->state) {
659 case DHCP6_STATE_INFORMATION_REQUEST:
660 return DHCP6_MESSAGE_INFORMATION_REQUEST;
661 case DHCP6_STATE_SOLICITATION:
662 return DHCP6_MESSAGE_SOLICIT;
663 case DHCP6_STATE_REQUEST:
664 return DHCP6_MESSAGE_REQUEST;
665 case DHCP6_STATE_RENEW:
666 return DHCP6_MESSAGE_RENEW;
667 case DHCP6_STATE_REBIND:
668 return DHCP6_MESSAGE_REBIND;
669 case DHCP6_STATE_STOPPING:
670 return DHCP6_MESSAGE_RELEASE;
671 default:
672 assert_not_reached();
673 }
674 }
675
676 static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
677 _cleanup_free_ be16_t *p = NULL;
678 be16_t *req_opts;
679 size_t n;
680
681 assert(client);
682 assert(buf);
683 assert(*buf);
684 assert(offset);
685
686 switch (client->state) {
687 case DHCP6_STATE_INFORMATION_REQUEST:
688 n = client->n_req_opts;
689 p = new(be16_t, n + 2);
690 if (!p)
691 return -ENOMEM;
692
693 memcpy_safe(p, client->req_opts, n * sizeof(be16_t));
694 p[n++] = htobe16(SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME); /* RFC 8415 section 21.23 */
695 p[n++] = htobe16(SD_DHCP6_OPTION_INF_MAX_RT); /* RFC 8415 section 21.25 */
696
697 typesafe_qsort(p, n, be16_compare_func);
698 req_opts = p;
699 break;
700
701 case DHCP6_STATE_SOLICITATION:
702 n = client->n_req_opts;
703 p = new(be16_t, n + 1);
704 if (!p)
705 return -ENOMEM;
706
707 memcpy_safe(p, client->req_opts, n * sizeof(be16_t));
708 p[n++] = htobe16(SD_DHCP6_OPTION_SOL_MAX_RT); /* RFC 8415 section 21.24 */
709
710 typesafe_qsort(p, n, be16_compare_func);
711 req_opts = p;
712 break;
713
714 case DHCP6_STATE_STOPPING:
715 return 0;
716
717 default:
718 n = client->n_req_opts;
719 req_opts = client->req_opts;
720 }
721
722 if (n == 0)
723 return 0;
724
725 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
726 }
727
728 static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) {
729 assert(client);
730 assert(buf);
731 assert(*buf);
732 assert(offset);
733
734 if (!client->mudurl)
735 return 0;
736
737 if (client->state == DHCP6_STATE_STOPPING)
738 return 0;
739
740 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_MUD_URL_V6,
741 strlen(client->mudurl), client->mudurl);
742 }
743
744 int dhcp6_client_send_message(sd_dhcp6_client *client) {
745 _cleanup_free_ uint8_t *buf = NULL;
746 struct sd_dhcp6_option *j;
747 usec_t elapsed_usec, time_now;
748 be16_t elapsed_time;
749 DHCP6Message *message;
750 size_t offset;
751 int r;
752
753 assert(client);
754 assert(client->event);
755
756 r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
757 if (r < 0)
758 return r;
759
760 if (!GREEDY_REALLOC0(buf, offsetof(DHCP6Message, options)))
761 return -ENOMEM;
762
763 message = (DHCP6Message*) buf;
764 message->transaction_id = client->transaction_id;
765 message->type = client_message_type_from_state(client);
766 offset = offsetof(DHCP6Message, options);
767
768 switch (client->state) {
769 case DHCP6_STATE_INFORMATION_REQUEST:
770 break;
771
772 case DHCP6_STATE_SOLICITATION:
773 if (client->rapid_commit) {
774 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
775 if (r < 0)
776 return r;
777 }
778
779 r = client_append_common_options_in_managed_mode(client, &buf, &offset,
780 &client->ia_na, &client->ia_pd);
781 if (r < 0)
782 return r;
783 break;
784
785 case DHCP6_STATE_REQUEST:
786 case DHCP6_STATE_RENEW:
787 case DHCP6_STATE_STOPPING:
788 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID,
789 client->lease->serverid_len,
790 client->lease->serverid);
791 if (r < 0)
792 return r;
793
794 _fallthrough_;
795 case DHCP6_STATE_REBIND:
796
797 assert(client->lease);
798
799 r = client_append_common_options_in_managed_mode(client, &buf, &offset,
800 client->lease->ia_na, client->lease->ia_pd);
801 if (r < 0)
802 return r;
803 break;
804
805 case DHCP6_STATE_BOUND:
806 case DHCP6_STATE_STOPPED:
807 default:
808 assert_not_reached();
809 }
810
811 r = client_append_mudurl(client, &buf, &offset);
812 if (r < 0)
813 return r;
814
815 r = client_append_oro(client, &buf, &offset);
816 if (r < 0)
817 return r;
818
819 assert(sd_dhcp_duid_is_set(&client->duid));
820 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
821 client->duid.size, &client->duid.duid);
822 if (r < 0)
823 return r;
824
825 ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
826 r = dhcp6_option_append(&buf, &offset, j->option, j->length, j->data);
827 if (r < 0)
828 return r;
829 }
830
831 /* RFC 8415 Section 21.9.
832 * A client MUST include an Elapsed Time option in messages to indicate how long the client has
833 * been trying to complete a DHCP message exchange. */
834 elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
835 elapsed_time = htobe16(elapsed_usec);
836 r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
837 if (r < 0)
838 return r;
839
840 r = dhcp6_network_send_udp_socket(client->fd, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS, buf, offset);
841 if (r < 0)
842 return r;
843
844 log_dhcp6_client(client, "Sent %s",
845 dhcp6_message_type_to_string(client_message_type_from_state(client)));
846 return 0;
847 }
848
849 static usec_t client_timeout_compute_random(usec_t val) {
850 return usec_sub_unsigned(val, random_u64_range(val / 10));
851 }
852
853 static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
854 sd_dhcp6_client *client = ASSERT_PTR(userdata);
855 usec_t init_retransmit_time, max_retransmit_time;
856 int r;
857
858 assert(client->event);
859
860 switch (client->state) {
861 case DHCP6_STATE_INFORMATION_REQUEST:
862 init_retransmit_time = DHCP6_INF_TIMEOUT;
863 max_retransmit_time = DHCP6_INF_MAX_RT;
864 break;
865
866 case DHCP6_STATE_SOLICITATION:
867
868 if (client->retransmit_count > 0 && client->lease) {
869 (void) client_start_transaction(client, DHCP6_STATE_REQUEST);
870 return 0;
871 }
872
873 init_retransmit_time = DHCP6_SOL_TIMEOUT;
874 max_retransmit_time = DHCP6_SOL_MAX_RT;
875 break;
876
877 case DHCP6_STATE_REQUEST:
878
879 if (client->retransmit_count >= DHCP6_REQ_MAX_RC) {
880 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
881 return 0;
882 }
883
884 init_retransmit_time = DHCP6_REQ_TIMEOUT;
885 max_retransmit_time = DHCP6_REQ_MAX_RT;
886 break;
887
888 case DHCP6_STATE_RENEW:
889 init_retransmit_time = DHCP6_REN_TIMEOUT;
890 max_retransmit_time = DHCP6_REN_MAX_RT;
891
892 /* RFC 3315, section 18.1.3. says max retransmit duration will
893 be the remaining time until T2. Instead of setting MRD,
894 wait for T2 to trigger with the same end result */
895 break;
896
897 case DHCP6_STATE_REBIND:
898 init_retransmit_time = DHCP6_REB_TIMEOUT;
899 max_retransmit_time = DHCP6_REB_MAX_RT;
900
901 /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */
902 break;
903
904 case DHCP6_STATE_STOPPED:
905 case DHCP6_STATE_STOPPING:
906 case DHCP6_STATE_BOUND:
907 default:
908 assert_not_reached();
909 }
910
911 r = dhcp6_client_send_message(client);
912 if (r >= 0)
913 client->retransmit_count++;
914
915 if (client->retransmit_time == 0) {
916 client->retransmit_time = client_timeout_compute_random(init_retransmit_time);
917
918 if (client->state == DHCP6_STATE_SOLICITATION)
919 client->retransmit_time += init_retransmit_time / 10;
920
921 } else if (client->retransmit_time > max_retransmit_time / 2)
922 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
923 else
924 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
925
926 log_dhcp6_client(client, "Next retransmission in %s",
927 FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC));
928
929 r = event_reset_time_relative(client->event, &client->timeout_resend,
930 CLOCK_BOOTTIME,
931 client->retransmit_time, 10 * USEC_PER_MSEC,
932 client_timeout_resend, client,
933 client->event_priority, "dhcp6-resend-timer", true);
934 if (r < 0)
935 client_stop(client, r);
936
937 return 0;
938 }
939
940 static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) {
941 int r;
942
943 assert(client);
944 assert(client->event);
945
946 switch (state) {
947 case DHCP6_STATE_INFORMATION_REQUEST:
948 case DHCP6_STATE_SOLICITATION:
949 assert(client->state == DHCP6_STATE_STOPPED);
950 break;
951 case DHCP6_STATE_REQUEST:
952 assert(client->state == DHCP6_STATE_SOLICITATION);
953 break;
954 case DHCP6_STATE_RENEW:
955 assert(client->state == DHCP6_STATE_BOUND);
956 break;
957 case DHCP6_STATE_REBIND:
958 assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW));
959 break;
960 case DHCP6_STATE_STOPPED:
961 case DHCP6_STATE_STOPPING:
962 case DHCP6_STATE_BOUND:
963 default:
964 assert_not_reached();
965 }
966
967 client_set_state(client, state);
968
969 client->retransmit_time = 0;
970 client->retransmit_count = 0;
971 client->transaction_id = random_u32() & htobe32(0x00ffffff);
972
973 r = sd_event_now(client->event, CLOCK_BOOTTIME, &client->transaction_start);
974 if (r < 0)
975 goto error;
976
977 r = event_reset_time(client->event, &client->timeout_resend,
978 CLOCK_BOOTTIME,
979 0, 0,
980 client_timeout_resend, client,
981 client->event_priority, "dhcp6-resend-timeout", true);
982 if (r < 0)
983 goto error;
984
985 r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON);
986 if (r < 0)
987 goto error;
988
989 return 0;
990
991 error:
992 client_stop(client, r);
993 return r;
994 }
995
996 static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
997 sd_dhcp6_client *client = ASSERT_PTR(userdata);
998 DHCP6_CLIENT_DONT_DESTROY(client);
999 DHCP6State state;
1000
1001 (void) event_source_disable(client->timeout_expire);
1002 (void) event_source_disable(client->timeout_t2);
1003 (void) event_source_disable(client->timeout_t1);
1004
1005 state = client->state;
1006
1007 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
1008
1009 /* RFC 3315, section 18.1.4., says that "...the client may choose to
1010 use a Solicit message to locate a new DHCP server..." */
1011 if (state == DHCP6_STATE_REBIND)
1012 (void) client_start_transaction(client, DHCP6_STATE_SOLICITATION);
1013
1014 return 0;
1015 }
1016
1017 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
1018 sd_dhcp6_client *client = ASSERT_PTR(userdata);
1019
1020 (void) event_source_disable(client->timeout_t2);
1021 (void) event_source_disable(client->timeout_t1);
1022
1023 log_dhcp6_client(client, "Timeout T2");
1024
1025 (void) client_start_transaction(client, DHCP6_STATE_REBIND);
1026
1027 return 0;
1028 }
1029
1030 static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
1031 sd_dhcp6_client *client = ASSERT_PTR(userdata);
1032
1033 (void) event_source_disable(client->timeout_t1);
1034
1035 log_dhcp6_client(client, "Timeout T1");
1036
1037 (void) client_start_transaction(client, DHCP6_STATE_RENEW);
1038
1039 return 0;
1040 }
1041
1042 static int client_enter_bound_state(sd_dhcp6_client *client) {
1043 usec_t lifetime_t1, lifetime_t2, lifetime_valid;
1044 int r;
1045
1046 assert(client);
1047 assert(client->lease);
1048 assert(IN_SET(client->state,
1049 DHCP6_STATE_SOLICITATION,
1050 DHCP6_STATE_REQUEST,
1051 DHCP6_STATE_RENEW,
1052 DHCP6_STATE_REBIND));
1053
1054 (void) event_source_disable(client->receive_message);
1055 (void) event_source_disable(client->timeout_resend);
1056
1057 r = sd_dhcp6_lease_get_t1(client->lease, &lifetime_t1);
1058 if (r < 0)
1059 goto error;
1060
1061 r = sd_dhcp6_lease_get_t2(client->lease, &lifetime_t2);
1062 if (r < 0)
1063 goto error;
1064
1065 r = sd_dhcp6_lease_get_valid_lifetime(client->lease, &lifetime_valid);
1066 if (r < 0)
1067 goto error;
1068
1069 lifetime_t2 = client_timeout_compute_random(lifetime_t2);
1070 lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
1071
1072 if (lifetime_t1 == USEC_INFINITY) {
1073 log_dhcp6_client(client, "Infinite T1");
1074 event_source_disable(client->timeout_t1);
1075 } else {
1076 log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC));
1077 r = event_reset_time_relative(client->event, &client->timeout_t1,
1078 CLOCK_BOOTTIME,
1079 lifetime_t1, 10 * USEC_PER_SEC,
1080 client_timeout_t1, client,
1081 client->event_priority, "dhcp6-t1-timeout", true);
1082 if (r < 0)
1083 goto error;
1084 }
1085
1086 if (lifetime_t2 == USEC_INFINITY) {
1087 log_dhcp6_client(client, "Infinite T2");
1088 event_source_disable(client->timeout_t2);
1089 } else {
1090 log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC));
1091 r = event_reset_time_relative(client->event, &client->timeout_t2,
1092 CLOCK_BOOTTIME,
1093 lifetime_t2, 10 * USEC_PER_SEC,
1094 client_timeout_t2, client,
1095 client->event_priority, "dhcp6-t2-timeout", true);
1096 if (r < 0)
1097 goto error;
1098 }
1099
1100 if (lifetime_valid == USEC_INFINITY) {
1101 log_dhcp6_client(client, "Infinite valid lifetime");
1102 event_source_disable(client->timeout_expire);
1103 } else {
1104 log_dhcp6_client(client, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid, USEC_PER_SEC));
1105
1106 r = event_reset_time_relative(client->event, &client->timeout_expire,
1107 CLOCK_BOOTTIME,
1108 lifetime_valid, USEC_PER_SEC,
1109 client_timeout_expire, client,
1110 client->event_priority, "dhcp6-lease-expire", true);
1111 if (r < 0)
1112 goto error;
1113 }
1114
1115 client_set_state(client, DHCP6_STATE_BOUND);
1116 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
1117 return 0;
1118
1119 error:
1120 client_stop(client, r);
1121 return r;
1122 }
1123
1124 static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) {
1125 const char *type_str;
1126
1127 assert(client);
1128 assert(message);
1129
1130 type_str = dhcp6_message_type_to_string(message->type);
1131 if (type_str)
1132 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1133 "Received unexpected %s message, ignoring.", type_str);
1134 else
1135 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1136 "Received unsupported message type %u, ignoring.", message->type);
1137 }
1138
1139 static int client_process_information(
1140 sd_dhcp6_client *client,
1141 DHCP6Message *message,
1142 size_t len,
1143 const triple_timestamp *timestamp,
1144 const struct in6_addr *server_address) {
1145
1146 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1147 int r;
1148
1149 assert(client);
1150 assert(message);
1151
1152 if (message->type != DHCP6_MESSAGE_REPLY)
1153 return log_invalid_message_type(client, message);
1154
1155 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1156 if (r < 0)
1157 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
1158
1159 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1160
1161 sd_dhcp6_lease_unref(client->lease);
1162 client->lease = TAKE_PTR(lease);
1163
1164 /* Do not call client_stop() here, as it frees the acquired lease. */
1165 (void) event_source_disable(client->receive_message);
1166 (void) event_source_disable(client->timeout_resend);
1167 client_set_state(client, DHCP6_STATE_STOPPED);
1168
1169 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
1170 return 0;
1171 }
1172
1173 static int client_process_reply(
1174 sd_dhcp6_client *client,
1175 DHCP6Message *message,
1176 size_t len,
1177 const triple_timestamp *timestamp,
1178 const struct in6_addr *server_address) {
1179
1180 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1181 int r;
1182
1183 assert(client);
1184 assert(message);
1185
1186 if (message->type != DHCP6_MESSAGE_REPLY)
1187 return log_invalid_message_type(client, message);
1188
1189 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1190 if (r == -EADDRNOTAVAIL) {
1191
1192 /* If NoBinding status code is received, we cannot request the address anymore.
1193 * Let's restart transaction from the beginning. */
1194
1195 if (client->state == DHCP6_STATE_REQUEST)
1196 /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
1197 client_cleanup(client);
1198 else
1199 /* We need to notify the previous lease was expired. */
1200 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
1201
1202 return client_start_transaction(client, DHCP6_STATE_SOLICITATION);
1203 }
1204 if (r < 0)
1205 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
1206
1207 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1208
1209 sd_dhcp6_lease_unref(client->lease);
1210 client->lease = TAKE_PTR(lease);
1211
1212 return client_enter_bound_state(client);
1213 }
1214
1215 static int client_process_advertise_or_rapid_commit_reply(
1216 sd_dhcp6_client *client,
1217 DHCP6Message *message,
1218 size_t len,
1219 const triple_timestamp *timestamp,
1220 const struct in6_addr *server_address) {
1221
1222 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1223 uint8_t pref_advertise, pref_lease = 0;
1224 int r;
1225
1226 assert(client);
1227 assert(message);
1228
1229 if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY))
1230 return log_invalid_message_type(client, message);
1231
1232 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1233 if (r < 0)
1234 return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m",
1235 dhcp6_message_type_to_string(message->type));
1236
1237 if (message->type == DHCP6_MESSAGE_REPLY) {
1238 bool rapid_commit;
1239
1240 if (!client->rapid_commit)
1241 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1242 "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
1243
1244 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1245 if (r < 0)
1246 return r;
1247
1248 if (!rapid_commit)
1249 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1250 "Received reply message without rapid commit flag, ignoring.");
1251
1252 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1253
1254 sd_dhcp6_lease_unref(client->lease);
1255 client->lease = TAKE_PTR(lease);
1256
1257 return client_enter_bound_state(client);
1258 }
1259
1260 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1261 if (r < 0)
1262 return r;
1263
1264 if (client->lease) {
1265 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
1266 if (r < 0)
1267 return r;
1268 }
1269
1270 log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type));
1271
1272 if (!client->lease || pref_advertise > pref_lease) {
1273 /* If this is the first advertise message or has higher preference, then save the lease. */
1274 sd_dhcp6_lease_unref(client->lease);
1275 client->lease = TAKE_PTR(lease);
1276 }
1277
1278 if (pref_advertise == 255 || client->retransmit_count > 1)
1279 (void) client_start_transaction(client, DHCP6_STATE_REQUEST);
1280
1281 return 0;
1282 }
1283
1284 static int client_receive_message(
1285 sd_event_source *s,
1286 int fd, uint32_t
1287 revents,
1288 void *userdata) {
1289
1290 sd_dhcp6_client *client = ASSERT_PTR(userdata);
1291 DHCP6_CLIENT_DONT_DESTROY(client);
1292 /* This needs to be initialized with zero. See #20741. */
1293 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
1294 struct iovec iov;
1295 union sockaddr_union sa = {};
1296 struct msghdr msg = {
1297 .msg_name = &sa.sa,
1298 .msg_namelen = sizeof(sa),
1299 .msg_iov = &iov,
1300 .msg_iovlen = 1,
1301 .msg_control = &control,
1302 .msg_controllen = sizeof(control),
1303 };
1304 triple_timestamp t;
1305 _cleanup_free_ DHCP6Message *message = NULL;
1306 struct in6_addr *server_address = NULL;
1307 ssize_t buflen, len;
1308
1309 buflen = next_datagram_size_fd(fd);
1310 if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
1311 return 0;
1312 if (buflen < 0) {
1313 log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
1314 return 0;
1315 }
1316
1317 message = malloc(buflen);
1318 if (!message)
1319 return -ENOMEM;
1320
1321 iov = IOVEC_MAKE(message, buflen);
1322
1323 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
1324 if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
1325 return 0;
1326 if (len < 0) {
1327 log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
1328 return 0;
1329 }
1330 if ((size_t) len < sizeof(DHCP6Message)) {
1331 log_dhcp6_client(client, "Too small to be DHCPv6 message: ignoring");
1332 return 0;
1333 }
1334
1335 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1336 if (msg.msg_namelen > 0) {
1337 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
1338 log_dhcp6_client(client, "Received message from invalid source, ignoring.");
1339 return 0;
1340 }
1341
1342 server_address = &sa.in6.sin6_addr;
1343 }
1344
1345 triple_timestamp_from_cmsg(&t, &msg);
1346
1347 if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
1348 return 0;
1349
1350 switch (client->state) {
1351 case DHCP6_STATE_INFORMATION_REQUEST:
1352 if (client_process_information(client, message, len, &t, server_address) < 0)
1353 return 0;
1354 break;
1355
1356 case DHCP6_STATE_SOLICITATION:
1357 if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0)
1358 return 0;
1359 break;
1360
1361 case DHCP6_STATE_REQUEST:
1362 case DHCP6_STATE_RENEW:
1363 case DHCP6_STATE_REBIND:
1364 if (client_process_reply(client, message, len, &t, server_address) < 0)
1365 return 0;
1366 break;
1367
1368 case DHCP6_STATE_BOUND:
1369 case DHCP6_STATE_STOPPED:
1370 case DHCP6_STATE_STOPPING:
1371 default:
1372 assert_not_reached();
1373 }
1374
1375 return 0;
1376 }
1377
1378 static int client_send_release(sd_dhcp6_client *client) {
1379 sd_dhcp6_lease *lease;
1380
1381 assert(client);
1382
1383 if (!client->send_release)
1384 return 0;
1385
1386 if (sd_dhcp6_client_get_lease(client, &lease) < 0)
1387 return 0;
1388
1389 if (!lease->ia_na && !lease->ia_pd)
1390 return 0;
1391
1392 client_set_state(client, DHCP6_STATE_STOPPING);
1393 return dhcp6_client_send_message(client);
1394 }
1395
1396 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1397 int r;
1398
1399 if (!client)
1400 return 0;
1401
1402 /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client
1403 * engine is about to release its UDP socket unconditionally. */
1404 r = client_send_release(client);
1405 if (r < 0)
1406 log_dhcp6_client_errno(client, r,
1407 "Failed to send DHCPv6 release message, ignoring: %m");
1408
1409 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1410
1411 client->receive_message = sd_event_source_unref(client->receive_message);
1412 client->fd = safe_close(client->fd);
1413
1414 return 0;
1415 }
1416
1417 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1418 if (!client)
1419 return false;
1420
1421 return client->state != DHCP6_STATE_STOPPED;
1422 }
1423
1424 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1425 DHCP6State state = DHCP6_STATE_SOLICITATION;
1426 int r;
1427
1428 assert_return(client, -EINVAL);
1429 assert_return(client->event, -EINVAL);
1430 assert_return(client->ifindex > 0, -EINVAL);
1431 assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
1432 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1433 assert_return(client->information_request || client->request_ia != 0, -EINVAL);
1434
1435 /* Even if the client is in the STOPPED state, the lease acquired in the previous information
1436 * request may be stored. */
1437 client->lease = sd_dhcp6_lease_unref(client->lease);
1438
1439 r = client_ensure_iaid(client);
1440 if (r < 0)
1441 return r;
1442
1443 r = client_ensure_duid(client);
1444 if (r < 0)
1445 return r;
1446
1447 if (client->fd < 0) {
1448 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1449 if (r < 0)
1450 return log_dhcp6_client_errno(client, r,
1451 "Failed to bind to UDP socket at address %s: %m",
1452 IN6_ADDR_TO_STRING(&client->local_address));
1453
1454 client->fd = r;
1455 }
1456
1457 if (!client->receive_message) {
1458 _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL;
1459
1460 r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client);
1461 if (r < 0)
1462 return r;
1463
1464 r = sd_event_source_set_priority(s, client->event_priority);
1465 if (r < 0)
1466 return r;
1467
1468 r = sd_event_source_set_description(s, "dhcp6-receive-message");
1469 if (r < 0)
1470 return r;
1471
1472 client->receive_message = TAKE_PTR(s);
1473 }
1474
1475 if (client->information_request) {
1476 usec_t t = now(CLOCK_MONOTONIC);
1477
1478 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1479 return 0;
1480
1481 client->information_request_time_usec = t;
1482 state = DHCP6_STATE_INFORMATION_REQUEST;
1483 }
1484
1485 log_dhcp6_client(client, "Starting in %s mode",
1486 client->information_request ? "Information request" : "Solicit");
1487
1488 return client_start_transaction(client, state);
1489 }
1490
1491 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
1492 int r;
1493
1494 assert_return(client, -EINVAL);
1495 assert_return(!client->event, -EBUSY);
1496 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1497
1498 if (event)
1499 client->event = sd_event_ref(event);
1500 else {
1501 r = sd_event_default(&client->event);
1502 if (r < 0)
1503 return 0;
1504 }
1505
1506 client->event_priority = priority;
1507
1508 return 0;
1509 }
1510
1511 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1512 assert_return(client, -EINVAL);
1513 assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
1514
1515 client->event = sd_event_unref(client->event);
1516
1517 return 0;
1518 }
1519
1520 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1521 assert_return(client, NULL);
1522
1523 return client->event;
1524 }
1525
1526 int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev) {
1527 assert_return(client, -EINVAL);
1528
1529 return device_unref_and_replace(client->dev, dev);
1530 }
1531
1532 static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1533 if (!client)
1534 return NULL;
1535
1536 sd_dhcp6_lease_unref(client->lease);
1537
1538 sd_event_source_disable_unref(client->receive_message);
1539 sd_event_source_disable_unref(client->timeout_resend);
1540 sd_event_source_disable_unref(client->timeout_expire);
1541 sd_event_source_disable_unref(client->timeout_t1);
1542 sd_event_source_disable_unref(client->timeout_t2);
1543 sd_event_unref(client->event);
1544
1545 client->fd = safe_close(client->fd);
1546
1547 sd_device_unref(client->dev);
1548
1549 free(client->req_opts);
1550 free(client->fqdn);
1551 free(client->mudurl);
1552 dhcp6_ia_clear_addresses(&client->ia_pd);
1553 ordered_hashmap_free(client->extra_options);
1554 ordered_set_free(client->vendor_options);
1555 strv_free(client->user_class);
1556 strv_free(client->vendor_class);
1557 free(client->ifname);
1558
1559 return mfree(client);
1560 }
1561
1562 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1563
1564 int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1565 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1566
1567 assert_return(ret, -EINVAL);
1568
1569 client = new(sd_dhcp6_client, 1);
1570 if (!client)
1571 return -ENOMEM;
1572
1573 *client = (sd_dhcp6_client) {
1574 .n_ref = 1,
1575 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1576 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1577 .ifindex = -1,
1578 .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
1579 .fd = -EBADF,
1580 .rapid_commit = true,
1581 };
1582
1583 *ret = TAKE_PTR(client);
1584
1585 return 0;
1586 }