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