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