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