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