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