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