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