]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
sd-dhcp6-client: disable T1 timer on T2
[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);
220a88ca 823 (void) event_source_disable(client->timeout_t1);
a34b57c0
PF
824
825 log_dhcp6_client(client, "Timeout T2");
826
06d5e856 827 client_set_state(client, DHCP6_STATE_REBIND);
3dc34fcc 828
a34b57c0
PF
829 return 0;
830}
831
4b558378 832static int client_timeout_t1(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_t1);
a34b57c0
PF
840
841 log_dhcp6_client(client, "Timeout T1");
842
06d5e856 843 client_set_state(client, DHCP6_STATE_RENEW);
3dc34fcc 844
a34b57c0
PF
845 return 0;
846}
847
02354ee7 848static int client_enter_bound_state(sd_dhcp6_client *client) {
cdf3d8c5 849 usec_t lifetime_t1, lifetime_t2;
02354ee7
YW
850 int r;
851
852 assert(client);
853 assert(client->lease);
854 assert(IN_SET(client->state,
855 DHCP6_STATE_SOLICITATION,
856 DHCP6_STATE_REQUEST,
857 DHCP6_STATE_RENEW,
858 DHCP6_STATE_REBIND));
859
860 (void) event_source_disable(client->timeout_resend_expire);
861 (void) event_source_disable(client->timeout_resend);
862
02354ee7
YW
863 r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2);
864 if (r < 0)
865 goto error;
866
cdf3d8c5
YW
867 lifetime_t2 = client_timeout_compute_random(lifetime_t2);
868 lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
02354ee7 869
cdf3d8c5
YW
870 if (lifetime_t1 == USEC_INFINITY) {
871 log_dhcp6_client(client, "Infinite T1");
872 event_source_disable(client->timeout_t1);
873 } else {
874 log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC));
875 r = event_reset_time_relative(client->event, &client->timeout_t1,
876 clock_boottime_or_monotonic(),
877 lifetime_t1, 10 * USEC_PER_SEC,
878 client_timeout_t1, client,
879 client->event_priority, "dhcp6-t1-timeout", true);
880 if (r < 0)
881 goto error;
882 }
02354ee7 883
cdf3d8c5
YW
884 if (lifetime_t2 == USEC_INFINITY) {
885 log_dhcp6_client(client, "Infinite T2");
886 event_source_disable(client->timeout_t2);
887 } else {
888 log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC));
889 r = event_reset_time_relative(client->event, &client->timeout_t2,
890 clock_boottime_or_monotonic(),
891 lifetime_t2, 10 * USEC_PER_SEC,
892 client_timeout_t2, client,
893 client->event_priority, "dhcp6-t2-timeout", true);
894 if (r < 0)
895 goto error;
896 }
02354ee7
YW
897
898 client->state = DHCP6_STATE_BOUND;
c41bdb17 899 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
02354ee7
YW
900 return 0;
901
902error:
903 client_stop(client, r);
904 return r;
905}
906
4b558378 907static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3 908 sd_dhcp6_client *client = userdata;
3dc34fcc 909 DHCP6_CLIENT_DONT_DESTROY(client);
dd5e9378 910 DHCP6State state;
d1b0afe3
PF
911
912 assert(s);
913 assert(client);
914 assert(client->event);
915
3dc34fcc
PF
916 state = client->state;
917
10c9ce61 918 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
d1b0afe3 919
3dc34fcc
PF
920 /* RFC 3315, section 18.1.4., says that "...the client may choose to
921 use a Solicit message to locate a new DHCP server..." */
922 if (state == DHCP6_STATE_REBIND)
06d5e856 923 client_set_state(client, DHCP6_STATE_SOLICITATION);
3dc34fcc 924
d1b0afe3
PF
925 return 0;
926}
927
4b558378 928static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3
PF
929 int r = 0;
930 sd_dhcp6_client *client = userdata;
4b4923e6 931 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
5e913450 932 usec_t max_retransmit_duration = 0;
513a6fa8 933 uint8_t max_retransmit_count = 0;
d1b0afe3
PF
934
935 assert(s);
936 assert(client);
937 assert(client->event);
938
c9393e8c 939 (void) event_source_disable(client->timeout_resend);
d1b0afe3
PF
940
941 switch (client->state) {
bbfa43ca
PF
942 case DHCP6_STATE_INFORMATION_REQUEST:
943 init_retransmit_time = DHCP6_INF_TIMEOUT;
944 max_retransmit_time = DHCP6_INF_MAX_RT;
945
946 break;
947
d1b0afe3 948 case DHCP6_STATE_SOLICITATION:
7246333c 949
0936c189 950 if (client->retransmit_count > 0 && client->lease) {
06d5e856 951 client_set_state(client, DHCP6_STATE_REQUEST);
7246333c
PF
952 return 0;
953 }
954
d1b0afe3
PF
955 init_retransmit_time = DHCP6_SOL_TIMEOUT;
956 max_retransmit_time = DHCP6_SOL_MAX_RT;
d1b0afe3
PF
957
958 break;
959
7246333c
PF
960 case DHCP6_STATE_REQUEST:
961 init_retransmit_time = DHCP6_REQ_TIMEOUT;
962 max_retransmit_time = DHCP6_REQ_MAX_RT;
963 max_retransmit_count = DHCP6_REQ_MAX_RC;
7246333c
PF
964
965 break;
966
3dc34fcc
PF
967 case DHCP6_STATE_RENEW:
968 init_retransmit_time = DHCP6_REN_TIMEOUT;
969 max_retransmit_time = DHCP6_REN_MAX_RT;
3dc34fcc
PF
970
971 /* RFC 3315, section 18.1.3. says max retransmit duration will
972 be the remaining time until T2. Instead of setting MRD,
973 wait for T2 to trigger with the same end result */
3dc34fcc
PF
974
975 break;
976
977 case DHCP6_STATE_REBIND:
978 init_retransmit_time = DHCP6_REB_TIMEOUT;
979 max_retransmit_time = DHCP6_REB_MAX_RT;
3dc34fcc 980
c9393e8c 981 if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
f4fbea7a 982 r = dhcp6_lease_get_max_retransmit_duration(client->lease, &max_retransmit_duration);
3dc34fcc
PF
983 if (r < 0) {
984 client_stop(client, r);
985 return 0;
986 }
3dc34fcc
PF
987 }
988
989 break;
990
d1b0afe3 991 case DHCP6_STATE_STOPPED:
a34b57c0 992 case DHCP6_STATE_BOUND:
d1b0afe3 993 return 0;
dd5e9378
YW
994 default:
995 assert_not_reached();
d1b0afe3
PF
996 }
997
3ae01632 998 if (max_retransmit_count > 0 &&
d1b0afe3 999 client->retransmit_count >= max_retransmit_count) {
10c9ce61 1000 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
d1b0afe3
PF
1001 return 0;
1002 }
1003
fa94c34b 1004 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d1b0afe3
PF
1005 if (r < 0)
1006 goto error;
1007
346e13a2
PF
1008 r = client_send_message(client, time_now);
1009 if (r >= 0)
1010 client->retransmit_count++;
1011
3ae01632 1012 if (client->retransmit_time == 0) {
d1b0afe3
PF
1013 client->retransmit_time =
1014 client_timeout_compute_random(init_retransmit_time);
a9aff361
PF
1015
1016 if (client->state == DHCP6_STATE_SOLICITATION)
1017 client->retransmit_time += init_retransmit_time / 10;
1018
d1b0afe3 1019 } else {
7502812c
YW
1020 assert(max_retransmit_time > 0);
1021 if (client->retransmit_time > max_retransmit_time / 2)
d1b0afe3
PF
1022 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
1023 else
1024 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
1025 }
1026
1027 log_dhcp6_client(client, "Next retransmission in %s",
5291f26d 1028 FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC));
d1b0afe3 1029
c9393e8c
YW
1030 r = event_reset_time(client->event, &client->timeout_resend,
1031 clock_boottime_or_monotonic(),
1032 time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
1033 client_timeout_resend, client,
1034 client->event_priority, "dhcp6-resend-timer", true);
d1b0afe3
PF
1035 if (r < 0)
1036 goto error;
1037
3ae01632 1038 if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
d1b0afe3
PF
1039
1040 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
1041 max_retransmit_duration / USEC_PER_SEC);
1042
c9393e8c
YW
1043 r = event_reset_time(client->event, &client->timeout_resend_expire,
1044 clock_boottime_or_monotonic(),
1045 time_now + max_retransmit_duration, USEC_PER_SEC,
1046 client_timeout_resend_expire, client,
1047 client->event_priority, "dhcp6-resend-expire-timer", true);
9021bb9f
TG
1048 if (r < 0)
1049 goto error;
d1b0afe3
PF
1050 }
1051
1052error:
1053 if (r < 0)
1054 client_stop(client, r);
1055
1056 return 0;
1057}
1058
f12abb48 1059static int client_ensure_iaid(sd_dhcp6_client *client) {
cfb5b380 1060 int r;
6d13616b 1061 uint32_t iaid;
f12abb48
PF
1062
1063 assert(client);
1064
8217ed5e 1065 if (client->iaid_set)
f12abb48
PF
1066 return 0;
1067
8d71f2b3
YW
1068 r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
1069 /* legacy_unstable_byteorder = */ true,
1070 /* use_mac = */ client->test_mode,
1071 &iaid);
cfb5b380
TG
1072 if (r < 0)
1073 return r;
f12abb48 1074
4b0f2717
YW
1075 client->ia_na.header.id = iaid;
1076 client->ia_pd.header.id = iaid;
8217ed5e 1077 client->iaid_set = true;
69b43997 1078
f12abb48
PF
1079 return 0;
1080}
1081
07a3b340
YW
1082static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) {
1083 const char *type_str;
1084
1085 assert(client);
1086 assert(message);
1087
1088 type_str = dhcp6_message_type_to_string(message->type);
1089 if (type_str)
1090 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1091 "Received unexpected %s message, ignoring.", type_str);
1092 else
1093 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1094 "Received unsupported message type %u, ignoring.", message->type);
1095}
1096
1097static int client_process_information(
ef4edc15 1098 sd_dhcp6_client *client,
65ece4c8 1099 DHCP6Message *message,
ef4edc15 1100 size_t len,
65ece4c8 1101 const triple_timestamp *timestamp,
ef4edc15
YW
1102 const struct in6_addr *server_address) {
1103
4afd3348 1104 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
a1140666
LP
1105 int r;
1106
65ece4c8 1107 if (message->type != DHCP6_MESSAGE_REPLY)
07a3b340 1108 return log_invalid_message_type(client, message);
a34b57c0 1109
65ece4c8 1110 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
a34b57c0 1111 if (r < 0)
07a3b340 1112 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
a34b57c0 1113
07a3b340
YW
1114 sd_dhcp6_lease_unref(client->lease);
1115 client->lease = TAKE_PTR(lease);
ed6ee219 1116
07a3b340 1117 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
4db6334c 1118 return client_set_state(client, DHCP6_STATE_STOPPED);
07a3b340
YW
1119}
1120
1121static int client_process_reply(
1122 sd_dhcp6_client *client,
1123 DHCP6Message *message,
1124 size_t len,
1125 const triple_timestamp *timestamp,
1126 const struct in6_addr *server_address) {
1127
1128 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1129 int r;
1130
1131 assert(client);
1132 assert(message);
1133
1134 if (message->type != DHCP6_MESSAGE_REPLY)
1135 return log_invalid_message_type(client, message);
1136
1137 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
1138 if (r < 0)
1139 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
1140
431a4bc8
YW
1141 sd_dhcp6_lease_unref(client->lease);
1142 client->lease = TAKE_PTR(lease);
a34b57c0 1143
c41bdb17 1144 return client_enter_bound_state(client);
a34b57c0
PF
1145}
1146
07a3b340 1147static int client_process_advertise_or_rapid_commit_reply(
ef4edc15 1148 sd_dhcp6_client *client,
65ece4c8 1149 DHCP6Message *message,
ef4edc15 1150 size_t len,
65ece4c8 1151 const triple_timestamp *timestamp,
ef4edc15
YW
1152 const struct in6_addr *server_address) {
1153
4afd3348 1154 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
07a3b340 1155 uint8_t pref_advertise, pref_lease = 0;
a1140666 1156 int r;
631bbe71 1157
653ddc1d 1158 assert(client);
65ece4c8 1159 assert(message);
653ddc1d 1160
07a3b340
YW
1161 if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY))
1162 return log_invalid_message_type(client, message);
631bbe71 1163
65ece4c8 1164 r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
631bbe71 1165 if (r < 0)
07a3b340
YW
1166 return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m",
1167 dhcp6_message_type_to_string(message->type));
1168
1169 if (message->type == DHCP6_MESSAGE_REPLY) {
1170 bool rapid_commit;
1171
1172 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1173 if (r < 0)
1174 return r;
1175
1176 if (!rapid_commit)
1177 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1178 "Received reply message without rapid commit flag, ignoring.");
1179
1180 sd_dhcp6_lease_unref(client->lease);
1181 client->lease = TAKE_PTR(lease);
1182
c41bdb17 1183 return client_enter_bound_state(client);
07a3b340 1184 }
631bbe71
PF
1185
1186 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1187 if (r < 0)
1188 return r;
1189
049fddfa
YW
1190 if (client->lease) {
1191 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
1192 if (r < 0)
1193 return r;
1194 }
bbfa43ca 1195
049fddfa 1196 if (!client->lease || pref_advertise > pref_lease) {
07a3b340 1197 /* If this is the first advertise message or has higher preference, then save the lease. */
431a4bc8
YW
1198 sd_dhcp6_lease_unref(client->lease);
1199 client->lease = TAKE_PTR(lease);
631bbe71
PF
1200 }
1201
07a3b340 1202 if (pref_advertise == 255 || client->retransmit_count > 1) {
06d5e856 1203 r = client_set_state(client, DHCP6_STATE_REQUEST);
4db6334c 1204 if (r < 0)
07a3b340 1205 return r;
07a3b340 1206 }
7246333c 1207
07a3b340 1208 return 0;
631bbe71
PF
1209}
1210
004845d1
LP
1211static int client_receive_message(
1212 sd_event_source *s,
1213 int fd, uint32_t
1214 revents,
1215 void *userdata) {
1216
631bbe71 1217 sd_dhcp6_client *client = userdata;
3f0c075f 1218 DHCP6_CLIENT_DONT_DESTROY(client);
653ddc1d
YW
1219 /* This needs to be initialized with zero. See #20741. */
1220 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
1221 struct iovec iov;
ef4edc15 1222 union sockaddr_union sa = {};
653ddc1d 1223 struct msghdr msg = {
ef4edc15
YW
1224 .msg_name = &sa.sa,
1225 .msg_namelen = sizeof(sa),
653ddc1d
YW
1226 .msg_iov = &iov,
1227 .msg_iovlen = 1,
1228 .msg_control = &control,
1229 .msg_controllen = sizeof(control),
1230 };
1231 struct cmsghdr *cmsg;
1232 triple_timestamp t = {};
0d43d2fc 1233 _cleanup_free_ DHCP6Message *message = NULL;
ef4edc15 1234 struct in6_addr *server_address = NULL;
4edc2c9b 1235 ssize_t buflen, len;
631bbe71
PF
1236
1237 assert(s);
1238 assert(client);
1239 assert(client->event);
1240
4edc2c9b 1241 buflen = next_datagram_size_fd(fd);
ab8a8a4e
YW
1242 if (buflen < 0) {
1243 if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
1244 return 0;
1245
1246 log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
22a3fd2d 1247 return 0;
ab8a8a4e 1248 }
631bbe71 1249
0d43d2fc 1250 message = malloc(buflen);
631bbe71
PF
1251 if (!message)
1252 return -ENOMEM;
1253
653ddc1d
YW
1254 iov = IOVEC_MAKE(message, buflen);
1255
1256 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
0d43d2fc 1257 if (len < 0) {
ab8a8a4e 1258 if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
0d43d2fc
TG
1259 return 0;
1260
ab8a8a4e
YW
1261 log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
1262 return 0;
004845d1
LP
1263 }
1264 if ((size_t) len < sizeof(DHCP6Message)) {
1265 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
631bbe71 1266 return 0;
004845d1 1267 }
631bbe71 1268
ef4edc15
YW
1269 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1270 if (msg.msg_namelen > 0) {
1271 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
1272 log_dhcp6_client(client, "Received message from invalid source, ignoring.");
1273 return 0;
1274 }
1275
1276 server_address = &sa.in6.sin6_addr;
1277 }
1278
653ddc1d
YW
1279 CMSG_FOREACH(cmsg, &msg) {
1280 if (cmsg->cmsg_level == SOL_SOCKET &&
1281 cmsg->cmsg_type == SO_TIMESTAMP &&
1282 cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
1283 triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
1284 }
1285
d7799877 1286 if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
631bbe71
PF
1287 return 0;
1288
1289 switch (client->state) {
bbfa43ca 1290 case DHCP6_STATE_INFORMATION_REQUEST:
07a3b340 1291 if (client_process_information(client, message, len, &t, server_address) < 0)
bbfa43ca 1292 return 0;
bbfa43ca
PF
1293 break;
1294
631bbe71 1295 case DHCP6_STATE_SOLICITATION:
07a3b340 1296 if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0)
d7799877 1297 return 0;
07a3b340 1298 break;
631bbe71 1299
7246333c 1300 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1301 case DHCP6_STATE_RENEW:
1302 case DHCP6_STATE_REBIND:
07a3b340 1303 if (client_process_reply(client, message, len, &t, server_address) < 0)
a34b57c0 1304 return 0;
a34b57c0
PF
1305 break;
1306
1307 case DHCP6_STATE_BOUND:
631bbe71 1308 case DHCP6_STATE_STOPPED:
631bbe71 1309 return 0;
07a3b340 1310
dd5e9378
YW
1311 default:
1312 assert_not_reached();
631bbe71
PF
1313 }
1314
712fdfd6
YW
1315 log_dhcp6_client(client, "Recv %s",
1316 dhcp6_message_type_to_string(message->type));
631bbe71 1317
a9aff361
PF
1318 return 0;
1319}
1320
06d5e856 1321static int client_set_state(sd_dhcp6_client *client, DHCP6State state) {
02354ee7 1322 usec_t time_now;
f12abb48
PF
1323 int r;
1324
1325 assert_return(client, -EINVAL);
1326 assert_return(client->event, -EINVAL);
2f8e7633 1327 assert_return(client->ifindex > 0, -EINVAL);
c3e2adea 1328 assert_return(client->state != state, -EINVAL);
f12abb48 1329
c9393e8c
YW
1330 (void) event_source_disable(client->timeout_resend_expire);
1331 (void) event_source_disable(client->timeout_resend);
c3e2adea
PF
1332 client->retransmit_time = 0;
1333 client->retransmit_count = 0;
f12abb48 1334
38a03f06
LP
1335 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1336 if (r < 0)
bfa1139a 1337 goto error;
346e13a2 1338
7ac6c26a
PF
1339 if (!client->receive_message) {
1340 r = sd_event_add_io(client->event, &client->receive_message,
1341 client->fd, EPOLLIN, client_receive_message,
1342 client);
1343 if (r < 0)
1344 goto error;
1345
1346 r = sd_event_source_set_priority(client->receive_message,
1347 client->event_priority);
1348 if (r < 0)
1349 goto error;
1350
1351 r = sd_event_source_set_description(client->receive_message,
1352 "dhcp6-receive-message");
1353 if (r < 0)
1354 goto error;
1355 }
1356
c3e2adea
PF
1357 switch (state) {
1358 case DHCP6_STATE_STOPPED:
bbfa43ca
PF
1359 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1360 client->state = DHCP6_STATE_STOPPED;
a9aff361 1361
bbfa43ca
PF
1362 return 0;
1363 }
9021bb9f 1364
4831981d 1365 _fallthrough_;
bbfa43ca 1366 case DHCP6_STATE_SOLICITATION:
c3e2adea
PF
1367 client->state = DHCP6_STATE_SOLICITATION;
1368
7246333c
PF
1369 break;
1370
bbfa43ca 1371 case DHCP6_STATE_INFORMATION_REQUEST:
7246333c 1372 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1373 case DHCP6_STATE_RENEW:
1374 case DHCP6_STATE_REBIND:
a34b57c0 1375
7246333c
PF
1376 client->state = state;
1377
c3e2adea 1378 break;
a34b57c0
PF
1379
1380 case DHCP6_STATE_BOUND:
dd5e9378
YW
1381 default:
1382 assert_not_reached();
c3e2adea 1383 }
a9aff361 1384
c3e2adea 1385 client->transaction_id = random_u32() & htobe32(0x00ffffff);
346e13a2 1386 client->transaction_start = time_now;
d1b0afe3 1387
c9393e8c
YW
1388 r = event_reset_time(client->event, &client->timeout_resend,
1389 clock_boottime_or_monotonic(),
1390 0, 0,
1391 client_timeout_resend, client,
1392 client->event_priority, "dhcp6-resend-timeout", true);
9021bb9f 1393 if (r < 0)
7ac6c26a 1394 goto error;
9021bb9f 1395
f12abb48 1396 return 0;
7ac6c26a 1397
4db6334c
YW
1398error:
1399 client_stop(client, r);
7ac6c26a 1400 return r;
f12abb48
PF
1401}
1402
0ae0e5cd 1403int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
c8bae363
YW
1404 if (!client)
1405 return 0;
f667c150 1406
10c9ce61 1407 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
139b011a 1408
7ac6c26a
PF
1409 client->fd = safe_close(client->fd);
1410
139b011a
PF
1411 return 0;
1412}
1413
f667c150
TG
1414int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1415 assert_return(client, -EINVAL);
1416
1417 return client->state != DHCP6_STATE_STOPPED;
1418}
1419
0ae0e5cd 1420int sd_dhcp6_client_start(sd_dhcp6_client *client) {
dd5e9378 1421 DHCP6State state = DHCP6_STATE_SOLICITATION;
6d95e7d9 1422 int r;
139b011a
PF
1423
1424 assert_return(client, -EINVAL);
1425 assert_return(client->event, -EINVAL);
2f8e7633 1426 assert_return(client->ifindex > 0, -EINVAL);
94876904 1427 assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
139b011a 1428
080e5c2f 1429 if (client->state != DHCP6_STATE_STOPPED)
63348d13 1430 return -EBUSY;
d7c9c21f 1431
b261b5f4 1432 if (!client->information_request && client->request_ia == 0)
f311a62b
PF
1433 return -EINVAL;
1434
16347c15 1435 client_reset(client);
f12abb48 1436
bbfa43ca
PF
1437 r = client_ensure_iaid(client);
1438 if (r < 0)
1439 return r;
1440
cc22955c
TH
1441 r = client_ensure_duid(client);
1442 if (r < 0)
1443 return r;
1444
10a0f27b
PF
1445 if (client->fd < 0) {
1446 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1447 if (r < 0) {
1448 _cleanup_free_ char *p = NULL;
1449
77712331 1450 (void) in6_addr_to_string(&client->local_address, &p);
10a0f27b
PF
1451 return log_dhcp6_client_errno(client, r,
1452 "Failed to bind to UDP socket at address %s: %m", strna(p));
1453 }
bd9a7221 1454
10a0f27b 1455 client->fd = r;
bd9a7221 1456 }
bbfa43ca 1457
fcb51238
YW
1458 if (client->information_request) {
1459 usec_t t = now(CLOCK_MONOTONIC);
1460
1461 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1462 return 0;
1463
1464 client->information_request_time_usec = t;
bbfa43ca 1465 state = DHCP6_STATE_INFORMATION_REQUEST;
fcb51238 1466 }
bbfa43ca
PF
1467
1468 log_dhcp6_client(client, "Started in %s mode",
787dd704 1469 client->information_request ? "Information request" : "Managed");
bbfa43ca 1470
06d5e856 1471 return client_set_state(client, state);
139b011a
PF
1472}
1473
32d20645 1474int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
139b011a
PF
1475 int r;
1476
1477 assert_return(client, -EINVAL);
1478 assert_return(!client->event, -EBUSY);
1479
1480 if (event)
1481 client->event = sd_event_ref(event);
1482 else {
1483 r = sd_event_default(&client->event);
1484 if (r < 0)
1485 return 0;
1486 }
1487
1488 client->event_priority = priority;
1489
1490 return 0;
1491}
1492
1493int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1494 assert_return(client, -EINVAL);
1495
1496 client->event = sd_event_unref(client->event);
1497
1498 return 0;
1499}
1500
1501sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
a1140666 1502 assert_return(client, NULL);
139b011a
PF
1503
1504 return client->event;
1505}
1506
8301aa0b
YW
1507static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1508 assert(client);
139b011a 1509
d0875a07 1510 sd_dhcp6_lease_unref(client->lease);
c9393e8c 1511
d0875a07
YW
1512 sd_event_source_disable_unref(client->receive_message);
1513 sd_event_source_disable_unref(client->timeout_resend);
1514 sd_event_source_disable_unref(client->timeout_resend_expire);
1515 sd_event_source_disable_unref(client->timeout_t1);
1516 sd_event_source_disable_unref(client->timeout_t2);
3733eec3 1517
10a0f27b
PF
1518 client->fd = safe_close(client->fd);
1519
3733eec3 1520 sd_dhcp6_client_detach_event(client);
3733eec3
LP
1521
1522 free(client->req_opts);
8006aa32 1523 free(client->fqdn);
de8d6e55 1524 free(client->mudurl);
877bfc78 1525 dhcp6_ia_clear_addresses(&client->ia_pd);
e7d5fe17 1526 ordered_hashmap_free(client->extra_options);
33923925 1527 strv_free(client->user_class);
73c8ced7 1528 strv_free(client->vendor_class);
61a9fa8f 1529 free(client->ifname);
33923925 1530
6b430fdb 1531 return mfree(client);
139b011a
PF
1532}
1533
8301aa0b
YW
1534DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1535
0ae0e5cd 1536int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
4afd3348 1537 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
8b8ecac8 1538 _cleanup_free_ be16_t *req_opts = NULL;
da6fe470 1539 size_t t;
139b011a
PF
1540
1541 assert_return(ret, -EINVAL);
1542
8b8ecac8
YW
1543 req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
1544 if (!req_opts)
139b011a
PF
1545 return -ENOMEM;
1546
8b8ecac8
YW
1547 for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
1548 req_opts[t] = htobe16(default_req_opts[t]);
c806ffb9 1549
8b8ecac8
YW
1550 client = new(sd_dhcp6_client, 1);
1551 if (!client)
da6fe470
PF
1552 return -ENOMEM;
1553
8b8ecac8
YW
1554 *client = (sd_dhcp6_client) {
1555 .n_ref = 1,
1556 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1557 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1558 .ifindex = -1,
01b4e90f 1559 .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
8b8ecac8
YW
1560 .fd = -1,
1561 .req_opts_len = ELEMENTSOF(default_req_opts),
1562 .req_opts = TAKE_PTR(req_opts),
1563 };
da6fe470 1564
1cc6c93a 1565 *ret = TAKE_PTR(client);
139b011a
PF
1566
1567 return 0;
1568}