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