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