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