]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
dhcp6: parse the FQDN option
[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 1284
c43eea9f
BG
1285 case SD_DHCP6_OPTION_FQDN:
1286 r = dhcp6_lease_set_fqdn(lease, optval, optlen);
1287 if (r < 0)
1288 return r;
1289
1290 break;
1291
fcb51238 1292 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
6ffe71d0
YW
1293 if (optlen != 4)
1294 return -EINVAL;
1295
0eb5e6d3 1296 irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
fcb51238 1297 break;
631bbe71 1298 }
6599680e 1299
23976cb9 1300 pos += offsetof(DHCP6Option, data) + optlen;
631bbe71
PF
1301 }
1302
5c95a913
SS
1303 if (ia_na_status > 0 && ia_pd_status > 0) {
1304 log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
1305 return -EINVAL;
1306 }
1307
1eeddba4 1308 if (!clientid) {
631bbe71
PF
1309 log_dhcp6_client(client, "%s has incomplete options",
1310 dhcp6_message_type_to_string(message->type));
1311 return -EINVAL;
1312 }
1313
bbfa43ca 1314 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
99f1d3fc
ZJS
1315 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1316 if (r < 0) {
bbfa43ca
PF
1317 log_dhcp6_client(client, "%s has no server id",
1318 dhcp6_message_type_to_string(message->type));
99f1d3fc
ZJS
1319 return -EINVAL;
1320 }
b47fb949 1321
99f1d3fc
ZJS
1322 } else {
1323 if (lease->ia.addresses) {
1324 lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
1325 lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
1326 }
b47fb949 1327
99f1d3fc
ZJS
1328 if (lease->pd.addresses) {
1329 lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
1330 lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
1331 }
bbfa43ca 1332 }
631bbe71 1333
fcb51238
YW
1334 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
1335
21a9905c 1336 return 0;
631bbe71
PF
1337}
1338
0ae0e5cd 1339static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
4afd3348 1340 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
ed6ee219 1341 bool rapid_commit;
a1140666
LP
1342 int r;
1343
1344 assert(client);
1345 assert(reply);
a34b57c0
PF
1346
1347 if (reply->type != DHCP6_REPLY)
ed6ee219 1348 return 0;
a34b57c0
PF
1349
1350 r = dhcp6_lease_new(&lease);
1351 if (r < 0)
1352 return -ENOMEM;
1353
1354 r = client_parse_message(client, reply, len, lease);
1355 if (r < 0)
1356 return r;
1357
ed6ee219
PF
1358 if (client->state == DHCP6_STATE_SOLICITATION) {
1359 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1360 if (r < 0)
1361 return r;
1362
1363 if (!rapid_commit)
1364 return 0;
1365 }
1366
431a4bc8
YW
1367 sd_dhcp6_lease_unref(client->lease);
1368 client->lease = TAKE_PTR(lease);
a34b57c0
PF
1369
1370 return DHCP6_STATE_BOUND;
1371}
1372
0ae0e5cd 1373static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
4afd3348 1374 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
631bbe71 1375 uint8_t pref_advertise = 0, pref_lease = 0;
a1140666 1376 int r;
631bbe71
PF
1377
1378 if (advertise->type != DHCP6_ADVERTISE)
ed6ee219 1379 return 0;
631bbe71
PF
1380
1381 r = dhcp6_lease_new(&lease);
1382 if (r < 0)
1383 return r;
1384
1385 r = client_parse_message(client, advertise, len, lease);
1386 if (r < 0)
1387 return r;
1388
1389 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1390 if (r < 0)
1391 return r;
1392
1393 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
bbfa43ca
PF
1394
1395 if (r < 0 || pref_advertise > pref_lease) {
431a4bc8
YW
1396 sd_dhcp6_lease_unref(client->lease);
1397 client->lease = TAKE_PTR(lease);
631bbe71
PF
1398 r = 0;
1399 }
1400
7246333c
PF
1401 if (pref_advertise == 255 || client->retransmit_count > 1)
1402 r = DHCP6_STATE_REQUEST;
1403
631bbe71
PF
1404 return r;
1405}
1406
004845d1
LP
1407static int client_receive_message(
1408 sd_event_source *s,
1409 int fd, uint32_t
1410 revents,
1411 void *userdata) {
1412
631bbe71 1413 sd_dhcp6_client *client = userdata;
3f0c075f 1414 DHCP6_CLIENT_DONT_DESTROY(client);
0d43d2fc 1415 _cleanup_free_ DHCP6Message *message = NULL;
4edc2c9b
LP
1416 ssize_t buflen, len;
1417 int r = 0;
631bbe71
PF
1418
1419 assert(s);
1420 assert(client);
1421 assert(client->event);
1422
4edc2c9b 1423 buflen = next_datagram_size_fd(fd);
22a3fd2d
BG
1424 if (buflen == -ENETDOWN) {
1425 /* the link is down. Don't return an error or the I/O event
1426 source will be disconnected and we won't be able to receive
1427 packets again when the link comes back. */
1428 return 0;
1429 }
4edc2c9b
LP
1430 if (buflen < 0)
1431 return buflen;
631bbe71 1432
0d43d2fc 1433 message = malloc(buflen);
631bbe71
PF
1434 if (!message)
1435 return -ENOMEM;
1436
cf447cb6 1437 len = recv(fd, message, buflen, 0);
0d43d2fc 1438 if (len < 0) {
22a3fd2d
BG
1439 /* see comment above for why we shouldn't error out on ENETDOWN. */
1440 if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
0d43d2fc
TG
1441 return 0;
1442
bd9a7221 1443 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
0d43d2fc 1444
004845d1
LP
1445 }
1446 if ((size_t) len < sizeof(DHCP6Message)) {
1447 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
631bbe71 1448 return 0;
004845d1 1449 }
631bbe71
PF
1450
1451 switch(message->type) {
1452 case DHCP6_SOLICIT:
1453 case DHCP6_REQUEST:
1454 case DHCP6_CONFIRM:
1455 case DHCP6_RENEW:
1456 case DHCP6_REBIND:
1457 case DHCP6_RELEASE:
1458 case DHCP6_DECLINE:
1459 case DHCP6_INFORMATION_REQUEST:
1460 case DHCP6_RELAY_FORW:
1461 case DHCP6_RELAY_REPL:
1462 return 0;
1463
1464 case DHCP6_ADVERTISE:
1465 case DHCP6_REPLY:
1466 case DHCP6_RECONFIGURE:
1467 break;
1468
1469 default:
bd9a7221 1470 log_dhcp6_client(client, "Unknown message type %d", message->type);
631bbe71
PF
1471 return 0;
1472 }
1473
1474 if (client->transaction_id != (message->transaction_id &
1475 htobe32(0x00ffffff)))
1476 return 0;
1477
1478 switch (client->state) {
bbfa43ca
PF
1479 case DHCP6_STATE_INFORMATION_REQUEST:
1480 r = client_receive_reply(client, message, len);
1481 if (r < 0)
1482 return 0;
1483
10c9ce61 1484 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
bbfa43ca
PF
1485
1486 client_start(client, DHCP6_STATE_STOPPED);
1487
1488 break;
1489
631bbe71
PF
1490 case DHCP6_STATE_SOLICITATION:
1491 r = client_receive_advertise(client, message, len);
1492
ed6ee219 1493 if (r == DHCP6_STATE_REQUEST) {
7246333c
PF
1494 client_start(client, r);
1495
ed6ee219
PF
1496 break;
1497 }
631bbe71 1498
d7b34e38 1499 _fallthrough_; /* for Solicitation Rapid Commit option check */
7246333c 1500 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1501 case DHCP6_STATE_RENEW:
1502 case DHCP6_STATE_REBIND:
1503
a34b57c0
PF
1504 r = client_receive_reply(client, message, len);
1505 if (r < 0)
1506 return 0;
1507
1508 if (r == DHCP6_STATE_BOUND) {
1509
1510 r = client_start(client, DHCP6_STATE_BOUND);
1511 if (r < 0) {
1512 client_stop(client, r);
1513 return 0;
1514 }
1515
10c9ce61 1516 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
a34b57c0
PF
1517 }
1518
1519 break;
1520
1521 case DHCP6_STATE_BOUND:
1522
1523 break;
1524
631bbe71 1525 case DHCP6_STATE_STOPPED:
631bbe71
PF
1526 return 0;
1527 }
1528
712fdfd6
YW
1529 log_dhcp6_client(client, "Recv %s",
1530 dhcp6_message_type_to_string(message->type));
631bbe71 1531
a9aff361
PF
1532 return 0;
1533}
1534
134ebaa4
PF
1535static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
1536 uint32_t *lifetime_t2) {
1537 assert_return(client, -EINVAL);
1538 assert_return(client->lease, -EINVAL);
1539
1540 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
1541 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
1542 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
1543
1544 return 0;
1545 }
1546
1547 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
1548 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
1549 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
1550
1551 return 0;
1552 }
1553
1554 return -ENOMSG;
1555}
1556
0ae0e5cd 1557static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
f12abb48 1558 int r;
a34b57c0
PF
1559 usec_t timeout, time_now;
1560 char time_string[FORMAT_TIMESPAN_MAX];
134ebaa4 1561 uint32_t lifetime_t1, lifetime_t2;
f12abb48
PF
1562
1563 assert_return(client, -EINVAL);
1564 assert_return(client->event, -EINVAL);
2f8e7633 1565 assert_return(client->ifindex > 0, -EINVAL);
c3e2adea 1566 assert_return(client->state != state, -EINVAL);
f12abb48 1567
c9393e8c
YW
1568 (void) event_source_disable(client->timeout_resend_expire);
1569 (void) event_source_disable(client->timeout_resend);
c3e2adea
PF
1570 client->retransmit_time = 0;
1571 client->retransmit_count = 0;
f12abb48 1572
38a03f06
LP
1573 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1574 if (r < 0)
1575 return r;
346e13a2 1576
7ac6c26a
PF
1577 if (!client->receive_message) {
1578 r = sd_event_add_io(client->event, &client->receive_message,
1579 client->fd, EPOLLIN, client_receive_message,
1580 client);
1581 if (r < 0)
1582 goto error;
1583
1584 r = sd_event_source_set_priority(client->receive_message,
1585 client->event_priority);
1586 if (r < 0)
1587 goto error;
1588
1589 r = sd_event_source_set_description(client->receive_message,
1590 "dhcp6-receive-message");
1591 if (r < 0)
1592 goto error;
1593 }
1594
c3e2adea
PF
1595 switch (state) {
1596 case DHCP6_STATE_STOPPED:
bbfa43ca
PF
1597 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1598 client->state = DHCP6_STATE_STOPPED;
a9aff361 1599
bbfa43ca
PF
1600 return 0;
1601 }
9021bb9f 1602
4831981d 1603 _fallthrough_;
bbfa43ca 1604 case DHCP6_STATE_SOLICITATION:
c3e2adea
PF
1605 client->state = DHCP6_STATE_SOLICITATION;
1606
7246333c
PF
1607 break;
1608
bbfa43ca 1609 case DHCP6_STATE_INFORMATION_REQUEST:
7246333c 1610 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1611 case DHCP6_STATE_RENEW:
1612 case DHCP6_STATE_REBIND:
a34b57c0 1613
7246333c
PF
1614 client->state = state;
1615
c3e2adea 1616 break;
a34b57c0
PF
1617
1618 case DHCP6_STATE_BOUND:
1619
134ebaa4
PF
1620 r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
1621 if (r < 0)
1622 goto error;
a34b57c0 1623
134ebaa4 1624 if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
bd9a7221 1625 log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
134ebaa4 1626 lifetime_t1, lifetime_t2);
a34b57c0
PF
1627
1628 return 0;
1629 }
1630
134ebaa4 1631 timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
a34b57c0
PF
1632
1633 log_dhcp6_client(client, "T1 expires in %s",
ed19c567 1634 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0 1635
c9393e8c
YW
1636 r = event_reset_time(client->event, &client->timeout_t1,
1637 clock_boottime_or_monotonic(),
1638 time_now + timeout, 10 * USEC_PER_SEC,
1639 client_timeout_t1, client,
1640 client->event_priority, "dhcp6-t1-timeout", true);
9021bb9f 1641 if (r < 0)
7ac6c26a 1642 goto error;
9021bb9f 1643
134ebaa4 1644 timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
a34b57c0
PF
1645
1646 log_dhcp6_client(client, "T2 expires in %s",
ed19c567 1647 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0 1648
c9393e8c
YW
1649 r = event_reset_time(client->event, &client->timeout_t2,
1650 clock_boottime_or_monotonic(),
1651 time_now + timeout, 10 * USEC_PER_SEC,
1652 client_timeout_t2, client,
1653 client->event_priority, "dhcp6-t2-timeout", true);
9021bb9f 1654 if (r < 0)
7ac6c26a 1655 goto error;
9021bb9f 1656
3dc34fcc
PF
1657 client->state = state;
1658
a34b57c0 1659 return 0;
c3e2adea 1660 }
a9aff361 1661
c3e2adea 1662 client->transaction_id = random_u32() & htobe32(0x00ffffff);
346e13a2 1663 client->transaction_start = time_now;
d1b0afe3 1664
c9393e8c
YW
1665 r = event_reset_time(client->event, &client->timeout_resend,
1666 clock_boottime_or_monotonic(),
1667 0, 0,
1668 client_timeout_resend, client,
1669 client->event_priority, "dhcp6-resend-timeout", true);
9021bb9f 1670 if (r < 0)
7ac6c26a 1671 goto error;
9021bb9f 1672
f12abb48 1673 return 0;
7ac6c26a
PF
1674
1675 error:
1676 client_reset(client);
1677 return r;
f12abb48
PF
1678}
1679
0ae0e5cd 1680int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
f667c150
TG
1681 assert_return(client, -EINVAL);
1682
10c9ce61 1683 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
139b011a 1684
7ac6c26a
PF
1685 client->fd = safe_close(client->fd);
1686
139b011a
PF
1687 return 0;
1688}
1689
f667c150
TG
1690int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1691 assert_return(client, -EINVAL);
1692
1693 return client->state != DHCP6_STATE_STOPPED;
1694}
1695
0ae0e5cd 1696int sd_dhcp6_client_start(sd_dhcp6_client *client) {
bbfa43ca 1697 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
2f8e7633 1698 int r = 0;
139b011a
PF
1699
1700 assert_return(client, -EINVAL);
1701 assert_return(client->event, -EINVAL);
2f8e7633 1702 assert_return(client->ifindex > 0, -EINVAL);
c601ebf7 1703 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
139b011a 1704
d7c9c21f 1705 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
63348d13 1706 return -EBUSY;
d7c9c21f 1707
f311a62b
PF
1708 if (!client->information_request && !client->request)
1709 return -EINVAL;
1710
c806ffb9 1711 r = client_reset(client);
f12abb48
PF
1712 if (r < 0)
1713 return r;
1714
bbfa43ca
PF
1715 r = client_ensure_iaid(client);
1716 if (r < 0)
1717 return r;
1718
cc22955c
TH
1719 r = client_ensure_duid(client);
1720 if (r < 0)
1721 return r;
1722
10a0f27b
PF
1723 if (client->fd < 0) {
1724 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1725 if (r < 0) {
1726 _cleanup_free_ char *p = NULL;
1727
1728 (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1729 return log_dhcp6_client_errno(client, r,
1730 "Failed to bind to UDP socket at address %s: %m", strna(p));
1731 }
bd9a7221 1732
10a0f27b 1733 client->fd = r;
bd9a7221 1734 }
bbfa43ca 1735
fcb51238
YW
1736 if (client->information_request) {
1737 usec_t t = now(CLOCK_MONOTONIC);
1738
1739 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1740 return 0;
1741
1742 client->information_request_time_usec = t;
bbfa43ca 1743 state = DHCP6_STATE_INFORMATION_REQUEST;
fcb51238 1744 }
bbfa43ca
PF
1745
1746 log_dhcp6_client(client, "Started in %s mode",
483d099e
ZJS
1747 client->information_request? "Information request":
1748 "Managed");
bbfa43ca
PF
1749
1750 return client_start(client, state);
139b011a
PF
1751}
1752
32d20645 1753int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
139b011a
PF
1754 int r;
1755
1756 assert_return(client, -EINVAL);
1757 assert_return(!client->event, -EBUSY);
1758
1759 if (event)
1760 client->event = sd_event_ref(event);
1761 else {
1762 r = sd_event_default(&client->event);
1763 if (r < 0)
1764 return 0;
1765 }
1766
1767 client->event_priority = priority;
1768
1769 return 0;
1770}
1771
1772int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1773 assert_return(client, -EINVAL);
1774
1775 client->event = sd_event_unref(client->event);
1776
1777 return 0;
1778}
1779
1780sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
a1140666 1781 assert_return(client, NULL);
139b011a
PF
1782
1783 return client->event;
1784}
1785
8301aa0b
YW
1786static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1787 assert(client);
139b011a 1788
c9393e8c
YW
1789 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1790 client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
1791 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1792 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1793
3733eec3
LP
1794 client_reset(client);
1795
10a0f27b
PF
1796 client->fd = safe_close(client->fd);
1797
3733eec3 1798 sd_dhcp6_client_detach_event(client);
3733eec3
LP
1799
1800 free(client->req_opts);
8006aa32 1801 free(client->fqdn);
de8d6e55 1802 free(client->mudurl);
33923925 1803
e7d5fe17 1804 ordered_hashmap_free(client->extra_options);
33923925 1805 strv_free(client->user_class);
73c8ced7 1806 strv_free(client->vendor_class);
33923925 1807
6b430fdb 1808 return mfree(client);
139b011a
PF
1809}
1810
8301aa0b
YW
1811DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1812
0ae0e5cd 1813int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
4afd3348 1814 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
8b8ecac8 1815 _cleanup_free_ be16_t *req_opts = NULL;
da6fe470 1816 size_t t;
139b011a
PF
1817
1818 assert_return(ret, -EINVAL);
1819
8b8ecac8
YW
1820 req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
1821 if (!req_opts)
139b011a
PF
1822 return -ENOMEM;
1823
8b8ecac8
YW
1824 for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
1825 req_opts[t] = htobe16(default_req_opts[t]);
c806ffb9 1826
8b8ecac8
YW
1827 client = new(sd_dhcp6_client, 1);
1828 if (!client)
da6fe470
PF
1829 return -ENOMEM;
1830
8b8ecac8
YW
1831 *client = (sd_dhcp6_client) {
1832 .n_ref = 1,
1833 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1834 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1835 .ifindex = -1,
1836 .request = DHCP6_REQUEST_IA_NA,
1837 .fd = -1,
1838 .req_opts_len = ELEMENTSOF(default_req_opts),
2805536b
SS
1839 .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
1840 .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1,
8b8ecac8
YW
1841 .req_opts = TAKE_PTR(req_opts),
1842 };
da6fe470 1843
1cc6c93a 1844 *ret = TAKE_PTR(client);
139b011a
PF
1845
1846 return 0;
1847}