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