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