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