]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
sd-dhcp6-client: do not implicitly cast to boolean
[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
76253e73
DW
33#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
34
6ffe71d0
YW
35#define IRT_DEFAULT (1 * USEC_PER_DAY)
36#define IRT_MINIMUM (600 * USEC_PER_SEC)
fcb51238 37
f311a62b 38/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
b261b5f4
YW
39typedef enum DHCP6RequestIA {
40 DHCP6_REQUEST_IA_NA = 1 << 0,
41 DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */
42 DHCP6_REQUEST_IA_PD = 1 << 2,
43} DHCP6RequestIA;
f311a62b 44
139b011a 45struct sd_dhcp6_client {
3733eec3 46 unsigned n_ref;
139b011a 47
dd5e9378 48 DHCP6State state;
139b011a
PF
49 sd_event *event;
50 int event_priority;
2f8e7633 51 int ifindex;
61a9fa8f 52 char *ifname;
2805536b 53 DHCP6Address hint_pd_prefix;
c601ebf7 54 struct in6_addr local_address;
76253e73
DW
55 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
56 size_t mac_addr_len;
57 uint16_t arp_type;
f12abb48 58 DHCP6IA ia_na;
dce6563f 59 DHCP6IA ia_pd;
9a7225de
PF
60 sd_event_source *timeout_t1;
61 sd_event_source *timeout_t2;
b261b5f4 62 DHCP6RequestIA request_ia;
a9aff361 63 be32_t transaction_id;
346e13a2 64 usec_t transaction_start;
631bbe71 65 struct sd_dhcp6_lease *lease;
a9aff361 66 int fd;
bbfa43ca 67 bool information_request;
8217ed5e 68 bool iaid_set;
da6fe470 69 be16_t *req_opts;
da6fe470 70 size_t req_opts_len;
8006aa32 71 char *fqdn;
de8d6e55 72 char *mudurl;
33923925 73 char **user_class;
73c8ced7 74 char **vendor_class;
a9aff361 75 sd_event_source *receive_message;
d1b0afe3
PF
76 usec_t retransmit_time;
77 uint8_t retransmit_count;
78 sd_event_source *timeout_resend;
79 sd_event_source *timeout_resend_expire;
45aa74c7 80 sd_dhcp6_client_callback_t callback;
139b011a 81 void *userdata;
764aad62 82 struct duid duid;
66eac120 83 size_t duid_len;
fcb51238
YW
84 usec_t information_request_time_usec;
85 usec_t information_refresh_time_usec;
e7d5fe17 86 OrderedHashmap *extra_options;
99ccb8ff 87 OrderedHashmap *vendor_options;
8d71f2b3
YW
88
89 /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
90 bool test_mode;
139b011a
PF
91};
92
da6fe470 93static const uint16_t default_req_opts[] = {
2c1ab8ca
BG
94 SD_DHCP6_OPTION_DNS_SERVERS,
95 SD_DHCP6_OPTION_DOMAIN_LIST,
96 SD_DHCP6_OPTION_NTP_SERVER,
97 SD_DHCP6_OPTION_SNTP_SERVERS,
da6fe470
PF
98};
99
59f1ded8
YW
100const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = {
101 [DHCP6_MESSAGE_SOLICIT] = "Solicit",
102 [DHCP6_MESSAGE_ADVERTISE] = "Advertise",
103 [DHCP6_MESSAGE_REQUEST] = "Request",
104 [DHCP6_MESSAGE_CONFIRM] = "Confirm",
105 [DHCP6_MESSAGE_RENEW] = "Renew",
106 [DHCP6_MESSAGE_REBIND] = "Rebind",
107 [DHCP6_MESSAGE_REPLY] = "Reply",
108 [DHCP6_MESSAGE_RELEASE] = "Release",
109 [DHCP6_MESSAGE_DECLINE] = "Decline",
110 [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure",
111 [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request",
112 [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward",
113 [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply",
114 [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query",
115 [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply",
116 [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done",
117 [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data",
118 [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request",
119 [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply",
120 [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query",
121 [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response",
122 [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query",
123 [DHCP6_MESSAGE_START_TLS] = "Start TLS",
124 [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update",
125 [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply",
126 [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request",
127 [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response",
128 [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request",
129 [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All",
130 [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done",
131 [DHCP6_MESSAGE_CONNECT] = "Connect",
132 [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply",
133 [DHCP6_MESSAGE_DISCONNECT] = "Disconnect",
134 [DHCP6_MESSAGE_STATE] = "State",
135 [DHCP6_MESSAGE_CONTACT] = "Contact",
a9aff361
PF
136};
137
138DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
139
631bbe71 140const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
76643fed
SS
141 [DHCP6_STATUS_SUCCESS] = "Success",
142 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
143 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
144 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
145 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
146 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
147 [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available",
148 [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type",
149 [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query",
150 [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured",
151 [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed",
152 [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated",
153 [DHCP6_STATUS_DATA_MISSING] = "Data missing",
154 [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete",
155 [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported",
156 [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused",
157 [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use",
158 [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict",
159 [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information",
160 [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
161 [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down",
162 [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported",
163 [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew",
631bbe71
PF
164};
165
166DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
167
3f0c075f 168#define DHCP6_CLIENT_DONT_DESTROY(client) \
4afd3348 169 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
3f0c075f 170
dd5e9378 171static int client_start(sd_dhcp6_client *client, DHCP6State state);
c3e2adea 172
4b558378
ZJS
173int sd_dhcp6_client_set_callback(
174 sd_dhcp6_client *client,
175 sd_dhcp6_client_callback_t cb,
176 void *userdata) {
45aa74c7 177
139b011a
PF
178 assert_return(client, -EINVAL);
179
45aa74c7 180 client->callback = cb;
139b011a
PF
181 client->userdata = userdata;
182
183 return 0;
184}
185
2f8e7633 186int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
2f8e7633 187 assert_return(client, -EINVAL);
7fa69c0a 188 assert_return(ifindex > 0, -EINVAL);
080e5c2f 189 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
d7c9c21f 190
2f8e7633 191 client->ifindex = ifindex;
139b011a
PF
192 return 0;
193}
194
61a9fa8f
YW
195int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) {
196 assert_return(client, -EINVAL);
197 assert_return(ifname, -EINVAL);
198
199 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
200 return -EINVAL;
201
202 return free_and_strdup(&client->ifname, ifname);
203}
204
5977b71f
YW
205int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret) {
206 int r;
61a9fa8f 207
5977b71f
YW
208 assert_return(client, -EINVAL);
209
210 r = get_ifname(client->ifindex, &client->ifname);
211 if (r < 0)
212 return r;
213
214 if (ret)
215 *ret = client->ifname;
216
217 return 0;
61a9fa8f
YW
218}
219
4b558378
ZJS
220int sd_dhcp6_client_set_local_address(
221 sd_dhcp6_client *client,
222 const struct in6_addr *local_address) {
223
c601ebf7
TG
224 assert_return(client, -EINVAL);
225 assert_return(local_address, -EINVAL);
94876904 226 assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
080e5c2f 227 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
c601ebf7
TG
228
229 client->local_address = *local_address;
230
231 return 0;
232}
233
0ae0e5cd
LP
234int sd_dhcp6_client_set_mac(
235 sd_dhcp6_client *client,
236 const uint8_t *addr, size_t addr_len,
237 uint16_t arp_type) {
238
139b011a 239 assert_return(client, -EINVAL);
76253e73 240 assert_return(addr, -EINVAL);
1d370b2c 241 assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
080e5c2f 242 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
d7c9c21f 243
76253e73
DW
244 if (arp_type == ARPHRD_ETHER)
245 assert_return(addr_len == ETH_ALEN, -EINVAL);
246 else if (arp_type == ARPHRD_INFINIBAND)
247 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
1d370b2c
JT
248 else {
249 client->arp_type = ARPHRD_NONE;
250 client->mac_addr_len = 0;
251 return 0;
252 }
76253e73 253
76253e73
DW
254 memcpy(&client->mac_addr, addr, addr_len);
255 client->mac_addr_len = addr_len;
256 client->arp_type = arp_type;
139b011a
PF
257
258 return 0;
259}
260
2805536b
SS
261int sd_dhcp6_client_set_prefix_delegation_hint(
262 sd_dhcp6_client *client,
263 uint8_t prefixlen,
264 const struct in6_addr *pd_address) {
265
266 assert_return(client, -EINVAL);
267 assert_return(pd_address, -EINVAL);
080e5c2f 268 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
2805536b
SS
269
270 client->hint_pd_prefix.iapdprefix.address = *pd_address;
271 client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
272
273 return 0;
274}
275
99ccb8ff
SS
276int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
277 int r;
278
279 assert_return(client, -EINVAL);
280 assert_return(v, -EINVAL);
281
02288f3e 282 r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v);
99ccb8ff
SS
283 if (r < 0)
284 return r;
285
286 sd_dhcp6_option_ref(v);
287
288 return 1;
289}
290
0ae0e5cd 291static int client_ensure_duid(sd_dhcp6_client *client) {
cc22955c
TH
292 if (client->duid_len != 0)
293 return 0;
0ae0e5cd 294
cc22955c
TH
295 return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
296}
297
d7df2fd3
ZJS
298/**
299 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
300 * without further modification. Otherwise, if duid_type is supported, DUID
301 * is set based on that type. Otherwise, an error is returned.
302 */
7e90a499 303static int dhcp6_client_set_duid_internal(
4b558378
ZJS
304 sd_dhcp6_client *client,
305 uint16_t duid_type,
f7a92d1a 306 const void *duid,
7e90a499
YW
307 size_t duid_len,
308 usec_t llt_time) {
413708d1 309 int r;
27eba50e 310
66eac120 311 assert_return(client, -EINVAL);
d7df2fd3 312 assert_return(duid_len == 0 || duid != NULL, -EINVAL);
080e5c2f 313 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
c83321e6 314
1d6cc5d0 315 if (duid) {
ab4a88bc
TH
316 r = dhcp_validate_duid_len(duid_type, duid_len, true);
317 if (r < 0) {
318 r = dhcp_validate_duid_len(duid_type, duid_len, false);
319 if (r < 0)
a339859f 320 return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
01bcea49
LP
321
322 log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
ab4a88bc 323 }
d7df2fd3 324
413708d1
VK
325 client->duid.type = htobe16(duid_type);
326 memcpy(&client->duid.raw.data, duid, duid_len);
d7df2fd3 327 client->duid_len = sizeof(client->duid.type) + duid_len;
d7df2fd3 328 } else
27eba50e 329 switch (duid_type) {
335f80a6 330 case DUID_TYPE_LLT:
339697f0 331 if (client->mac_addr_len == 0)
a339859f 332 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
335f80a6 333
53253d9c 334 r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
335f80a6 335 if (r < 0)
a339859f 336 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
335f80a6 337 break;
27eba50e
YW
338 case DUID_TYPE_EN:
339 r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
340 if (r < 0)
a339859f 341 return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
27eba50e 342 break;
335f80a6 343 case DUID_TYPE_LL:
339697f0 344 if (client->mac_addr_len == 0)
a339859f 345 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
335f80a6
YW
346
347 r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
348 if (r < 0)
a339859f 349 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
335f80a6 350 break;
27eba50e
YW
351 case DUID_TYPE_UUID:
352 r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
353 if (r < 0)
a339859f 354 return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
27eba50e
YW
355 break;
356 default:
a339859f 357 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
27eba50e 358 }
fe4b2156 359
413708d1
VK
360 return 0;
361}
362
7e90a499
YW
363int sd_dhcp6_client_set_duid(
364 sd_dhcp6_client *client,
365 uint16_t duid_type,
366 const void *duid,
367 size_t duid_len) {
368 return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
369}
370
371int sd_dhcp6_client_set_duid_llt(
372 sd_dhcp6_client *client,
373 usec_t llt_time) {
374 return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
375}
376
6b7d5b6e
SS
377static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = {
378 [DUID_TYPE_LLT] = "DUID-LLT",
379 [DUID_TYPE_EN] = "DUID-EN/Vendor",
380 [DUID_TYPE_LL] = "DUID-LL",
381 [DUID_TYPE_UUID] = "UUID",
382};
383DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType);
384
385int sd_dhcp6_client_duid_as_string(
386 sd_dhcp6_client *client,
387 char **duid) {
388 _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
389 const char *v;
390 int r;
391
392 assert_return(client, -EINVAL);
393 assert_return(client->duid_len > 0, -ENODATA);
361eb412 394 assert_return(duid, -EINVAL);
6b7d5b6e
SS
395
396 v = dhcp6_duid_type_to_string(be16toh(client->duid.type));
397 if (v) {
398 s = strdup(v);
399 if (!s)
400 return -ENOMEM;
401 } else {
402 r = asprintf(&s, "%0x", client->duid.type);
403 if (r < 0)
404 return -ENOMEM;
405 }
406
407 t = hexmem(&client->duid.raw.data, client->duid_len);
408 if (!t)
409 return -ENOMEM;
410
411 p = strjoin(s, ":", t);
412 if (!p)
413 return -ENOMEM;
414
415 *duid = TAKE_PTR(p);
416
417 return 0;
418}
419
413708d1
VK
420int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
421 assert_return(client, -EINVAL);
080e5c2f 422 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
413708d1 423
e0026dcb 424 client->ia_na.ia_na.id = htobe32(iaid);
dce6563f 425 client->ia_pd.ia_pd.id = htobe32(iaid);
8217ed5e 426 client->iaid_set = true;
66eac120
DW
427
428 return 0;
429}
430
8d71f2b3
YW
431void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) {
432 assert(client);
433
434 client->test_mode = test_mode;
435}
436
d69d4038
SS
437int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
438 assert_return(client, -EINVAL);
439 assert_return(iaid, -EINVAL);
440
441 if (!client->iaid_set)
442 return -ENODATA;
443
444 *iaid = be32toh(client->ia_na.ia_na.id);
445
446 return 0;
447}
448
8006aa32
SA
449int sd_dhcp6_client_set_fqdn(
450 sd_dhcp6_client *client,
451 const char *fqdn) {
452
453 assert_return(client, -EINVAL);
454
455 /* Make sure FQDN qualifies as DNS and as Linux hostname */
456 if (fqdn &&
52ef5dd7 457 !(hostname_is_valid(fqdn, 0) && dns_name_is_valid(fqdn) > 0))
8006aa32
SA
458 return -EINVAL;
459
460 return free_and_strdup(&client->fqdn, fqdn);
461}
462
04c01369 463int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
bbfa43ca 464 assert_return(client, -EINVAL);
080e5c2f 465 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
d7c9c21f 466
bbfa43ca
PF
467 client->information_request = enabled;
468
469 return 0;
470}
471
04c01369 472int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
bbfa43ca
PF
473 assert_return(client, -EINVAL);
474 assert_return(enabled, -EINVAL);
475
476 *enabled = client->information_request;
477
478 return 0;
479}
480
0ae0e5cd 481int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
da6fe470
PF
482 size_t t;
483
484 assert_return(client, -EINVAL);
485 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
486
fea8c180 487 if (!dhcp6_option_can_request(option))
da6fe470 488 return -EINVAL;
da6fe470
PF
489
490 for (t = 0; t < client->req_opts_len; t++)
491 if (client->req_opts[t] == htobe16(option))
492 return -EEXIST;
493
319a4f4b 494 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_len + 1))
da6fe470
PF
495 return -ENOMEM;
496
497 client->req_opts[client->req_opts_len++] = htobe16(option);
498
499 return 0;
500}
501
feb7d7a2 502int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
de8d6e55
SS
503 assert_return(client, -EINVAL);
504 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
505 assert_return(mudurl, -EINVAL);
2d3adfa6 506 assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
de8d6e55
SS
507 assert_return(http_url_is_valid(mudurl), -EINVAL);
508
509 return free_and_strdup(&client->mudurl, mudurl);
510}
511
5a99444e
YW
512int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) {
513 char * const *p;
514 char **s;
33923925
SS
515
516 assert_return(client, -EINVAL);
517 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
5a99444e 518 assert_return(!strv_isempty(user_class), -EINVAL);
73c8ced7 519
5a99444e
YW
520 STRV_FOREACH(p, user_class) {
521 size_t len = strlen(*p);
33923925 522
5a99444e
YW
523 if (len > UINT16_MAX || len == 0)
524 return -EINVAL;
525 }
33923925 526
2d3adfa6 527 s = strv_copy(user_class);
33923925
SS
528 if (!s)
529 return -ENOMEM;
530
5a99444e 531 return strv_free_and_replace(client->user_class, s);
33923925
SS
532}
533
019951ec
YW
534int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) {
535 char * const *p;
536 char **s;
73c8ced7
SS
537
538 assert_return(client, -EINVAL);
539 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
019951ec 540 assert_return(!strv_isempty(vendor_class), -EINVAL);
73c8ced7 541
019951ec
YW
542 STRV_FOREACH(p, vendor_class) {
543 size_t len = strlen(*p);
544
545 if (len > UINT16_MAX || len == 0)
546 return -EINVAL;
547 }
73c8ced7
SS
548
549 s = strv_copy(vendor_class);
550 if (!s)
551 return -ENOMEM;
552
019951ec 553 return strv_free_and_replace(client->vendor_class, s);
73c8ced7
SS
554}
555
d8c51121
PF
556int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
557 assert_return(client, -EINVAL);
558 assert_return(delegation, -EINVAL);
559
b261b5f4 560 *delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
d8c51121
PF
561
562 return 0;
563}
564
565int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
7c3de8f8
PF
566 assert_return(client, -EINVAL);
567
b261b5f4 568 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
f311a62b
PF
569
570 return 0;
571}
572
573int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
574 assert_return(client, -EINVAL);
575 assert_return(request, -EINVAL);
576
b261b5f4 577 *request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
f311a62b
PF
578
579 return 0;
580}
581
582int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
583 assert_return(client, -EINVAL);
584
b261b5f4 585 SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
7c3de8f8
PF
586
587 return 0;
588}
589
b62f9008 590int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
d89a400e
EV
591 assert_return(client, -EINVAL);
592
593 client->transaction_id = transaction_id;
594
595 return 0;
596}
597
ea3b3a75
PF
598int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
599 assert_return(client, -EINVAL);
ea3b3a75
PF
600
601 if (!client->lease)
602 return -ENOMSG;
603
3098562c
TG
604 if (ret)
605 *ret = client->lease;
ea3b3a75
PF
606
607 return 0;
608}
609
e7d5fe17
AD
610int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
611 int r;
612
613 assert_return(client, -EINVAL);
614 assert_return(v, -EINVAL);
615
02288f3e 616 r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp6_option_hash_ops, UINT_TO_PTR(v->option), v);
e7d5fe17
AD
617 if (r < 0)
618 return r;
619
620 sd_dhcp6_option_ref(v);
621 return 0;
622}
623
3f0c075f 624static void client_notify(sd_dhcp6_client *client, int event) {
45aa74c7
LP
625 assert(client);
626
627 if (client->callback)
628 client->callback(client, event, client->userdata);
139b011a
PF
629}
630
f8908727 631static int client_reset(sd_dhcp6_client *client) {
a1140666 632 assert(client);
f8908727 633
431a4bc8 634 client->lease = sd_dhcp6_lease_unref(client->lease);
4e3e6679 635
a9aff361
PF
636 client->receive_message =
637 sd_event_source_unref(client->receive_message);
638
c3e2adea 639 client->transaction_id = 0;
346e13a2 640 client->transaction_start = 0;
a9aff361 641
d1b0afe3
PF
642 client->retransmit_time = 0;
643 client->retransmit_count = 0;
d1b0afe3 644
c9393e8c
YW
645 (void) event_source_disable(client->timeout_resend);
646 (void) event_source_disable(client->timeout_resend_expire);
647 (void) event_source_disable(client->timeout_t1);
648 (void) event_source_disable(client->timeout_t2);
213e759a 649
139b011a
PF
650 client->state = DHCP6_STATE_STOPPED;
651
652 return 0;
653}
654
3f0c075f
PF
655static void client_stop(sd_dhcp6_client *client, int error) {
656 DHCP6_CLIENT_DONT_DESTROY(client);
139b011a 657
3f0c075f 658 assert(client);
139b011a 659
3f0c075f
PF
660 client_notify(client, error);
661
662 client_reset(client);
139b011a
PF
663}
664
346e13a2 665static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
a9aff361
PF
666 _cleanup_free_ DHCP6Message *message = NULL;
667 struct in6_addr all_servers =
668 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
e7d5fe17 669 struct sd_dhcp6_option *j;
a9aff361
PF
670 size_t len, optlen = 512;
671 uint8_t *opt;
672 int r;
346e13a2
PF
673 usec_t elapsed_usec;
674 be16_t elapsed_time;
a9aff361 675
a1140666
LP
676 assert(client);
677
a9aff361
PF
678 len = sizeof(DHCP6Message) + optlen;
679
680 message = malloc0(len);
681 if (!message)
682 return -ENOMEM;
683
684 opt = (uint8_t *)(message + 1);
685
686 message->transaction_id = client->transaction_id;
687
688 switch(client->state) {
bbfa43ca 689 case DHCP6_STATE_INFORMATION_REQUEST:
59f1ded8 690 message->type = DHCP6_MESSAGE_INFORMATION_REQUEST;
bbfa43ca 691
de8d6e55
SS
692 if (client->mudurl) {
693 r = dhcp6_option_append(&opt, &optlen,
f5e3619b 694 SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
de8d6e55
SS
695 client->mudurl);
696 if (r < 0)
697 return r;
698 }
699
bbfa43ca
PF
700 break;
701
a9aff361 702 case DHCP6_STATE_SOLICITATION:
59f1ded8 703 message->type = DHCP6_MESSAGE_SOLICIT;
a9aff361 704
ed6ee219 705 r = dhcp6_option_append(&opt, &optlen,
2c1ab8ca 706 SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
926695f1
TA
707 if (r < 0)
708 return r;
ed6ee219 709
b261b5f4 710 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
f311a62b
PF
711 r = dhcp6_option_append_ia(&opt, &optlen,
712 &client->ia_na);
713 if (r < 0)
714 return r;
715 }
a9aff361 716
8006aa32
SA
717 if (client->fqdn) {
718 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
719 if (r < 0)
720 return r;
721 }
722
de8d6e55
SS
723 if (client->mudurl) {
724 r = dhcp6_option_append(&opt, &optlen,
f5e3619b 725 SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
de8d6e55
SS
726 client->mudurl);
727 if (r < 0)
728 return r;
729 }
730
33923925
SS
731 if (client->user_class) {
732 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
733 if (r < 0)
734 return r;
735 }
736
73c8ced7
SS
737 if (client->vendor_class) {
738 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
739 if (r < 0)
740 return r;
741 }
742
99ccb8ff
SS
743 if (!ordered_hashmap_isempty(client->vendor_options)) {
744 r = dhcp6_option_append_vendor_option(&opt, &optlen,
745 client->vendor_options);
746 if (r < 0)
747 return r;
748 }
749
b261b5f4 750 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
73b49d43 751 r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix);
7c3de8f8
PF
752 if (r < 0)
753 return r;
7c3de8f8
PF
754 }
755
7246333c
PF
756 break;
757
758 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
759 case DHCP6_STATE_RENEW:
760
761 if (client->state == DHCP6_STATE_REQUEST)
59f1ded8 762 message->type = DHCP6_MESSAGE_REQUEST;
3dc34fcc 763 else
59f1ded8 764 message->type = DHCP6_MESSAGE_RENEW;
7246333c 765
2c1ab8ca 766 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
7246333c
PF
767 client->lease->serverid_len,
768 client->lease->serverid);
769 if (r < 0)
770 return r;
771
b261b5f4 772 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
f311a62b
PF
773 r = dhcp6_option_append_ia(&opt, &optlen,
774 &client->lease->ia);
775 if (r < 0)
776 return r;
777 }
a9aff361 778
8006aa32
SA
779 if (client->fqdn) {
780 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
781 if (r < 0)
782 return r;
783 }
784
de8d6e55
SS
785 if (client->mudurl) {
786 r = dhcp6_option_append(&opt, &optlen,
f5e3619b 787 SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
de8d6e55
SS
788 client->mudurl);
789 if (r < 0)
790 return r;
791 }
792
33923925
SS
793 if (client->user_class) {
794 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
795 if (r < 0)
796 return r;
797 }
798
73c8ced7
SS
799 if (client->vendor_class) {
800 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
801 if (r < 0)
802 return r;
803 }
804
99ccb8ff
SS
805 if (!ordered_hashmap_isempty(client->vendor_options)) {
806 r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
807 if (r < 0)
808 return r;
809 }
810
b261b5f4 811 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
73b49d43 812 r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
7c3de8f8
PF
813 if (r < 0)
814 return r;
7c3de8f8
PF
815 }
816
a9aff361
PF
817 break;
818
3dc34fcc 819 case DHCP6_STATE_REBIND:
59f1ded8 820 message->type = DHCP6_MESSAGE_REBIND;
3dc34fcc 821
b261b5f4 822 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
f311a62b
PF
823 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
824 if (r < 0)
825 return r;
826 }
3dc34fcc 827
8006aa32
SA
828 if (client->fqdn) {
829 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
830 if (r < 0)
831 return r;
832 }
833
de8d6e55
SS
834 if (client->mudurl) {
835 r = dhcp6_option_append(&opt, &optlen,
f5e3619b 836 SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
de8d6e55
SS
837 client->mudurl);
838 if (r < 0)
839 return r;
840 }
841
33923925
SS
842 if (client->user_class) {
843 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
844 if (r < 0)
845 return r;
846 }
847
73c8ced7
SS
848 if (client->vendor_class) {
849 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
850 if (r < 0)
851 return r;
852 }
853
99ccb8ff
SS
854 if (!ordered_hashmap_isempty(client->vendor_options)) {
855 r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
856 if (r < 0)
857 return r;
858 }
859
b261b5f4 860 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
73b49d43 861 r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
7c3de8f8
PF
862 if (r < 0)
863 return r;
7c3de8f8
PF
864 }
865
3dc34fcc
PF
866 break;
867
a9aff361 868 case DHCP6_STATE_STOPPED:
a34b57c0 869 case DHCP6_STATE_BOUND:
a9aff361 870 return -EINVAL;
dd5e9378
YW
871 default:
872 assert_not_reached();
a9aff361
PF
873 }
874
2c1ab8ca 875 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
da6fe470
PF
876 client->req_opts_len * sizeof(be16_t),
877 client->req_opts);
878 if (r < 0)
879 return r;
880
ccd1fc2f 881 assert(client->duid_len);
2c1ab8ca 882 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
66eac120 883 client->duid_len, &client->duid);
7246333c
PF
884 if (r < 0)
885 return r;
886
346e13a2
PF
887 elapsed_usec = time_now - client->transaction_start;
888 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
889 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
890 else
891 elapsed_time = 0xffff;
892
2c1ab8ca 893 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
346e13a2
PF
894 sizeof(elapsed_time), &elapsed_time);
895 if (r < 0)
896 return r;
897
90e74a66 898 ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
e7d5fe17
AD
899 r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
900 if (r < 0)
901 return r;
902 }
903
a9aff361
PF
904 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
905 len - optlen);
906 if (r < 0)
907 return r;
908
909 log_dhcp6_client(client, "Sent %s",
910 dhcp6_message_type_to_string(message->type));
911
912 return 0;
913}
914
4b558378 915static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
916 sd_dhcp6_client *client = userdata;
917
a1140666
LP
918 assert(s);
919 assert(client);
920 assert(client->lease);
a34b57c0 921
c9393e8c 922 (void) event_source_disable(client->timeout_t2);
a34b57c0
PF
923
924 log_dhcp6_client(client, "Timeout T2");
925
3dc34fcc
PF
926 client_start(client, DHCP6_STATE_REBIND);
927
a34b57c0
PF
928 return 0;
929}
930
4b558378 931static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
932 sd_dhcp6_client *client = userdata;
933
a1140666
LP
934 assert(s);
935 assert(client);
936 assert(client->lease);
a34b57c0 937
c9393e8c 938 (void) event_source_disable(client->timeout_t1);
a34b57c0
PF
939
940 log_dhcp6_client(client, "Timeout T1");
941
3dc34fcc
PF
942 client_start(client, DHCP6_STATE_RENEW);
943
a34b57c0
PF
944 return 0;
945}
946
4b558378 947static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3 948 sd_dhcp6_client *client = userdata;
3dc34fcc 949 DHCP6_CLIENT_DONT_DESTROY(client);
dd5e9378 950 DHCP6State state;
d1b0afe3
PF
951
952 assert(s);
953 assert(client);
954 assert(client->event);
955
3dc34fcc
PF
956 state = client->state;
957
10c9ce61 958 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
d1b0afe3 959
3dc34fcc
PF
960 /* RFC 3315, section 18.1.4., says that "...the client may choose to
961 use a Solicit message to locate a new DHCP server..." */
962 if (state == DHCP6_STATE_REBIND)
963 client_start(client, DHCP6_STATE_SOLICITATION);
964
d1b0afe3
PF
965 return 0;
966}
967
968static usec_t client_timeout_compute_random(usec_t val) {
9de8a425 969 return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC;
d1b0afe3
PF
970}
971
4b558378 972static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3
PF
973 int r = 0;
974 sd_dhcp6_client *client = userdata;
4b4923e6 975 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
5e913450 976 usec_t max_retransmit_duration = 0;
513a6fa8 977 uint8_t max_retransmit_count = 0;
d1b0afe3
PF
978
979 assert(s);
980 assert(client);
981 assert(client->event);
982
c9393e8c 983 (void) event_source_disable(client->timeout_resend);
d1b0afe3
PF
984
985 switch (client->state) {
bbfa43ca
PF
986 case DHCP6_STATE_INFORMATION_REQUEST:
987 init_retransmit_time = DHCP6_INF_TIMEOUT;
988 max_retransmit_time = DHCP6_INF_MAX_RT;
989
990 break;
991
d1b0afe3 992 case DHCP6_STATE_SOLICITATION:
7246333c 993
0936c189 994 if (client->retransmit_count > 0 && client->lease) {
7246333c
PF
995 client_start(client, DHCP6_STATE_REQUEST);
996 return 0;
997 }
998
d1b0afe3
PF
999 init_retransmit_time = DHCP6_SOL_TIMEOUT;
1000 max_retransmit_time = DHCP6_SOL_MAX_RT;
d1b0afe3
PF
1001
1002 break;
1003
7246333c
PF
1004 case DHCP6_STATE_REQUEST:
1005 init_retransmit_time = DHCP6_REQ_TIMEOUT;
1006 max_retransmit_time = DHCP6_REQ_MAX_RT;
1007 max_retransmit_count = DHCP6_REQ_MAX_RC;
7246333c
PF
1008
1009 break;
1010
3dc34fcc
PF
1011 case DHCP6_STATE_RENEW:
1012 init_retransmit_time = DHCP6_REN_TIMEOUT;
1013 max_retransmit_time = DHCP6_REN_MAX_RT;
3dc34fcc
PF
1014
1015 /* RFC 3315, section 18.1.3. says max retransmit duration will
1016 be the remaining time until T2. Instead of setting MRD,
1017 wait for T2 to trigger with the same end result */
3dc34fcc
PF
1018
1019 break;
1020
1021 case DHCP6_STATE_REBIND:
1022 init_retransmit_time = DHCP6_REB_TIMEOUT;
1023 max_retransmit_time = DHCP6_REB_MAX_RT;
3dc34fcc 1024
c9393e8c 1025 if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
3ae01632
YW
1026 uint32_t expire = 0;
1027
1028 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire);
3dc34fcc
PF
1029 if (r < 0) {
1030 client_stop(client, r);
1031 return 0;
1032 }
1033 max_retransmit_duration = expire * USEC_PER_SEC;
1034 }
1035
1036 break;
1037
d1b0afe3 1038 case DHCP6_STATE_STOPPED:
a34b57c0 1039 case DHCP6_STATE_BOUND:
d1b0afe3 1040 return 0;
dd5e9378
YW
1041 default:
1042 assert_not_reached();
d1b0afe3
PF
1043 }
1044
3ae01632 1045 if (max_retransmit_count > 0 &&
d1b0afe3 1046 client->retransmit_count >= max_retransmit_count) {
10c9ce61 1047 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
d1b0afe3
PF
1048 return 0;
1049 }
1050
fa94c34b 1051 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d1b0afe3
PF
1052 if (r < 0)
1053 goto error;
1054
346e13a2
PF
1055 r = client_send_message(client, time_now);
1056 if (r >= 0)
1057 client->retransmit_count++;
1058
3ae01632 1059 if (client->retransmit_time == 0) {
d1b0afe3
PF
1060 client->retransmit_time =
1061 client_timeout_compute_random(init_retransmit_time);
a9aff361
PF
1062
1063 if (client->state == DHCP6_STATE_SOLICITATION)
1064 client->retransmit_time += init_retransmit_time / 10;
1065
d1b0afe3 1066 } else {
7502812c
YW
1067 assert(max_retransmit_time > 0);
1068 if (client->retransmit_time > max_retransmit_time / 2)
d1b0afe3
PF
1069 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
1070 else
1071 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
1072 }
1073
1074 log_dhcp6_client(client, "Next retransmission in %s",
5291f26d 1075 FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC));
d1b0afe3 1076
c9393e8c
YW
1077 r = event_reset_time(client->event, &client->timeout_resend,
1078 clock_boottime_or_monotonic(),
1079 time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
1080 client_timeout_resend, client,
1081 client->event_priority, "dhcp6-resend-timer", true);
d1b0afe3
PF
1082 if (r < 0)
1083 goto error;
1084
3ae01632 1085 if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
d1b0afe3
PF
1086
1087 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
1088 max_retransmit_duration / USEC_PER_SEC);
1089
c9393e8c
YW
1090 r = event_reset_time(client->event, &client->timeout_resend_expire,
1091 clock_boottime_or_monotonic(),
1092 time_now + max_retransmit_duration, USEC_PER_SEC,
1093 client_timeout_resend_expire, client,
1094 client->event_priority, "dhcp6-resend-expire-timer", true);
9021bb9f
TG
1095 if (r < 0)
1096 goto error;
d1b0afe3
PF
1097 }
1098
1099error:
1100 if (r < 0)
1101 client_stop(client, r);
1102
1103 return 0;
1104}
1105
f12abb48 1106static int client_ensure_iaid(sd_dhcp6_client *client) {
cfb5b380 1107 int r;
6d13616b 1108 uint32_t iaid;
f12abb48
PF
1109
1110 assert(client);
1111
8217ed5e 1112 if (client->iaid_set)
f12abb48
PF
1113 return 0;
1114
8d71f2b3
YW
1115 r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
1116 /* legacy_unstable_byteorder = */ true,
1117 /* use_mac = */ client->test_mode,
1118 &iaid);
cfb5b380
TG
1119 if (r < 0)
1120 return r;
f12abb48 1121
69b43997
PF
1122 client->ia_na.ia_na.id = iaid;
1123 client->ia_pd.ia_pd.id = iaid;
8217ed5e 1124 client->iaid_set = true;
69b43997 1125
f12abb48
PF
1126 return 0;
1127}
1128
4b558378
ZJS
1129static int client_parse_message(
1130 sd_dhcp6_client *client,
1131 DHCP6Message *message,
1132 size_t len,
1133 sd_dhcp6_lease *lease) {
da07cf35 1134
8a895550 1135 uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
5c95a913 1136 usec_t irt = IRT_DEFAULT;
631bbe71 1137 int r;
631bbe71 1138
a1140666
LP
1139 assert(client);
1140 assert(message);
1141 assert(len >= sizeof(DHCP6Message));
1142 assert(lease);
1143
44481a8b 1144 len -= sizeof(DHCP6Message);
548c33d7
YW
1145 for (size_t offset = 0; offset < len;) {
1146 uint16_t optcode;
1147 size_t optlen;
1148 const uint8_t *optval;
44481a8b 1149
548c33d7
YW
1150 r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
1151 if (r < 0)
1152 return r;
8a895550 1153
631bbe71 1154 switch (optcode) {
2c1ab8ca 1155 case SD_DHCP6_OPTION_CLIENTID:
e79b4b85 1156 if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
d3aa9d6a 1157 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
0eec7f5f 1158 dhcp6_message_type_to_string(message->type));
631bbe71 1159
e79b4b85
YW
1160 r = dhcp6_lease_set_clientid(lease, optval, optlen);
1161 if (r < 0)
1162 return r;
631bbe71
PF
1163
1164 break;
1165
2c1ab8ca 1166 case SD_DHCP6_OPTION_SERVERID:
65457c17 1167 if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
d3aa9d6a 1168 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
0eec7f5f 1169 dhcp6_message_type_to_string(message->type));
631bbe71
PF
1170
1171 r = dhcp6_lease_set_serverid(lease, optval, optlen);
1172 if (r < 0)
1173 return r;
1174
1175 break;
1176
2c1ab8ca 1177 case SD_DHCP6_OPTION_PREFERENCE:
631bbe71
PF
1178 if (optlen != 1)
1179 return -EINVAL;
1180
3bc424a3 1181 r = dhcp6_lease_set_preference(lease, optval[0]);
631bbe71
PF
1182 if (r < 0)
1183 return r;
1184
1185 break;
1186
4af39cb8
YW
1187 case SD_DHCP6_OPTION_STATUS_CODE: {
1188 _cleanup_free_ char *msg = NULL;
91c43f39 1189
4af39cb8
YW
1190 r = dhcp6_option_parse_status(optval, optlen, &msg);
1191 if (r < 0)
1192 return r;
631bbe71 1193
4af39cb8
YW
1194 if (r > 0)
1195 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
1196 "Received %s message with non-zero status: %s%s%s",
1197 dhcp6_message_type_to_string(message->type),
1198 strempty(msg), isempty(msg) ? "" : ": ",
1199 dhcp6_message_status_to_string(r));
631bbe71 1200 break;
4af39cb8 1201 }
8a895550
YW
1202 case SD_DHCP6_OPTION_IA_NA: {
1203 _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
1204
bbfa43ca 1205 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
0eec7f5f 1206 log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
bbfa43ca
PF
1207 break;
1208 }
1209
8a895550
YW
1210 r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia);
1211 if (r == -ENOMEM)
631bbe71 1212 return r;
8a895550 1213 if (r < 0)
5c95a913 1214 continue;
5c95a913 1215
b47fb949 1216 if (lease->ia.addresses) {
8a895550
YW
1217 log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
1218 continue;
b47fb949
PF
1219 }
1220
8a895550
YW
1221 lease->ia = ia;
1222 ia = (DHCP6IA) {};
1223
1224 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
1225 lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
1226
dce6563f 1227 break;
8a895550
YW
1228 }
1229 case SD_DHCP6_OPTION_IA_PD: {
1230 _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
dce6563f 1231
dce6563f 1232 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
0eec7f5f 1233 log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
dce6563f
PF
1234 break;
1235 }
1236
8a895550
YW
1237 r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia);
1238 if (r == -ENOMEM)
dce6563f 1239 return r;
8a895550 1240 if (r < 0)
5c95a913 1241 continue;
5c95a913 1242
b47fb949 1243 if (lease->pd.addresses) {
8a895550
YW
1244 log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
1245 continue;
b47fb949
PF
1246 }
1247
8a895550
YW
1248 lease->pd = ia;
1249 ia = (DHCP6IA) {};
ed6ee219 1250
8a895550
YW
1251 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
1252 lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
1253
1254 break;
1255 }
2c1ab8ca 1256 case SD_DHCP6_OPTION_RAPID_COMMIT:
ed6ee219
PF
1257 r = dhcp6_lease_set_rapid_commit(lease);
1258 if (r < 0)
1259 return r;
1260
1261 break;
7bd8e95d 1262
2c1ab8ca 1263 case SD_DHCP6_OPTION_DNS_SERVERS:
e210f027 1264 r = dhcp6_lease_add_dns(lease, optval, optlen);
7bd8e95d
PF
1265 if (r < 0)
1266 return r;
1267
1268 break;
5da1b97f 1269
2c1ab8ca 1270 case SD_DHCP6_OPTION_DOMAIN_LIST:
41b14f03 1271 r = dhcp6_lease_add_domains(lease, optval, optlen);
5da1b97f
PF
1272 if (r < 0)
1273 return r;
1274
1275 break;
1276
2c1ab8ca 1277 case SD_DHCP6_OPTION_NTP_SERVER:
9c3d46bf 1278 r = dhcp6_lease_add_ntp(lease, optval, optlen);
6599680e
PF
1279 if (r < 0)
1280 return r;
1281
1282 break;
41e4615d 1283
2c1ab8ca 1284 case SD_DHCP6_OPTION_SNTP_SERVERS:
e693e969 1285 r = dhcp6_lease_add_sntp(lease, optval, optlen);
41e4615d
PF
1286 if (r < 0)
1287 return r;
1288
1289 break;
fcb51238 1290
f5e3619b 1291 case SD_DHCP6_OPTION_CLIENT_FQDN:
c43eea9f
BG
1292 r = dhcp6_lease_set_fqdn(lease, optval, optlen);
1293 if (r < 0)
1294 return r;
1295
1296 break;
1297
fcb51238 1298 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
6ffe71d0
YW
1299 if (optlen != 4)
1300 return -EINVAL;
1301
0eb5e6d3 1302 irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
fcb51238 1303 break;
631bbe71
PF
1304 }
1305 }
1306
e79b4b85
YW
1307 uint8_t *clientid;
1308 size_t clientid_len;
1309 if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
d3aa9d6a 1310 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.",
0eec7f5f 1311 dhcp6_message_type_to_string(message->type));
631bbe71 1312
e79b4b85
YW
1313 if (clientid_len != client->duid_len ||
1314 memcmp(clientid, &client->duid, clientid_len) != 0)
1315 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.",
1316 dhcp6_message_type_to_string(message->type));
1317
bbfa43ca 1318 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
99f1d3fc 1319 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
0eec7f5f
YW
1320 if (r < 0)
1321 return log_dhcp6_client_errno(client, r, "%s has no server id",
1322 dhcp6_message_type_to_string(message->type));
b47fb949 1323
8a895550
YW
1324 if (!lease->ia.addresses && !lease->pd.addresses)
1325 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
b47fb949 1326
8a895550
YW
1327 if (lease->ia.addresses) {
1328 lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
1329 lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
1330 }
1331
1332 if (lease->pd.addresses) {
1333 lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
1334 lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
1335 }
bbfa43ca 1336 }
631bbe71 1337
fcb51238
YW
1338 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
1339
21a9905c 1340 return 0;
631bbe71
PF
1341}
1342
ef4edc15
YW
1343static int client_receive_reply(
1344 sd_dhcp6_client *client,
1345 DHCP6Message *reply,
1346 size_t len,
1347 const triple_timestamp *t,
1348 const struct in6_addr *server_address) {
1349
4afd3348 1350 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
ed6ee219 1351 bool rapid_commit;
a1140666
LP
1352 int r;
1353
1354 assert(client);
1355 assert(reply);
653ddc1d 1356 assert(t);
a34b57c0 1357
59f1ded8 1358 if (reply->type != DHCP6_MESSAGE_REPLY)
ed6ee219 1359 return 0;
a34b57c0
PF
1360
1361 r = dhcp6_lease_new(&lease);
1362 if (r < 0)
1363 return -ENOMEM;
1364
653ddc1d 1365 lease->timestamp = *t;
ef4edc15
YW
1366 if (server_address)
1367 lease->server_address = *server_address;
653ddc1d 1368
a34b57c0
PF
1369 r = client_parse_message(client, reply, len, lease);
1370 if (r < 0)
1371 return r;
1372
ed6ee219
PF
1373 if (client->state == DHCP6_STATE_SOLICITATION) {
1374 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1375 if (r < 0)
1376 return r;
1377
1378 if (!rapid_commit)
1379 return 0;
1380 }
1381
431a4bc8
YW
1382 sd_dhcp6_lease_unref(client->lease);
1383 client->lease = TAKE_PTR(lease);
a34b57c0
PF
1384
1385 return DHCP6_STATE_BOUND;
1386}
1387
ef4edc15
YW
1388static int client_receive_advertise(
1389 sd_dhcp6_client *client,
1390 DHCP6Message *advertise,
1391 size_t len,
1392 const triple_timestamp *t,
1393 const struct in6_addr *server_address) {
1394
4afd3348 1395 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
631bbe71 1396 uint8_t pref_advertise = 0, pref_lease = 0;
a1140666 1397 int r;
631bbe71 1398
653ddc1d
YW
1399 assert(client);
1400 assert(advertise);
1401 assert(t);
1402
59f1ded8 1403 if (advertise->type != DHCP6_MESSAGE_ADVERTISE)
ed6ee219 1404 return 0;
631bbe71
PF
1405
1406 r = dhcp6_lease_new(&lease);
1407 if (r < 0)
1408 return r;
1409
653ddc1d 1410 lease->timestamp = *t;
ef4edc15
YW
1411 if (server_address)
1412 lease->server_address = *server_address;
653ddc1d 1413
631bbe71
PF
1414 r = client_parse_message(client, advertise, len, lease);
1415 if (r < 0)
1416 return r;
1417
1418 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1419 if (r < 0)
1420 return r;
1421
1422 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
bbfa43ca
PF
1423
1424 if (r < 0 || pref_advertise > pref_lease) {
431a4bc8
YW
1425 sd_dhcp6_lease_unref(client->lease);
1426 client->lease = TAKE_PTR(lease);
631bbe71
PF
1427 r = 0;
1428 }
1429
7246333c
PF
1430 if (pref_advertise == 255 || client->retransmit_count > 1)
1431 r = DHCP6_STATE_REQUEST;
1432
631bbe71
PF
1433 return r;
1434}
1435
004845d1
LP
1436static int client_receive_message(
1437 sd_event_source *s,
1438 int fd, uint32_t
1439 revents,
1440 void *userdata) {
1441
631bbe71 1442 sd_dhcp6_client *client = userdata;
3f0c075f 1443 DHCP6_CLIENT_DONT_DESTROY(client);
653ddc1d
YW
1444 /* This needs to be initialized with zero. See #20741. */
1445 CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
1446 struct iovec iov;
ef4edc15 1447 union sockaddr_union sa = {};
653ddc1d 1448 struct msghdr msg = {
ef4edc15
YW
1449 .msg_name = &sa.sa,
1450 .msg_namelen = sizeof(sa),
653ddc1d
YW
1451 .msg_iov = &iov,
1452 .msg_iovlen = 1,
1453 .msg_control = &control,
1454 .msg_controllen = sizeof(control),
1455 };
1456 struct cmsghdr *cmsg;
1457 triple_timestamp t = {};
0d43d2fc 1458 _cleanup_free_ DHCP6Message *message = NULL;
ef4edc15 1459 struct in6_addr *server_address = NULL;
4edc2c9b
LP
1460 ssize_t buflen, len;
1461 int r = 0;
631bbe71
PF
1462
1463 assert(s);
1464 assert(client);
1465 assert(client->event);
1466
4edc2c9b 1467 buflen = next_datagram_size_fd(fd);
d46b79bb 1468 if (buflen == -ENETDOWN)
653ddc1d
YW
1469 /* the link is down. Don't return an error or the I/O event source will be disconnected
1470 * and we won't be able to receive packets again when the link comes back. */
22a3fd2d 1471 return 0;
4edc2c9b
LP
1472 if (buflen < 0)
1473 return buflen;
631bbe71 1474
0d43d2fc 1475 message = malloc(buflen);
631bbe71
PF
1476 if (!message)
1477 return -ENOMEM;
1478
653ddc1d
YW
1479 iov = IOVEC_MAKE(message, buflen);
1480
1481 len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
0d43d2fc 1482 if (len < 0) {
22a3fd2d
BG
1483 /* see comment above for why we shouldn't error out on ENETDOWN. */
1484 if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
0d43d2fc
TG
1485 return 0;
1486
bd9a7221 1487 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
0d43d2fc 1488
004845d1
LP
1489 }
1490 if ((size_t) len < sizeof(DHCP6Message)) {
1491 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
631bbe71 1492 return 0;
004845d1 1493 }
631bbe71 1494
ef4edc15
YW
1495 /* msg_namelen == 0 happens when running the test-suite over a socketpair */
1496 if (msg.msg_namelen > 0) {
1497 if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
1498 log_dhcp6_client(client, "Received message from invalid source, ignoring.");
1499 return 0;
1500 }
1501
1502 server_address = &sa.in6.sin6_addr;
1503 }
1504
653ddc1d
YW
1505 CMSG_FOREACH(cmsg, &msg) {
1506 if (cmsg->cmsg_level == SOL_SOCKET &&
1507 cmsg->cmsg_type == SO_TIMESTAMP &&
1508 cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
1509 triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
1510 }
1511
1512 if (!triple_timestamp_is_set(&t))
1513 triple_timestamp_get(&t);
1514
59f1ded8
YW
1515 if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) {
1516 const char *type_str = dhcp6_message_type_to_string(message->type);
1517 if (type_str)
1518 log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str);
1519 else
1520 log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type);
631bbe71
PF
1521 return 0;
1522 }
1523
d7799877 1524 if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
631bbe71
PF
1525 return 0;
1526
1527 switch (client->state) {
bbfa43ca 1528 case DHCP6_STATE_INFORMATION_REQUEST:
ef4edc15 1529 r = client_receive_reply(client, message, len, &t, server_address);
d7799877
YW
1530 if (r < 0) {
1531 log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
bbfa43ca 1532 return 0;
d7799877 1533 }
bbfa43ca 1534
10c9ce61 1535 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
bbfa43ca
PF
1536
1537 client_start(client, DHCP6_STATE_STOPPED);
1538
1539 break;
1540
631bbe71 1541 case DHCP6_STATE_SOLICITATION:
ef4edc15 1542 r = client_receive_advertise(client, message, len, &t, server_address);
d7799877
YW
1543 if (r < 0) {
1544 log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
1545 return 0;
1546 }
631bbe71 1547
ed6ee219 1548 if (r == DHCP6_STATE_REQUEST) {
7246333c 1549 client_start(client, r);
ed6ee219
PF
1550 break;
1551 }
631bbe71 1552
d7b34e38 1553 _fallthrough_; /* for Solicitation Rapid Commit option check */
7246333c 1554 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1555 case DHCP6_STATE_RENEW:
1556 case DHCP6_STATE_REBIND:
1557
ef4edc15 1558 r = client_receive_reply(client, message, len, &t, server_address);
d7799877
YW
1559 if (r < 0) {
1560 log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
a34b57c0 1561 return 0;
d7799877 1562 }
a34b57c0
PF
1563
1564 if (r == DHCP6_STATE_BOUND) {
a34b57c0
PF
1565 r = client_start(client, DHCP6_STATE_BOUND);
1566 if (r < 0) {
1567 client_stop(client, r);
1568 return 0;
1569 }
1570
10c9ce61 1571 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
a34b57c0
PF
1572 }
1573
1574 break;
1575
1576 case DHCP6_STATE_BOUND:
1577
1578 break;
1579
631bbe71 1580 case DHCP6_STATE_STOPPED:
631bbe71 1581 return 0;
dd5e9378
YW
1582 default:
1583 assert_not_reached();
631bbe71
PF
1584 }
1585
712fdfd6
YW
1586 log_dhcp6_client(client, "Recv %s",
1587 dhcp6_message_type_to_string(message->type));
631bbe71 1588
a9aff361
PF
1589 return 0;
1590}
1591
134ebaa4
PF
1592static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
1593 uint32_t *lifetime_t2) {
1594 assert_return(client, -EINVAL);
1595 assert_return(client->lease, -EINVAL);
1596
b261b5f4 1597 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
134ebaa4
PF
1598 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
1599 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
1600
1601 return 0;
1602 }
1603
b261b5f4 1604 if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
134ebaa4
PF
1605 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
1606 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
1607
1608 return 0;
1609 }
1610
1611 return -ENOMSG;
1612}
1613
dd5e9378 1614static int client_start(sd_dhcp6_client *client, DHCP6State state) {
f12abb48 1615 int r;
a34b57c0 1616 usec_t timeout, time_now;
134ebaa4 1617 uint32_t lifetime_t1, lifetime_t2;
f12abb48
PF
1618
1619 assert_return(client, -EINVAL);
1620 assert_return(client->event, -EINVAL);
2f8e7633 1621 assert_return(client->ifindex > 0, -EINVAL);
c3e2adea 1622 assert_return(client->state != state, -EINVAL);
f12abb48 1623
c9393e8c
YW
1624 (void) event_source_disable(client->timeout_resend_expire);
1625 (void) event_source_disable(client->timeout_resend);
c3e2adea
PF
1626 client->retransmit_time = 0;
1627 client->retransmit_count = 0;
f12abb48 1628
38a03f06
LP
1629 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1630 if (r < 0)
1631 return r;
346e13a2 1632
7ac6c26a
PF
1633 if (!client->receive_message) {
1634 r = sd_event_add_io(client->event, &client->receive_message,
1635 client->fd, EPOLLIN, client_receive_message,
1636 client);
1637 if (r < 0)
1638 goto error;
1639
1640 r = sd_event_source_set_priority(client->receive_message,
1641 client->event_priority);
1642 if (r < 0)
1643 goto error;
1644
1645 r = sd_event_source_set_description(client->receive_message,
1646 "dhcp6-receive-message");
1647 if (r < 0)
1648 goto error;
1649 }
1650
c3e2adea
PF
1651 switch (state) {
1652 case DHCP6_STATE_STOPPED:
bbfa43ca
PF
1653 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1654 client->state = DHCP6_STATE_STOPPED;
a9aff361 1655
bbfa43ca
PF
1656 return 0;
1657 }
9021bb9f 1658
4831981d 1659 _fallthrough_;
bbfa43ca 1660 case DHCP6_STATE_SOLICITATION:
c3e2adea
PF
1661 client->state = DHCP6_STATE_SOLICITATION;
1662
7246333c
PF
1663 break;
1664
bbfa43ca 1665 case DHCP6_STATE_INFORMATION_REQUEST:
7246333c 1666 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1667 case DHCP6_STATE_RENEW:
1668 case DHCP6_STATE_REBIND:
a34b57c0 1669
7246333c
PF
1670 client->state = state;
1671
c3e2adea 1672 break;
a34b57c0
PF
1673
1674 case DHCP6_STATE_BOUND:
1675
134ebaa4
PF
1676 r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
1677 if (r < 0)
1678 goto error;
a34b57c0 1679
134ebaa4 1680 if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
bd9a7221 1681 log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
134ebaa4 1682 lifetime_t1, lifetime_t2);
a34b57c0
PF
1683
1684 return 0;
1685 }
1686
134ebaa4 1687 timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
a34b57c0 1688
5291f26d 1689 log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
a34b57c0 1690
c9393e8c
YW
1691 r = event_reset_time(client->event, &client->timeout_t1,
1692 clock_boottime_or_monotonic(),
1693 time_now + timeout, 10 * USEC_PER_SEC,
1694 client_timeout_t1, client,
1695 client->event_priority, "dhcp6-t1-timeout", true);
9021bb9f 1696 if (r < 0)
7ac6c26a 1697 goto error;
9021bb9f 1698
134ebaa4 1699 timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
a34b57c0 1700
5291f26d 1701 log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
a34b57c0 1702
c9393e8c
YW
1703 r = event_reset_time(client->event, &client->timeout_t2,
1704 clock_boottime_or_monotonic(),
1705 time_now + timeout, 10 * USEC_PER_SEC,
1706 client_timeout_t2, client,
1707 client->event_priority, "dhcp6-t2-timeout", true);
9021bb9f 1708 if (r < 0)
7ac6c26a 1709 goto error;
9021bb9f 1710
3dc34fcc
PF
1711 client->state = state;
1712
a34b57c0 1713 return 0;
dd5e9378
YW
1714 default:
1715 assert_not_reached();
c3e2adea 1716 }
a9aff361 1717
c3e2adea 1718 client->transaction_id = random_u32() & htobe32(0x00ffffff);
346e13a2 1719 client->transaction_start = time_now;
d1b0afe3 1720
c9393e8c
YW
1721 r = event_reset_time(client->event, &client->timeout_resend,
1722 clock_boottime_or_monotonic(),
1723 0, 0,
1724 client_timeout_resend, client,
1725 client->event_priority, "dhcp6-resend-timeout", true);
9021bb9f 1726 if (r < 0)
7ac6c26a 1727 goto error;
9021bb9f 1728
f12abb48 1729 return 0;
7ac6c26a
PF
1730
1731 error:
1732 client_reset(client);
1733 return r;
f12abb48
PF
1734}
1735
0ae0e5cd 1736int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
c8bae363
YW
1737 if (!client)
1738 return 0;
f667c150 1739
10c9ce61 1740 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
139b011a 1741
7ac6c26a
PF
1742 client->fd = safe_close(client->fd);
1743
139b011a
PF
1744 return 0;
1745}
1746
f667c150
TG
1747int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1748 assert_return(client, -EINVAL);
1749
1750 return client->state != DHCP6_STATE_STOPPED;
1751}
1752
0ae0e5cd 1753int sd_dhcp6_client_start(sd_dhcp6_client *client) {
dd5e9378 1754 DHCP6State state = DHCP6_STATE_SOLICITATION;
6d95e7d9 1755 int r;
139b011a
PF
1756
1757 assert_return(client, -EINVAL);
1758 assert_return(client->event, -EINVAL);
2f8e7633 1759 assert_return(client->ifindex > 0, -EINVAL);
94876904 1760 assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
139b011a 1761
080e5c2f 1762 if (client->state != DHCP6_STATE_STOPPED)
63348d13 1763 return -EBUSY;
d7c9c21f 1764
b261b5f4 1765 if (!client->information_request && client->request_ia == 0)
f311a62b
PF
1766 return -EINVAL;
1767
c806ffb9 1768 r = client_reset(client);
f12abb48
PF
1769 if (r < 0)
1770 return r;
1771
bbfa43ca
PF
1772 r = client_ensure_iaid(client);
1773 if (r < 0)
1774 return r;
1775
cc22955c
TH
1776 r = client_ensure_duid(client);
1777 if (r < 0)
1778 return r;
1779
10a0f27b
PF
1780 if (client->fd < 0) {
1781 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1782 if (r < 0) {
1783 _cleanup_free_ char *p = NULL;
1784
1785 (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1786 return log_dhcp6_client_errno(client, r,
1787 "Failed to bind to UDP socket at address %s: %m", strna(p));
1788 }
bd9a7221 1789
10a0f27b 1790 client->fd = r;
bd9a7221 1791 }
bbfa43ca 1792
fcb51238
YW
1793 if (client->information_request) {
1794 usec_t t = now(CLOCK_MONOTONIC);
1795
1796 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1797 return 0;
1798
1799 client->information_request_time_usec = t;
bbfa43ca 1800 state = DHCP6_STATE_INFORMATION_REQUEST;
fcb51238 1801 }
bbfa43ca
PF
1802
1803 log_dhcp6_client(client, "Started in %s mode",
787dd704 1804 client->information_request ? "Information request" : "Managed");
bbfa43ca
PF
1805
1806 return client_start(client, state);
139b011a
PF
1807}
1808
32d20645 1809int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
139b011a
PF
1810 int r;
1811
1812 assert_return(client, -EINVAL);
1813 assert_return(!client->event, -EBUSY);
1814
1815 if (event)
1816 client->event = sd_event_ref(event);
1817 else {
1818 r = sd_event_default(&client->event);
1819 if (r < 0)
1820 return 0;
1821 }
1822
1823 client->event_priority = priority;
1824
1825 return 0;
1826}
1827
1828int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1829 assert_return(client, -EINVAL);
1830
1831 client->event = sd_event_unref(client->event);
1832
1833 return 0;
1834}
1835
1836sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
a1140666 1837 assert_return(client, NULL);
139b011a
PF
1838
1839 return client->event;
1840}
1841
8301aa0b
YW
1842static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1843 assert(client);
139b011a 1844
c9393e8c
YW
1845 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1846 client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
1847 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1848 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1849
3733eec3
LP
1850 client_reset(client);
1851
10a0f27b
PF
1852 client->fd = safe_close(client->fd);
1853
3733eec3 1854 sd_dhcp6_client_detach_event(client);
3733eec3
LP
1855
1856 free(client->req_opts);
8006aa32 1857 free(client->fqdn);
de8d6e55 1858 free(client->mudurl);
33923925 1859
e7d5fe17 1860 ordered_hashmap_free(client->extra_options);
33923925 1861 strv_free(client->user_class);
73c8ced7 1862 strv_free(client->vendor_class);
61a9fa8f 1863 free(client->ifname);
33923925 1864
6b430fdb 1865 return mfree(client);
139b011a
PF
1866}
1867
8301aa0b
YW
1868DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1869
0ae0e5cd 1870int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
4afd3348 1871 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
8b8ecac8 1872 _cleanup_free_ be16_t *req_opts = NULL;
da6fe470 1873 size_t t;
139b011a
PF
1874
1875 assert_return(ret, -EINVAL);
1876
8b8ecac8
YW
1877 req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
1878 if (!req_opts)
139b011a
PF
1879 return -ENOMEM;
1880
8b8ecac8
YW
1881 for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
1882 req_opts[t] = htobe16(default_req_opts[t]);
c806ffb9 1883
8b8ecac8
YW
1884 client = new(sd_dhcp6_client, 1);
1885 if (!client)
da6fe470
PF
1886 return -ENOMEM;
1887
8b8ecac8
YW
1888 *client = (sd_dhcp6_client) {
1889 .n_ref = 1,
1890 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1891 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1892 .ifindex = -1,
01b4e90f 1893 .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
8b8ecac8
YW
1894 .fd = -1,
1895 .req_opts_len = ELEMENTSOF(default_req_opts),
2805536b
SS
1896 .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
1897 .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1,
8b8ecac8
YW
1898 .req_opts = TAKE_PTR(req_opts),
1899 };
da6fe470 1900
1cc6c93a 1901 *ret = TAKE_PTR(client);
139b011a
PF
1902
1903 return 0;
1904}