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