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