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