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