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