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