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