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