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