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