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