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