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