]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
Merge pull request #17185 from yuwata/ethtool-update
[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
1d6cc5d0 273 if (duid) {
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
PF
626 size_t len, optlen = 512;
627 uint8_t *opt;
628 int r;
346e13a2
PF
629 usec_t elapsed_usec;
630 be16_t elapsed_time;
a9aff361 631
a1140666
LP
632 assert(client);
633
a9aff361
PF
634 len = sizeof(DHCP6Message) + optlen;
635
636 message = malloc0(len);
637 if (!message)
638 return -ENOMEM;
639
640 opt = (uint8_t *)(message + 1);
641
642 message->transaction_id = client->transaction_id;
643
644 switch(client->state) {
bbfa43ca
PF
645 case DHCP6_STATE_INFORMATION_REQUEST:
646 message->type = DHCP6_INFORMATION_REQUEST;
647
de8d6e55
SS
648 if (client->mudurl) {
649 r = dhcp6_option_append(&opt, &optlen,
650 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
651 client->mudurl);
652 if (r < 0)
653 return r;
654 }
655
bbfa43ca
PF
656 break;
657
a9aff361
PF
658 case DHCP6_STATE_SOLICITATION:
659 message->type = DHCP6_SOLICIT;
660
ed6ee219 661 r = dhcp6_option_append(&opt, &optlen,
2c1ab8ca 662 SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
926695f1
TA
663 if (r < 0)
664 return r;
ed6ee219 665
f311a62b
PF
666 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
667 r = dhcp6_option_append_ia(&opt, &optlen,
668 &client->ia_na);
669 if (r < 0)
670 return r;
671 }
a9aff361 672
8006aa32
SA
673 if (client->fqdn) {
674 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
675 if (r < 0)
676 return r;
677 }
678
de8d6e55
SS
679 if (client->mudurl) {
680 r = dhcp6_option_append(&opt, &optlen,
681 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
682 client->mudurl);
683 if (r < 0)
684 return r;
685 }
686
33923925
SS
687 if (client->user_class) {
688 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
689 if (r < 0)
690 return r;
691 }
692
73c8ced7
SS
693 if (client->vendor_class) {
694 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
695 if (r < 0)
696 return r;
697 }
698
99ccb8ff
SS
699 if (!ordered_hashmap_isempty(client->vendor_options)) {
700 r = dhcp6_option_append_vendor_option(&opt, &optlen,
701 client->vendor_options);
702 if (r < 0)
703 return r;
704 }
705
f311a62b 706 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
2805536b 707 r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
7c3de8f8
PF
708 if (r < 0)
709 return r;
710
711 opt += r;
712 optlen -= r;
713 }
714
7246333c
PF
715 break;
716
717 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
718 case DHCP6_STATE_RENEW:
719
720 if (client->state == DHCP6_STATE_REQUEST)
721 message->type = DHCP6_REQUEST;
722 else
723 message->type = DHCP6_RENEW;
7246333c 724
2c1ab8ca 725 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
7246333c
PF
726 client->lease->serverid_len,
727 client->lease->serverid);
728 if (r < 0)
729 return r;
730
26a63b81 731 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
f311a62b
PF
732 r = dhcp6_option_append_ia(&opt, &optlen,
733 &client->lease->ia);
734 if (r < 0)
735 return r;
736 }
a9aff361 737
8006aa32
SA
738 if (client->fqdn) {
739 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
740 if (r < 0)
741 return r;
742 }
743
de8d6e55
SS
744 if (client->mudurl) {
745 r = dhcp6_option_append(&opt, &optlen,
746 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
747 client->mudurl);
748 if (r < 0)
749 return r;
750 }
751
33923925
SS
752 if (client->user_class) {
753 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
754 if (r < 0)
755 return r;
756 }
757
73c8ced7
SS
758 if (client->vendor_class) {
759 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
760 if (r < 0)
761 return r;
762 }
763
99ccb8ff
SS
764 if (!ordered_hashmap_isempty(client->vendor_options)) {
765 r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
766 if (r < 0)
767 return r;
768 }
769
26a63b81 770 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
2805536b 771 r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
7c3de8f8
PF
772 if (r < 0)
773 return r;
774
775 opt += r;
776 optlen -= r;
777 }
778
a9aff361
PF
779 break;
780
3dc34fcc
PF
781 case DHCP6_STATE_REBIND:
782 message->type = DHCP6_REBIND;
783
f311a62b
PF
784 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
785 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
786 if (r < 0)
787 return r;
788 }
3dc34fcc 789
8006aa32
SA
790 if (client->fqdn) {
791 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
792 if (r < 0)
793 return r;
794 }
795
de8d6e55
SS
796 if (client->mudurl) {
797 r = dhcp6_option_append(&opt, &optlen,
798 SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
799 client->mudurl);
800 if (r < 0)
801 return r;
802 }
803
33923925
SS
804 if (client->user_class) {
805 r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
806 if (r < 0)
807 return r;
808 }
809
73c8ced7
SS
810 if (client->vendor_class) {
811 r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
812 if (r < 0)
813 return r;
814 }
815
99ccb8ff
SS
816 if (!ordered_hashmap_isempty(client->vendor_options)) {
817 r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
818 if (r < 0)
819 return r;
820 }
821
f311a62b 822 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
2805536b 823 r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
7c3de8f8
PF
824 if (r < 0)
825 return r;
826
827 opt += r;
828 optlen -= r;
829 }
830
3dc34fcc
PF
831 break;
832
a9aff361 833 case DHCP6_STATE_STOPPED:
a34b57c0 834 case DHCP6_STATE_BOUND:
a9aff361
PF
835 return -EINVAL;
836 }
837
2c1ab8ca 838 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
da6fe470
PF
839 client->req_opts_len * sizeof(be16_t),
840 client->req_opts);
841 if (r < 0)
842 return r;
843
ccd1fc2f 844 assert(client->duid_len);
2c1ab8ca 845 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
66eac120 846 client->duid_len, &client->duid);
7246333c
PF
847 if (r < 0)
848 return r;
849
346e13a2
PF
850 elapsed_usec = time_now - client->transaction_start;
851 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
852 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
853 else
854 elapsed_time = 0xffff;
855
2c1ab8ca 856 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
346e13a2
PF
857 sizeof(elapsed_time), &elapsed_time);
858 if (r < 0)
859 return r;
860
90e74a66 861 ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
e7d5fe17
AD
862 r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
863 if (r < 0)
864 return r;
865 }
866
a9aff361
PF
867 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
868 len - optlen);
869 if (r < 0)
870 return r;
871
872 log_dhcp6_client(client, "Sent %s",
873 dhcp6_message_type_to_string(message->type));
874
875 return 0;
876}
877
4b558378 878static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
879 sd_dhcp6_client *client = userdata;
880
a1140666
LP
881 assert(s);
882 assert(client);
883 assert(client->lease);
a34b57c0 884
c9393e8c 885 (void) event_source_disable(client->timeout_t2);
a34b57c0
PF
886
887 log_dhcp6_client(client, "Timeout T2");
888
3dc34fcc
PF
889 client_start(client, DHCP6_STATE_REBIND);
890
a34b57c0
PF
891 return 0;
892}
893
4b558378 894static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
895 sd_dhcp6_client *client = userdata;
896
a1140666
LP
897 assert(s);
898 assert(client);
899 assert(client->lease);
a34b57c0 900
c9393e8c 901 (void) event_source_disable(client->timeout_t1);
a34b57c0
PF
902
903 log_dhcp6_client(client, "Timeout T1");
904
3dc34fcc
PF
905 client_start(client, DHCP6_STATE_RENEW);
906
a34b57c0
PF
907 return 0;
908}
909
4b558378 910static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3 911 sd_dhcp6_client *client = userdata;
3dc34fcc
PF
912 DHCP6_CLIENT_DONT_DESTROY(client);
913 enum DHCP6State state;
d1b0afe3
PF
914
915 assert(s);
916 assert(client);
917 assert(client->event);
918
3dc34fcc
PF
919 state = client->state;
920
10c9ce61 921 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
d1b0afe3 922
3dc34fcc
PF
923 /* RFC 3315, section 18.1.4., says that "...the client may choose to
924 use a Solicit message to locate a new DHCP server..." */
925 if (state == DHCP6_STATE_REBIND)
926 client_start(client, DHCP6_STATE_SOLICITATION);
927
d1b0afe3
PF
928 return 0;
929}
930
931static usec_t client_timeout_compute_random(usec_t val) {
9de8a425 932 return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC;
d1b0afe3
PF
933}
934
4b558378 935static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3
PF
936 int r = 0;
937 sd_dhcp6_client *client = userdata;
4b4923e6 938 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
5e913450 939 usec_t max_retransmit_duration = 0;
513a6fa8 940 uint8_t max_retransmit_count = 0;
d1b0afe3
PF
941 char time_string[FORMAT_TIMESPAN_MAX];
942
943 assert(s);
944 assert(client);
945 assert(client->event);
946
c9393e8c 947 (void) event_source_disable(client->timeout_resend);
d1b0afe3
PF
948
949 switch (client->state) {
bbfa43ca
PF
950 case DHCP6_STATE_INFORMATION_REQUEST:
951 init_retransmit_time = DHCP6_INF_TIMEOUT;
952 max_retransmit_time = DHCP6_INF_MAX_RT;
953
954 break;
955
d1b0afe3 956 case DHCP6_STATE_SOLICITATION:
7246333c
PF
957
958 if (client->retransmit_count && client->lease) {
959 client_start(client, DHCP6_STATE_REQUEST);
960 return 0;
961 }
962
d1b0afe3
PF
963 init_retransmit_time = DHCP6_SOL_TIMEOUT;
964 max_retransmit_time = DHCP6_SOL_MAX_RT;
d1b0afe3
PF
965
966 break;
967
7246333c
PF
968 case DHCP6_STATE_REQUEST:
969 init_retransmit_time = DHCP6_REQ_TIMEOUT;
970 max_retransmit_time = DHCP6_REQ_MAX_RT;
971 max_retransmit_count = DHCP6_REQ_MAX_RC;
7246333c
PF
972
973 break;
974
3dc34fcc
PF
975 case DHCP6_STATE_RENEW:
976 init_retransmit_time = DHCP6_REN_TIMEOUT;
977 max_retransmit_time = DHCP6_REN_MAX_RT;
3dc34fcc
PF
978
979 /* RFC 3315, section 18.1.3. says max retransmit duration will
980 be the remaining time until T2. Instead of setting MRD,
981 wait for T2 to trigger with the same end result */
3dc34fcc
PF
982
983 break;
984
985 case DHCP6_STATE_REBIND:
986 init_retransmit_time = DHCP6_REB_TIMEOUT;
987 max_retransmit_time = DHCP6_REB_MAX_RT;
3dc34fcc 988
c9393e8c 989 if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
3ae01632
YW
990 uint32_t expire = 0;
991
992 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire);
3dc34fcc
PF
993 if (r < 0) {
994 client_stop(client, r);
995 return 0;
996 }
997 max_retransmit_duration = expire * USEC_PER_SEC;
998 }
999
1000 break;
1001
d1b0afe3 1002 case DHCP6_STATE_STOPPED:
a34b57c0 1003 case DHCP6_STATE_BOUND:
d1b0afe3
PF
1004 return 0;
1005 }
1006
3ae01632 1007 if (max_retransmit_count > 0 &&
d1b0afe3 1008 client->retransmit_count >= max_retransmit_count) {
10c9ce61 1009 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
d1b0afe3
PF
1010 return 0;
1011 }
1012
fa94c34b 1013 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d1b0afe3
PF
1014 if (r < 0)
1015 goto error;
1016
346e13a2
PF
1017 r = client_send_message(client, time_now);
1018 if (r >= 0)
1019 client->retransmit_count++;
1020
3ae01632 1021 if (client->retransmit_time == 0) {
d1b0afe3
PF
1022 client->retransmit_time =
1023 client_timeout_compute_random(init_retransmit_time);
a9aff361
PF
1024
1025 if (client->state == DHCP6_STATE_SOLICITATION)
1026 client->retransmit_time += init_retransmit_time / 10;
1027
d1b0afe3 1028 } else {
3ae01632 1029 if (max_retransmit_time > 0 &&
d1b0afe3
PF
1030 client->retransmit_time > max_retransmit_time / 2)
1031 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
1032 else
1033 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
1034 }
1035
1036 log_dhcp6_client(client, "Next retransmission in %s",
ed19c567 1037 format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
d1b0afe3 1038
c9393e8c
YW
1039 r = event_reset_time(client->event, &client->timeout_resend,
1040 clock_boottime_or_monotonic(),
1041 time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
1042 client_timeout_resend, client,
1043 client->event_priority, "dhcp6-resend-timer", true);
d1b0afe3
PF
1044 if (r < 0)
1045 goto error;
1046
3ae01632 1047 if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
d1b0afe3
PF
1048
1049 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
1050 max_retransmit_duration / USEC_PER_SEC);
1051
c9393e8c
YW
1052 r = event_reset_time(client->event, &client->timeout_resend_expire,
1053 clock_boottime_or_monotonic(),
1054 time_now + max_retransmit_duration, USEC_PER_SEC,
1055 client_timeout_resend_expire, client,
1056 client->event_priority, "dhcp6-resend-expire-timer", true);
9021bb9f
TG
1057 if (r < 0)
1058 goto error;
d1b0afe3
PF
1059 }
1060
1061error:
1062 if (r < 0)
1063 client_stop(client, r);
1064
1065 return 0;
1066}
1067
f12abb48 1068static int client_ensure_iaid(sd_dhcp6_client *client) {
cfb5b380 1069 int r;
6d13616b 1070 uint32_t iaid;
f12abb48
PF
1071
1072 assert(client);
1073
8217ed5e 1074 if (client->iaid_set)
f12abb48
PF
1075 return 0;
1076
6d13616b 1077 r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
cfb5b380
TG
1078 if (r < 0)
1079 return r;
f12abb48 1080
69b43997
PF
1081 client->ia_na.ia_na.id = iaid;
1082 client->ia_pd.ia_pd.id = iaid;
8217ed5e 1083 client->iaid_set = true;
69b43997 1084
f12abb48
PF
1085 return 0;
1086}
1087
4b558378
ZJS
1088static int client_parse_message(
1089 sd_dhcp6_client *client,
1090 DHCP6Message *message,
1091 size_t len,
1092 sd_dhcp6_lease *lease) {
da07cf35 1093
5c95a913 1094 uint16_t ia_na_status = 0, ia_pd_status = 0;
da07cf35 1095 uint32_t lt_t1 = ~0, lt_t2 = ~0;
5c95a913 1096 usec_t irt = IRT_DEFAULT;
da07cf35 1097 bool clientid = false;
3bc424a3 1098 size_t pos = 0;
631bbe71 1099 int r;
631bbe71 1100
a1140666
LP
1101 assert(client);
1102 assert(message);
1103 assert(len >= sizeof(DHCP6Message));
1104 assert(lease);
1105
44481a8b
ZJS
1106 len -= sizeof(DHCP6Message);
1107
3bc424a3 1108 while (pos < len) {
da07cf35 1109 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
c6b4f32a 1110 uint16_t optcode, optlen;
3bc424a3 1111 be32_t iaid_lease;
5c95a913 1112 int status;
da07cf35 1113 uint8_t *optval;
3bc424a3 1114
da07cf35 1115 if (len < pos + offsetof(DHCP6Option, data))
3bc424a3
PF
1116 return -ENOBUFS;
1117
1118 optcode = be16toh(option->code);
1119 optlen = be16toh(option->len);
1120 optval = option->data;
1121
da07cf35
YW
1122 if (len < pos + offsetof(DHCP6Option, data) + optlen)
1123 return -ENOBUFS;
1124
631bbe71 1125 switch (optcode) {
2c1ab8ca 1126 case SD_DHCP6_OPTION_CLIENTID:
631bbe71
PF
1127 if (clientid) {
1128 log_dhcp6_client(client, "%s contains multiple clientids",
1129 dhcp6_message_type_to_string(message->type));
1130 return -EINVAL;
1131 }
1132
66eac120 1133 if (optlen != client->duid_len ||
631bbe71
PF
1134 memcmp(&client->duid, optval, optlen) != 0) {
1135 log_dhcp6_client(client, "%s DUID does not match",
1136 dhcp6_message_type_to_string(message->type));
1137
1138 return -EINVAL;
1139 }
1140 clientid = true;
1141
1142 break;
1143
2c1ab8ca 1144 case SD_DHCP6_OPTION_SERVERID:
99f1d3fc
ZJS
1145 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1146 if (r >= 0) {
631bbe71
PF
1147 log_dhcp6_client(client, "%s contains multiple serverids",
1148 dhcp6_message_type_to_string(message->type));
1149 return -EINVAL;
1150 }
1151
1152 r = dhcp6_lease_set_serverid(lease, optval, optlen);
1153 if (r < 0)
1154 return r;
1155
1156 break;
1157
2c1ab8ca 1158 case SD_DHCP6_OPTION_PREFERENCE:
631bbe71
PF
1159 if (optlen != 1)
1160 return -EINVAL;
1161
3bc424a3 1162 r = dhcp6_lease_set_preference(lease, optval[0]);
631bbe71
PF
1163 if (r < 0)
1164 return r;
1165
1166 break;
1167
2c1ab8ca 1168 case SD_DHCP6_OPTION_STATUS_CODE:
91c43f39
YW
1169 status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
1170 if (status < 0)
1171 return status;
1172
1173 if (status > 0) {
631bbe71
PF
1174 log_dhcp6_client(client, "%s Status %s",
1175 dhcp6_message_type_to_string(message->type),
1176 dhcp6_message_status_to_string(status));
c6b4f32a 1177
631bbe71
PF
1178 return -EINVAL;
1179 }
1180
1181 break;
1182
2c1ab8ca 1183 case SD_DHCP6_OPTION_IA_NA:
bbfa43ca
PF
1184 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1185 log_dhcp6_client(client, "Information request ignoring IA NA option");
1186
1187 break;
1188 }
1189
5c95a913 1190 r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
631bbe71
PF
1191 if (r < 0 && r != -ENOMSG)
1192 return r;
1193
5c95a913
SS
1194 if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
1195 pos += offsetof(DHCP6Option, data) + optlen;
1196 continue;
1197 }
1198
631bbe71
PF
1199 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
1200 if (r < 0)
1201 return r;
1202
e0026dcb 1203 if (client->ia_na.ia_na.id != iaid_lease) {
dce6563f
PF
1204 log_dhcp6_client(client, "%s has wrong IAID for IA NA",
1205 dhcp6_message_type_to_string(message->type));
1206 return -EINVAL;
1207 }
1208
b47fb949
PF
1209 if (lease->ia.addresses) {
1210 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
1211 lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
1212 }
1213
dce6563f
PF
1214 break;
1215
1216 case SD_DHCP6_OPTION_IA_PD:
1217 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1218 log_dhcp6_client(client, "Information request ignoring IA PD option");
1219
1220 break;
1221 }
1222
5c95a913 1223 r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
dce6563f
PF
1224 if (r < 0 && r != -ENOMSG)
1225 return r;
1226
5c95a913
SS
1227 if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
1228 pos += offsetof(DHCP6Option, data) + optlen;
1229 continue;
1230 }
1231
49228c75 1232 r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
dce6563f
PF
1233 if (r < 0)
1234 return r;
1235
1236 if (client->ia_pd.ia_pd.id != iaid_lease) {
1237 log_dhcp6_client(client, "%s has wrong IAID for IA PD",
631bbe71
PF
1238 dhcp6_message_type_to_string(message->type));
1239 return -EINVAL;
1240 }
1241
b47fb949
PF
1242 if (lease->pd.addresses) {
1243 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
1244 lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
1245 }
1246
631bbe71 1247 break;
ed6ee219 1248
2c1ab8ca 1249 case SD_DHCP6_OPTION_RAPID_COMMIT:
ed6ee219
PF
1250 r = dhcp6_lease_set_rapid_commit(lease);
1251 if (r < 0)
1252 return r;
1253
1254 break;
7bd8e95d 1255
2c1ab8ca 1256 case SD_DHCP6_OPTION_DNS_SERVERS:
7bd8e95d
PF
1257 r = dhcp6_lease_set_dns(lease, optval, optlen);
1258 if (r < 0)
1259 return r;
1260
1261 break;
5da1b97f 1262
2c1ab8ca 1263 case SD_DHCP6_OPTION_DOMAIN_LIST:
5da1b97f
PF
1264 r = dhcp6_lease_set_domains(lease, optval, optlen);
1265 if (r < 0)
1266 return r;
1267
1268 break;
1269
2c1ab8ca 1270 case SD_DHCP6_OPTION_NTP_SERVER:
6599680e
PF
1271 r = dhcp6_lease_set_ntp(lease, optval, optlen);
1272 if (r < 0)
1273 return r;
1274
1275 break;
41e4615d 1276
2c1ab8ca 1277 case SD_DHCP6_OPTION_SNTP_SERVERS:
41e4615d
PF
1278 r = dhcp6_lease_set_sntp(lease, optval, optlen);
1279 if (r < 0)
1280 return r;
1281
1282 break;
fcb51238 1283
c43eea9f
BG
1284 case SD_DHCP6_OPTION_FQDN:
1285 r = dhcp6_lease_set_fqdn(lease, optval, optlen);
1286 if (r < 0)
1287 return r;
1288
1289 break;
1290
fcb51238 1291 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
6ffe71d0
YW
1292 if (optlen != 4)
1293 return -EINVAL;
1294
0eb5e6d3 1295 irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
fcb51238 1296 break;
631bbe71 1297 }
6599680e 1298
23976cb9 1299 pos += offsetof(DHCP6Option, data) + optlen;
631bbe71
PF
1300 }
1301
5c95a913
SS
1302 if (ia_na_status > 0 && ia_pd_status > 0) {
1303 log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
1304 return -EINVAL;
1305 }
1306
1eeddba4 1307 if (!clientid) {
631bbe71
PF
1308 log_dhcp6_client(client, "%s has incomplete options",
1309 dhcp6_message_type_to_string(message->type));
1310 return -EINVAL;
1311 }
1312
bbfa43ca 1313 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
99f1d3fc
ZJS
1314 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1315 if (r < 0) {
bbfa43ca
PF
1316 log_dhcp6_client(client, "%s has no server id",
1317 dhcp6_message_type_to_string(message->type));
99f1d3fc
ZJS
1318 return -EINVAL;
1319 }
b47fb949 1320
99f1d3fc
ZJS
1321 } else {
1322 if (lease->ia.addresses) {
1323 lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
1324 lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
1325 }
b47fb949 1326
99f1d3fc
ZJS
1327 if (lease->pd.addresses) {
1328 lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
1329 lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
1330 }
bbfa43ca 1331 }
631bbe71 1332
fcb51238
YW
1333 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
1334
21a9905c 1335 return 0;
631bbe71
PF
1336}
1337
0ae0e5cd 1338static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
4afd3348 1339 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
ed6ee219 1340 bool rapid_commit;
a1140666
LP
1341 int r;
1342
1343 assert(client);
1344 assert(reply);
a34b57c0
PF
1345
1346 if (reply->type != DHCP6_REPLY)
ed6ee219 1347 return 0;
a34b57c0
PF
1348
1349 r = dhcp6_lease_new(&lease);
1350 if (r < 0)
1351 return -ENOMEM;
1352
1353 r = client_parse_message(client, reply, len, lease);
1354 if (r < 0)
1355 return r;
1356
ed6ee219
PF
1357 if (client->state == DHCP6_STATE_SOLICITATION) {
1358 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1359 if (r < 0)
1360 return r;
1361
1362 if (!rapid_commit)
1363 return 0;
1364 }
1365
431a4bc8
YW
1366 sd_dhcp6_lease_unref(client->lease);
1367 client->lease = TAKE_PTR(lease);
a34b57c0
PF
1368
1369 return DHCP6_STATE_BOUND;
1370}
1371
0ae0e5cd 1372static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
4afd3348 1373 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
631bbe71 1374 uint8_t pref_advertise = 0, pref_lease = 0;
a1140666 1375 int r;
631bbe71
PF
1376
1377 if (advertise->type != DHCP6_ADVERTISE)
ed6ee219 1378 return 0;
631bbe71
PF
1379
1380 r = dhcp6_lease_new(&lease);
1381 if (r < 0)
1382 return r;
1383
1384 r = client_parse_message(client, advertise, len, lease);
1385 if (r < 0)
1386 return r;
1387
1388 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1389 if (r < 0)
1390 return r;
1391
1392 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
bbfa43ca
PF
1393
1394 if (r < 0 || pref_advertise > pref_lease) {
431a4bc8
YW
1395 sd_dhcp6_lease_unref(client->lease);
1396 client->lease = TAKE_PTR(lease);
631bbe71
PF
1397 r = 0;
1398 }
1399
7246333c
PF
1400 if (pref_advertise == 255 || client->retransmit_count > 1)
1401 r = DHCP6_STATE_REQUEST;
1402
631bbe71
PF
1403 return r;
1404}
1405
004845d1
LP
1406static int client_receive_message(
1407 sd_event_source *s,
1408 int fd, uint32_t
1409 revents,
1410 void *userdata) {
1411
631bbe71 1412 sd_dhcp6_client *client = userdata;
3f0c075f 1413 DHCP6_CLIENT_DONT_DESTROY(client);
0d43d2fc 1414 _cleanup_free_ DHCP6Message *message = NULL;
4edc2c9b
LP
1415 ssize_t buflen, len;
1416 int r = 0;
631bbe71
PF
1417
1418 assert(s);
1419 assert(client);
1420 assert(client->event);
1421
4edc2c9b 1422 buflen = next_datagram_size_fd(fd);
d46b79bb 1423 if (buflen == -ENETDOWN)
22a3fd2d
BG
1424 /* the link is down. Don't return an error or the I/O event
1425 source will be disconnected and we won't be able to receive
1426 packets again when the link comes back. */
1427 return 0;
4edc2c9b
LP
1428 if (buflen < 0)
1429 return buflen;
631bbe71 1430
0d43d2fc 1431 message = malloc(buflen);
631bbe71
PF
1432 if (!message)
1433 return -ENOMEM;
1434
cf447cb6 1435 len = recv(fd, message, buflen, 0);
0d43d2fc 1436 if (len < 0) {
22a3fd2d
BG
1437 /* see comment above for why we shouldn't error out on ENETDOWN. */
1438 if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
0d43d2fc
TG
1439 return 0;
1440
bd9a7221 1441 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
0d43d2fc 1442
004845d1
LP
1443 }
1444 if ((size_t) len < sizeof(DHCP6Message)) {
1445 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
631bbe71 1446 return 0;
004845d1 1447 }
631bbe71
PF
1448
1449 switch(message->type) {
1450 case DHCP6_SOLICIT:
1451 case DHCP6_REQUEST:
1452 case DHCP6_CONFIRM:
1453 case DHCP6_RENEW:
1454 case DHCP6_REBIND:
1455 case DHCP6_RELEASE:
1456 case DHCP6_DECLINE:
1457 case DHCP6_INFORMATION_REQUEST:
1458 case DHCP6_RELAY_FORW:
1459 case DHCP6_RELAY_REPL:
1460 return 0;
1461
1462 case DHCP6_ADVERTISE:
1463 case DHCP6_REPLY:
1464 case DHCP6_RECONFIGURE:
1465 break;
1466
1467 default:
bd9a7221 1468 log_dhcp6_client(client, "Unknown message type %d", message->type);
631bbe71
PF
1469 return 0;
1470 }
1471
1472 if (client->transaction_id != (message->transaction_id &
1473 htobe32(0x00ffffff)))
1474 return 0;
1475
1476 switch (client->state) {
bbfa43ca
PF
1477 case DHCP6_STATE_INFORMATION_REQUEST:
1478 r = client_receive_reply(client, message, len);
1479 if (r < 0)
1480 return 0;
1481
10c9ce61 1482 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
bbfa43ca
PF
1483
1484 client_start(client, DHCP6_STATE_STOPPED);
1485
1486 break;
1487
631bbe71
PF
1488 case DHCP6_STATE_SOLICITATION:
1489 r = client_receive_advertise(client, message, len);
1490
ed6ee219 1491 if (r == DHCP6_STATE_REQUEST) {
7246333c
PF
1492 client_start(client, r);
1493
ed6ee219
PF
1494 break;
1495 }
631bbe71 1496
d7b34e38 1497 _fallthrough_; /* for Solicitation Rapid Commit option check */
7246333c 1498 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1499 case DHCP6_STATE_RENEW:
1500 case DHCP6_STATE_REBIND:
1501
a34b57c0
PF
1502 r = client_receive_reply(client, message, len);
1503 if (r < 0)
1504 return 0;
1505
1506 if (r == DHCP6_STATE_BOUND) {
1507
1508 r = client_start(client, DHCP6_STATE_BOUND);
1509 if (r < 0) {
1510 client_stop(client, r);
1511 return 0;
1512 }
1513
10c9ce61 1514 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
a34b57c0
PF
1515 }
1516
1517 break;
1518
1519 case DHCP6_STATE_BOUND:
1520
1521 break;
1522
631bbe71 1523 case DHCP6_STATE_STOPPED:
631bbe71
PF
1524 return 0;
1525 }
1526
712fdfd6
YW
1527 log_dhcp6_client(client, "Recv %s",
1528 dhcp6_message_type_to_string(message->type));
631bbe71 1529
a9aff361
PF
1530 return 0;
1531}
1532
134ebaa4
PF
1533static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
1534 uint32_t *lifetime_t2) {
1535 assert_return(client, -EINVAL);
1536 assert_return(client->lease, -EINVAL);
1537
1538 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
1539 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
1540 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
1541
1542 return 0;
1543 }
1544
1545 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
1546 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
1547 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
1548
1549 return 0;
1550 }
1551
1552 return -ENOMSG;
1553}
1554
0ae0e5cd 1555static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
f12abb48 1556 int r;
a34b57c0
PF
1557 usec_t timeout, time_now;
1558 char time_string[FORMAT_TIMESPAN_MAX];
134ebaa4 1559 uint32_t lifetime_t1, lifetime_t2;
f12abb48
PF
1560
1561 assert_return(client, -EINVAL);
1562 assert_return(client->event, -EINVAL);
2f8e7633 1563 assert_return(client->ifindex > 0, -EINVAL);
c3e2adea 1564 assert_return(client->state != state, -EINVAL);
f12abb48 1565
c9393e8c
YW
1566 (void) event_source_disable(client->timeout_resend_expire);
1567 (void) event_source_disable(client->timeout_resend);
c3e2adea
PF
1568 client->retransmit_time = 0;
1569 client->retransmit_count = 0;
f12abb48 1570
38a03f06
LP
1571 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1572 if (r < 0)
1573 return r;
346e13a2 1574
7ac6c26a
PF
1575 if (!client->receive_message) {
1576 r = sd_event_add_io(client->event, &client->receive_message,
1577 client->fd, EPOLLIN, client_receive_message,
1578 client);
1579 if (r < 0)
1580 goto error;
1581
1582 r = sd_event_source_set_priority(client->receive_message,
1583 client->event_priority);
1584 if (r < 0)
1585 goto error;
1586
1587 r = sd_event_source_set_description(client->receive_message,
1588 "dhcp6-receive-message");
1589 if (r < 0)
1590 goto error;
1591 }
1592
c3e2adea
PF
1593 switch (state) {
1594 case DHCP6_STATE_STOPPED:
bbfa43ca
PF
1595 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1596 client->state = DHCP6_STATE_STOPPED;
a9aff361 1597
bbfa43ca
PF
1598 return 0;
1599 }
9021bb9f 1600
4831981d 1601 _fallthrough_;
bbfa43ca 1602 case DHCP6_STATE_SOLICITATION:
c3e2adea
PF
1603 client->state = DHCP6_STATE_SOLICITATION;
1604
7246333c
PF
1605 break;
1606
bbfa43ca 1607 case DHCP6_STATE_INFORMATION_REQUEST:
7246333c 1608 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1609 case DHCP6_STATE_RENEW:
1610 case DHCP6_STATE_REBIND:
a34b57c0 1611
7246333c
PF
1612 client->state = state;
1613
c3e2adea 1614 break;
a34b57c0
PF
1615
1616 case DHCP6_STATE_BOUND:
1617
134ebaa4
PF
1618 r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
1619 if (r < 0)
1620 goto error;
a34b57c0 1621
134ebaa4 1622 if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
bd9a7221 1623 log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
134ebaa4 1624 lifetime_t1, lifetime_t2);
a34b57c0
PF
1625
1626 return 0;
1627 }
1628
134ebaa4 1629 timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
a34b57c0
PF
1630
1631 log_dhcp6_client(client, "T1 expires in %s",
ed19c567 1632 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0 1633
c9393e8c
YW
1634 r = event_reset_time(client->event, &client->timeout_t1,
1635 clock_boottime_or_monotonic(),
1636 time_now + timeout, 10 * USEC_PER_SEC,
1637 client_timeout_t1, client,
1638 client->event_priority, "dhcp6-t1-timeout", true);
9021bb9f 1639 if (r < 0)
7ac6c26a 1640 goto error;
9021bb9f 1641
134ebaa4 1642 timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
a34b57c0
PF
1643
1644 log_dhcp6_client(client, "T2 expires in %s",
ed19c567 1645 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0 1646
c9393e8c
YW
1647 r = event_reset_time(client->event, &client->timeout_t2,
1648 clock_boottime_or_monotonic(),
1649 time_now + timeout, 10 * USEC_PER_SEC,
1650 client_timeout_t2, client,
1651 client->event_priority, "dhcp6-t2-timeout", true);
9021bb9f 1652 if (r < 0)
7ac6c26a 1653 goto error;
9021bb9f 1654
3dc34fcc
PF
1655 client->state = state;
1656
a34b57c0 1657 return 0;
c3e2adea 1658 }
a9aff361 1659
c3e2adea 1660 client->transaction_id = random_u32() & htobe32(0x00ffffff);
346e13a2 1661 client->transaction_start = time_now;
d1b0afe3 1662
c9393e8c
YW
1663 r = event_reset_time(client->event, &client->timeout_resend,
1664 clock_boottime_or_monotonic(),
1665 0, 0,
1666 client_timeout_resend, client,
1667 client->event_priority, "dhcp6-resend-timeout", true);
9021bb9f 1668 if (r < 0)
7ac6c26a 1669 goto error;
9021bb9f 1670
f12abb48 1671 return 0;
7ac6c26a
PF
1672
1673 error:
1674 client_reset(client);
1675 return r;
f12abb48
PF
1676}
1677
0ae0e5cd 1678int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
c8bae363
YW
1679 if (!client)
1680 return 0;
f667c150 1681
10c9ce61 1682 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
139b011a 1683
7ac6c26a
PF
1684 client->fd = safe_close(client->fd);
1685
139b011a
PF
1686 return 0;
1687}
1688
f667c150
TG
1689int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1690 assert_return(client, -EINVAL);
1691
1692 return client->state != DHCP6_STATE_STOPPED;
1693}
1694
0ae0e5cd 1695int sd_dhcp6_client_start(sd_dhcp6_client *client) {
bbfa43ca 1696 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
6d95e7d9 1697 int r;
139b011a
PF
1698
1699 assert_return(client, -EINVAL);
1700 assert_return(client->event, -EINVAL);
2f8e7633 1701 assert_return(client->ifindex > 0, -EINVAL);
c601ebf7 1702 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
139b011a 1703
d7c9c21f 1704 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
63348d13 1705 return -EBUSY;
d7c9c21f 1706
f311a62b
PF
1707 if (!client->information_request && !client->request)
1708 return -EINVAL;
1709
c806ffb9 1710 r = client_reset(client);
f12abb48
PF
1711 if (r < 0)
1712 return r;
1713
bbfa43ca
PF
1714 r = client_ensure_iaid(client);
1715 if (r < 0)
1716 return r;
1717
cc22955c
TH
1718 r = client_ensure_duid(client);
1719 if (r < 0)
1720 return r;
1721
10a0f27b
PF
1722 if (client->fd < 0) {
1723 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1724 if (r < 0) {
1725 _cleanup_free_ char *p = NULL;
1726
1727 (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1728 return log_dhcp6_client_errno(client, r,
1729 "Failed to bind to UDP socket at address %s: %m", strna(p));
1730 }
bd9a7221 1731
10a0f27b 1732 client->fd = r;
bd9a7221 1733 }
bbfa43ca 1734
fcb51238
YW
1735 if (client->information_request) {
1736 usec_t t = now(CLOCK_MONOTONIC);
1737
1738 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1739 return 0;
1740
1741 client->information_request_time_usec = t;
bbfa43ca 1742 state = DHCP6_STATE_INFORMATION_REQUEST;
fcb51238 1743 }
bbfa43ca
PF
1744
1745 log_dhcp6_client(client, "Started in %s mode",
483d099e
ZJS
1746 client->information_request? "Information request":
1747 "Managed");
bbfa43ca
PF
1748
1749 return client_start(client, state);
139b011a
PF
1750}
1751
32d20645 1752int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
139b011a
PF
1753 int r;
1754
1755 assert_return(client, -EINVAL);
1756 assert_return(!client->event, -EBUSY);
1757
1758 if (event)
1759 client->event = sd_event_ref(event);
1760 else {
1761 r = sd_event_default(&client->event);
1762 if (r < 0)
1763 return 0;
1764 }
1765
1766 client->event_priority = priority;
1767
1768 return 0;
1769}
1770
1771int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1772 assert_return(client, -EINVAL);
1773
1774 client->event = sd_event_unref(client->event);
1775
1776 return 0;
1777}
1778
1779sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
a1140666 1780 assert_return(client, NULL);
139b011a
PF
1781
1782 return client->event;
1783}
1784
8301aa0b
YW
1785static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1786 assert(client);
139b011a 1787
c9393e8c
YW
1788 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1789 client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
1790 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1791 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1792
3733eec3
LP
1793 client_reset(client);
1794
10a0f27b
PF
1795 client->fd = safe_close(client->fd);
1796
3733eec3 1797 sd_dhcp6_client_detach_event(client);
3733eec3
LP
1798
1799 free(client->req_opts);
8006aa32 1800 free(client->fqdn);
de8d6e55 1801 free(client->mudurl);
33923925 1802
e7d5fe17 1803 ordered_hashmap_free(client->extra_options);
33923925 1804 strv_free(client->user_class);
73c8ced7 1805 strv_free(client->vendor_class);
33923925 1806
6b430fdb 1807 return mfree(client);
139b011a
PF
1808}
1809
8301aa0b
YW
1810DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1811
0ae0e5cd 1812int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
4afd3348 1813 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
8b8ecac8 1814 _cleanup_free_ be16_t *req_opts = NULL;
da6fe470 1815 size_t t;
139b011a
PF
1816
1817 assert_return(ret, -EINVAL);
1818
8b8ecac8
YW
1819 req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
1820 if (!req_opts)
139b011a
PF
1821 return -ENOMEM;
1822
8b8ecac8
YW
1823 for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
1824 req_opts[t] = htobe16(default_req_opts[t]);
c806ffb9 1825
8b8ecac8
YW
1826 client = new(sd_dhcp6_client, 1);
1827 if (!client)
da6fe470
PF
1828 return -ENOMEM;
1829
8b8ecac8
YW
1830 *client = (sd_dhcp6_client) {
1831 .n_ref = 1,
1832 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1833 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1834 .ifindex = -1,
1835 .request = DHCP6_REQUEST_IA_NA,
1836 .fd = -1,
1837 .req_opts_len = ELEMENTSOF(default_req_opts),
2805536b
SS
1838 .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
1839 .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1,
8b8ecac8
YW
1840 .req_opts = TAKE_PTR(req_opts),
1841 };
da6fe470 1842
1cc6c93a 1843 *ret = TAKE_PTR(client);
139b011a
PF
1844
1845 return 0;
1846}