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