]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
Add SPDX license identifiers to source files under the LGPL
[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
c806ffb9 339 client->fd = safe_close(client->fd);
a9aff361 340
c3e2adea 341 client->transaction_id = 0;
346e13a2 342 client->transaction_start = 0;
a9aff361 343
f12abb48
PF
344 client->ia_na.timeout_t1 =
345 sd_event_source_unref(client->ia_na.timeout_t1);
346 client->ia_na.timeout_t2 =
347 sd_event_source_unref(client->ia_na.timeout_t2);
348
d1b0afe3
PF
349 client->retransmit_time = 0;
350 client->retransmit_count = 0;
351 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
352 client->timeout_resend_expire =
353 sd_event_source_unref(client->timeout_resend_expire);
354
139b011a
PF
355 client->state = DHCP6_STATE_STOPPED;
356
357 return 0;
358}
359
3f0c075f
PF
360static void client_stop(sd_dhcp6_client *client, int error) {
361 DHCP6_CLIENT_DONT_DESTROY(client);
139b011a 362
3f0c075f 363 assert(client);
139b011a 364
3f0c075f
PF
365 client_notify(client, error);
366
367 client_reset(client);
139b011a
PF
368}
369
346e13a2 370static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
a9aff361
PF
371 _cleanup_free_ DHCP6Message *message = NULL;
372 struct in6_addr all_servers =
373 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
374 size_t len, optlen = 512;
375 uint8_t *opt;
376 int r;
346e13a2
PF
377 usec_t elapsed_usec;
378 be16_t elapsed_time;
a9aff361 379
a1140666
LP
380 assert(client);
381
a9aff361
PF
382 len = sizeof(DHCP6Message) + optlen;
383
384 message = malloc0(len);
385 if (!message)
386 return -ENOMEM;
387
388 opt = (uint8_t *)(message + 1);
389
390 message->transaction_id = client->transaction_id;
391
392 switch(client->state) {
bbfa43ca
PF
393 case DHCP6_STATE_INFORMATION_REQUEST:
394 message->type = DHCP6_INFORMATION_REQUEST;
395
396 break;
397
a9aff361
PF
398 case DHCP6_STATE_SOLICITATION:
399 message->type = DHCP6_SOLICIT;
400
ed6ee219 401 r = dhcp6_option_append(&opt, &optlen,
2c1ab8ca 402 SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
926695f1
TA
403 if (r < 0)
404 return r;
ed6ee219 405
7246333c 406 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
a9aff361
PF
407 if (r < 0)
408 return r;
409
8006aa32
SA
410 if (client->fqdn) {
411 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
412 if (r < 0)
413 return r;
414 }
415
7246333c
PF
416 break;
417
418 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
419 case DHCP6_STATE_RENEW:
420
421 if (client->state == DHCP6_STATE_REQUEST)
422 message->type = DHCP6_REQUEST;
423 else
424 message->type = DHCP6_RENEW;
7246333c 425
2c1ab8ca 426 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
7246333c
PF
427 client->lease->serverid_len,
428 client->lease->serverid);
429 if (r < 0)
430 return r;
431
432 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
a9aff361
PF
433 if (r < 0)
434 return r;
435
8006aa32
SA
436 if (client->fqdn) {
437 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
438 if (r < 0)
439 return r;
440 }
441
a9aff361
PF
442 break;
443
3dc34fcc
PF
444 case DHCP6_STATE_REBIND:
445 message->type = DHCP6_REBIND;
446
447 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
448 if (r < 0)
449 return r;
450
8006aa32
SA
451 if (client->fqdn) {
452 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
453 if (r < 0)
454 return r;
455 }
456
3dc34fcc
PF
457 break;
458
a9aff361 459 case DHCP6_STATE_STOPPED:
a34b57c0 460 case DHCP6_STATE_BOUND:
a9aff361
PF
461 return -EINVAL;
462 }
463
2c1ab8ca 464 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
da6fe470
PF
465 client->req_opts_len * sizeof(be16_t),
466 client->req_opts);
467 if (r < 0)
468 return r;
469
ccd1fc2f 470 assert(client->duid_len);
2c1ab8ca 471 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
66eac120 472 client->duid_len, &client->duid);
7246333c
PF
473 if (r < 0)
474 return r;
475
346e13a2
PF
476 elapsed_usec = time_now - client->transaction_start;
477 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
478 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
479 else
480 elapsed_time = 0xffff;
481
2c1ab8ca 482 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
346e13a2
PF
483 sizeof(elapsed_time), &elapsed_time);
484 if (r < 0)
485 return r;
486
a9aff361
PF
487 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
488 len - optlen);
489 if (r < 0)
490 return r;
491
492 log_dhcp6_client(client, "Sent %s",
493 dhcp6_message_type_to_string(message->type));
494
495 return 0;
496}
497
4b558378 498static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
499 sd_dhcp6_client *client = userdata;
500
a1140666
LP
501 assert(s);
502 assert(client);
503 assert(client->lease);
a34b57c0
PF
504
505 client->lease->ia.timeout_t2 =
506 sd_event_source_unref(client->lease->ia.timeout_t2);
507
508 log_dhcp6_client(client, "Timeout T2");
509
3dc34fcc
PF
510 client_start(client, DHCP6_STATE_REBIND);
511
a34b57c0
PF
512 return 0;
513}
514
4b558378 515static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
a34b57c0
PF
516 sd_dhcp6_client *client = userdata;
517
a1140666
LP
518 assert(s);
519 assert(client);
520 assert(client->lease);
a34b57c0
PF
521
522 client->lease->ia.timeout_t1 =
523 sd_event_source_unref(client->lease->ia.timeout_t1);
524
525 log_dhcp6_client(client, "Timeout T1");
526
3dc34fcc
PF
527 client_start(client, DHCP6_STATE_RENEW);
528
a34b57c0
PF
529 return 0;
530}
531
4b558378 532static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3 533 sd_dhcp6_client *client = userdata;
3dc34fcc
PF
534 DHCP6_CLIENT_DONT_DESTROY(client);
535 enum DHCP6State state;
d1b0afe3
PF
536
537 assert(s);
538 assert(client);
539 assert(client->event);
540
3dc34fcc
PF
541 state = client->state;
542
10c9ce61 543 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
d1b0afe3 544
3dc34fcc
PF
545 /* RFC 3315, section 18.1.4., says that "...the client may choose to
546 use a Solicit message to locate a new DHCP server..." */
547 if (state == DHCP6_STATE_REBIND)
548 client_start(client, DHCP6_STATE_SOLICITATION);
549
d1b0afe3
PF
550 return 0;
551}
552
553static usec_t client_timeout_compute_random(usec_t val) {
554 return val - val / 10 +
555 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
556}
557
4b558378 558static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
d1b0afe3
PF
559 int r = 0;
560 sd_dhcp6_client *client = userdata;
4b4923e6 561 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
5e913450 562 usec_t max_retransmit_duration = 0;
513a6fa8 563 uint8_t max_retransmit_count = 0;
d1b0afe3 564 char time_string[FORMAT_TIMESPAN_MAX];
3dc34fcc 565 uint32_t expire = 0;
d1b0afe3
PF
566
567 assert(s);
568 assert(client);
569 assert(client->event);
570
571 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
572
573 switch (client->state) {
bbfa43ca
PF
574 case DHCP6_STATE_INFORMATION_REQUEST:
575 init_retransmit_time = DHCP6_INF_TIMEOUT;
576 max_retransmit_time = DHCP6_INF_MAX_RT;
577
578 break;
579
d1b0afe3 580 case DHCP6_STATE_SOLICITATION:
7246333c
PF
581
582 if (client->retransmit_count && client->lease) {
583 client_start(client, DHCP6_STATE_REQUEST);
584 return 0;
585 }
586
d1b0afe3
PF
587 init_retransmit_time = DHCP6_SOL_TIMEOUT;
588 max_retransmit_time = DHCP6_SOL_MAX_RT;
d1b0afe3
PF
589
590 break;
591
7246333c
PF
592 case DHCP6_STATE_REQUEST:
593 init_retransmit_time = DHCP6_REQ_TIMEOUT;
594 max_retransmit_time = DHCP6_REQ_MAX_RT;
595 max_retransmit_count = DHCP6_REQ_MAX_RC;
7246333c
PF
596
597 break;
598
3dc34fcc
PF
599 case DHCP6_STATE_RENEW:
600 init_retransmit_time = DHCP6_REN_TIMEOUT;
601 max_retransmit_time = DHCP6_REN_MAX_RT;
3dc34fcc
PF
602
603 /* RFC 3315, section 18.1.3. says max retransmit duration will
604 be the remaining time until T2. Instead of setting MRD,
605 wait for T2 to trigger with the same end result */
3dc34fcc
PF
606
607 break;
608
609 case DHCP6_STATE_REBIND:
610 init_retransmit_time = DHCP6_REB_TIMEOUT;
611 max_retransmit_time = DHCP6_REB_MAX_RT;
3dc34fcc
PF
612
613 if (!client->timeout_resend_expire) {
614 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
615 &expire);
616 if (r < 0) {
617 client_stop(client, r);
618 return 0;
619 }
620 max_retransmit_duration = expire * USEC_PER_SEC;
621 }
622
623 break;
624
d1b0afe3 625 case DHCP6_STATE_STOPPED:
a34b57c0 626 case DHCP6_STATE_BOUND:
d1b0afe3
PF
627 return 0;
628 }
629
630 if (max_retransmit_count &&
631 client->retransmit_count >= max_retransmit_count) {
10c9ce61 632 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
d1b0afe3
PF
633 return 0;
634 }
635
fa94c34b 636 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d1b0afe3
PF
637 if (r < 0)
638 goto error;
639
346e13a2
PF
640 r = client_send_message(client, time_now);
641 if (r >= 0)
642 client->retransmit_count++;
643
d1b0afe3
PF
644 if (!client->retransmit_time) {
645 client->retransmit_time =
646 client_timeout_compute_random(init_retransmit_time);
a9aff361
PF
647
648 if (client->state == DHCP6_STATE_SOLICITATION)
649 client->retransmit_time += init_retransmit_time / 10;
650
d1b0afe3
PF
651 } else {
652 if (max_retransmit_time &&
653 client->retransmit_time > max_retransmit_time / 2)
654 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
655 else
656 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
657 }
658
659 log_dhcp6_client(client, "Next retransmission in %s",
ed19c567 660 format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
d1b0afe3
PF
661
662 r = sd_event_add_time(client->event, &client->timeout_resend,
fa94c34b 663 clock_boottime_or_monotonic(),
d1b0afe3
PF
664 time_now + client->retransmit_time,
665 10 * USEC_PER_MSEC, client_timeout_resend,
666 client);
667 if (r < 0)
668 goto error;
669
670 r = sd_event_source_set_priority(client->timeout_resend,
671 client->event_priority);
672 if (r < 0)
673 goto error;
674
356779df 675 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer");
9021bb9f
TG
676 if (r < 0)
677 goto error;
678
d1b0afe3
PF
679 if (max_retransmit_duration && !client->timeout_resend_expire) {
680
681 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
682 max_retransmit_duration / USEC_PER_SEC);
683
684 r = sd_event_add_time(client->event,
685 &client->timeout_resend_expire,
fa94c34b 686 clock_boottime_or_monotonic(),
d1b0afe3
PF
687 time_now + max_retransmit_duration,
688 USEC_PER_SEC,
689 client_timeout_resend_expire, client);
690 if (r < 0)
691 goto error;
692
693 r = sd_event_source_set_priority(client->timeout_resend_expire,
694 client->event_priority);
695 if (r < 0)
696 goto error;
9021bb9f 697
356779df 698 r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer");
9021bb9f
TG
699 if (r < 0)
700 goto error;
d1b0afe3
PF
701 }
702
703error:
704 if (r < 0)
705 client_stop(client, r);
706
707 return 0;
708}
709
f12abb48 710static int client_ensure_iaid(sd_dhcp6_client *client) {
cfb5b380 711 int r;
f12abb48
PF
712
713 assert(client);
714
715 if (client->ia_na.id)
716 return 0;
717
2f8e7633 718 r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id);
cfb5b380
TG
719 if (r < 0)
720 return r;
f12abb48
PF
721
722 return 0;
723}
724
4b558378
ZJS
725static int client_parse_message(
726 sd_dhcp6_client *client,
727 DHCP6Message *message,
728 size_t len,
729 sd_dhcp6_lease *lease) {
631bbe71 730 int r;
44481a8b 731 uint8_t *optval, *option, *id = NULL;
631bbe71
PF
732 uint16_t optcode, status;
733 size_t optlen, id_len;
734 bool clientid = false;
735 be32_t iaid_lease;
736
a1140666
LP
737 assert(client);
738 assert(message);
739 assert(len >= sizeof(DHCP6Message));
740 assert(lease);
741
44481a8b
ZJS
742 option = (uint8_t *)message + sizeof(DHCP6Message);
743 len -= sizeof(DHCP6Message);
744
631bbe71
PF
745 while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
746 &optval)) >= 0) {
747 switch (optcode) {
2c1ab8ca 748 case SD_DHCP6_OPTION_CLIENTID:
631bbe71
PF
749 if (clientid) {
750 log_dhcp6_client(client, "%s contains multiple clientids",
751 dhcp6_message_type_to_string(message->type));
752 return -EINVAL;
753 }
754
66eac120 755 if (optlen != client->duid_len ||
631bbe71
PF
756 memcmp(&client->duid, optval, optlen) != 0) {
757 log_dhcp6_client(client, "%s DUID does not match",
758 dhcp6_message_type_to_string(message->type));
759
760 return -EINVAL;
761 }
762 clientid = true;
763
764 break;
765
2c1ab8ca 766 case SD_DHCP6_OPTION_SERVERID:
631bbe71
PF
767 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
768 if (r >= 0 && id) {
769 log_dhcp6_client(client, "%s contains multiple serverids",
770 dhcp6_message_type_to_string(message->type));
771 return -EINVAL;
772 }
773
774 r = dhcp6_lease_set_serverid(lease, optval, optlen);
775 if (r < 0)
776 return r;
777
778 break;
779
2c1ab8ca 780 case SD_DHCP6_OPTION_PREFERENCE:
631bbe71
PF
781 if (optlen != 1)
782 return -EINVAL;
783
784 r = dhcp6_lease_set_preference(lease, *optval);
785 if (r < 0)
786 return r;
787
788 break;
789
2c1ab8ca 790 case SD_DHCP6_OPTION_STATUS_CODE:
631bbe71
PF
791 if (optlen < 2)
792 return -EINVAL;
793
794 status = optval[0] << 8 | optval[1];
795 if (status) {
796 log_dhcp6_client(client, "%s Status %s",
797 dhcp6_message_type_to_string(message->type),
798 dhcp6_message_status_to_string(status));
799 return -EINVAL;
800 }
801
802 break;
803
2c1ab8ca 804 case SD_DHCP6_OPTION_IA_NA:
bbfa43ca
PF
805 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
806 log_dhcp6_client(client, "Information request ignoring IA NA option");
807
808 break;
809 }
810
631bbe71
PF
811 r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
812 &lease->ia);
813 if (r < 0 && r != -ENOMSG)
814 return r;
815
816 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
817 if (r < 0)
818 return r;
819
820 if (client->ia_na.id != iaid_lease) {
821 log_dhcp6_client(client, "%s has wrong IAID",
822 dhcp6_message_type_to_string(message->type));
823 return -EINVAL;
824 }
825
826 break;
ed6ee219 827
2c1ab8ca 828 case SD_DHCP6_OPTION_RAPID_COMMIT:
ed6ee219
PF
829 r = dhcp6_lease_set_rapid_commit(lease);
830 if (r < 0)
831 return r;
832
833 break;
7bd8e95d 834
2c1ab8ca 835 case SD_DHCP6_OPTION_DNS_SERVERS:
7bd8e95d
PF
836 r = dhcp6_lease_set_dns(lease, optval, optlen);
837 if (r < 0)
838 return r;
839
840 break;
5da1b97f 841
2c1ab8ca 842 case SD_DHCP6_OPTION_DOMAIN_LIST:
5da1b97f
PF
843 r = dhcp6_lease_set_domains(lease, optval, optlen);
844 if (r < 0)
845 return r;
846
847 break;
848
2c1ab8ca 849 case SD_DHCP6_OPTION_NTP_SERVER:
6599680e
PF
850 r = dhcp6_lease_set_ntp(lease, optval, optlen);
851 if (r < 0)
852 return r;
853
854 break;
41e4615d 855
2c1ab8ca 856 case SD_DHCP6_OPTION_SNTP_SERVERS:
41e4615d
PF
857 r = dhcp6_lease_set_sntp(lease, optval, optlen);
858 if (r < 0)
859 return r;
860
861 break;
631bbe71 862 }
6599680e 863
631bbe71
PF
864 }
865
c47e8936
PF
866 if (r == -ENOMSG)
867 r = 0;
868
869 if (r < 0 || !clientid) {
631bbe71
PF
870 log_dhcp6_client(client, "%s has incomplete options",
871 dhcp6_message_type_to_string(message->type));
872 return -EINVAL;
873 }
874
bbfa43ca
PF
875 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
876 r = dhcp6_lease_get_serverid(lease, &id, &id_len);
877 if (r < 0)
878 log_dhcp6_client(client, "%s has no server id",
879 dhcp6_message_type_to_string(message->type));
880 }
631bbe71
PF
881
882 return r;
883}
884
0ae0e5cd 885static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
4afd3348 886 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
ed6ee219 887 bool rapid_commit;
a1140666
LP
888 int r;
889
890 assert(client);
891 assert(reply);
a34b57c0
PF
892
893 if (reply->type != DHCP6_REPLY)
ed6ee219 894 return 0;
a34b57c0
PF
895
896 r = dhcp6_lease_new(&lease);
897 if (r < 0)
898 return -ENOMEM;
899
900 r = client_parse_message(client, reply, len, lease);
901 if (r < 0)
902 return r;
903
ed6ee219
PF
904 if (client->state == DHCP6_STATE_SOLICITATION) {
905 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
906 if (r < 0)
907 return r;
908
909 if (!rapid_commit)
910 return 0;
911 }
912
f8908727 913 client_set_lease(client, lease);
9d89d1ae 914 lease = NULL;
a34b57c0
PF
915
916 return DHCP6_STATE_BOUND;
917}
918
0ae0e5cd 919static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
4afd3348 920 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
631bbe71 921 uint8_t pref_advertise = 0, pref_lease = 0;
a1140666 922 int r;
631bbe71
PF
923
924 if (advertise->type != DHCP6_ADVERTISE)
ed6ee219 925 return 0;
631bbe71
PF
926
927 r = dhcp6_lease_new(&lease);
928 if (r < 0)
929 return r;
930
931 r = client_parse_message(client, advertise, len, lease);
932 if (r < 0)
933 return r;
934
935 r = dhcp6_lease_get_preference(lease, &pref_advertise);
936 if (r < 0)
937 return r;
938
939 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
bbfa43ca
PF
940
941 if (r < 0 || pref_advertise > pref_lease) {
f8908727 942 client_set_lease(client, lease);
631bbe71
PF
943 lease = NULL;
944 r = 0;
945 }
946
7246333c
PF
947 if (pref_advertise == 255 || client->retransmit_count > 1)
948 r = DHCP6_STATE_REQUEST;
949
631bbe71
PF
950 return r;
951}
952
004845d1
LP
953static int client_receive_message(
954 sd_event_source *s,
955 int fd, uint32_t
956 revents,
957 void *userdata) {
958
631bbe71 959 sd_dhcp6_client *client = userdata;
3f0c075f 960 DHCP6_CLIENT_DONT_DESTROY(client);
0d43d2fc 961 _cleanup_free_ DHCP6Message *message = NULL;
4edc2c9b
LP
962 ssize_t buflen, len;
963 int r = 0;
631bbe71
PF
964
965 assert(s);
966 assert(client);
967 assert(client->event);
968
4edc2c9b
LP
969 buflen = next_datagram_size_fd(fd);
970 if (buflen < 0)
971 return buflen;
631bbe71 972
0d43d2fc 973 message = malloc(buflen);
631bbe71
PF
974 if (!message)
975 return -ENOMEM;
976
cf447cb6 977 len = recv(fd, message, buflen, 0);
0d43d2fc 978 if (len < 0) {
4c701096 979 if (IN_SET(errno, EAGAIN, EINTR))
0d43d2fc
TG
980 return 0;
981
bd9a7221 982 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
0d43d2fc 983
004845d1
LP
984 }
985 if ((size_t) len < sizeof(DHCP6Message)) {
986 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
631bbe71 987 return 0;
004845d1 988 }
631bbe71
PF
989
990 switch(message->type) {
991 case DHCP6_SOLICIT:
992 case DHCP6_REQUEST:
993 case DHCP6_CONFIRM:
994 case DHCP6_RENEW:
995 case DHCP6_REBIND:
996 case DHCP6_RELEASE:
997 case DHCP6_DECLINE:
998 case DHCP6_INFORMATION_REQUEST:
999 case DHCP6_RELAY_FORW:
1000 case DHCP6_RELAY_REPL:
1001 return 0;
1002
1003 case DHCP6_ADVERTISE:
1004 case DHCP6_REPLY:
1005 case DHCP6_RECONFIGURE:
1006 break;
1007
1008 default:
bd9a7221 1009 log_dhcp6_client(client, "Unknown message type %d", message->type);
631bbe71
PF
1010 return 0;
1011 }
1012
1013 if (client->transaction_id != (message->transaction_id &
1014 htobe32(0x00ffffff)))
1015 return 0;
1016
1017 switch (client->state) {
bbfa43ca
PF
1018 case DHCP6_STATE_INFORMATION_REQUEST:
1019 r = client_receive_reply(client, message, len);
1020 if (r < 0)
1021 return 0;
1022
10c9ce61 1023 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
bbfa43ca
PF
1024
1025 client_start(client, DHCP6_STATE_STOPPED);
1026
1027 break;
1028
631bbe71
PF
1029 case DHCP6_STATE_SOLICITATION:
1030 r = client_receive_advertise(client, message, len);
1031
ed6ee219 1032 if (r == DHCP6_STATE_REQUEST) {
7246333c
PF
1033 client_start(client, r);
1034
ed6ee219
PF
1035 break;
1036 }
631bbe71 1037
ec251fe7 1038 /* fall through */ /* for Soliciation Rapid Commit option check */
7246333c 1039 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1040 case DHCP6_STATE_RENEW:
1041 case DHCP6_STATE_REBIND:
1042
a34b57c0
PF
1043 r = client_receive_reply(client, message, len);
1044 if (r < 0)
1045 return 0;
1046
1047 if (r == DHCP6_STATE_BOUND) {
1048
1049 r = client_start(client, DHCP6_STATE_BOUND);
1050 if (r < 0) {
1051 client_stop(client, r);
1052 return 0;
1053 }
1054
10c9ce61 1055 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
a34b57c0
PF
1056 }
1057
1058 break;
1059
1060 case DHCP6_STATE_BOUND:
1061
1062 break;
1063
631bbe71 1064 case DHCP6_STATE_STOPPED:
631bbe71
PF
1065 return 0;
1066 }
1067
bd9a7221 1068 if (r >= 0)
631bbe71
PF
1069 log_dhcp6_client(client, "Recv %s",
1070 dhcp6_message_type_to_string(message->type));
631bbe71 1071
a9aff361
PF
1072 return 0;
1073}
1074
0ae0e5cd 1075static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
f12abb48 1076 int r;
a34b57c0
PF
1077 usec_t timeout, time_now;
1078 char time_string[FORMAT_TIMESPAN_MAX];
f12abb48
PF
1079
1080 assert_return(client, -EINVAL);
1081 assert_return(client->event, -EINVAL);
2f8e7633 1082 assert_return(client->ifindex > 0, -EINVAL);
c3e2adea 1083 assert_return(client->state != state, -EINVAL);
f12abb48 1084
c3e2adea
PF
1085 client->timeout_resend_expire =
1086 sd_event_source_unref(client->timeout_resend_expire);
1087 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1088 client->retransmit_time = 0;
1089 client->retransmit_count = 0;
f12abb48 1090
38a03f06
LP
1091 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1092 if (r < 0)
1093 return r;
346e13a2 1094
c3e2adea
PF
1095 switch (state) {
1096 case DHCP6_STATE_STOPPED:
bbfa43ca
PF
1097 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1098 client->state = DHCP6_STATE_STOPPED;
a9aff361 1099
bbfa43ca
PF
1100 return 0;
1101 }
9021bb9f 1102
bbfa43ca
PF
1103 /* fall through */
1104 case DHCP6_STATE_SOLICITATION:
c3e2adea
PF
1105 client->state = DHCP6_STATE_SOLICITATION;
1106
7246333c
PF
1107 break;
1108
bbfa43ca 1109 case DHCP6_STATE_INFORMATION_REQUEST:
7246333c 1110 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1111 case DHCP6_STATE_RENEW:
1112 case DHCP6_STATE_REBIND:
a34b57c0 1113
7246333c
PF
1114 client->state = state;
1115
c3e2adea 1116 break;
a34b57c0
PF
1117
1118 case DHCP6_STATE_BOUND:
1119
a34b57c0
PF
1120 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1121 client->lease->ia.lifetime_t2 == 0xffffffff) {
1122
bd9a7221 1123 log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
a34b57c0
PF
1124 be32toh(client->lease->ia.lifetime_t1),
1125 be32toh(client->lease->ia.lifetime_t2));
1126
1127 return 0;
1128 }
1129
1130 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1131
1132 log_dhcp6_client(client, "T1 expires in %s",
ed19c567 1133 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0
PF
1134
1135 r = sd_event_add_time(client->event,
1136 &client->lease->ia.timeout_t1,
fa94c34b 1137 clock_boottime_or_monotonic(), time_now + timeout,
a34b57c0
PF
1138 10 * USEC_PER_SEC, client_timeout_t1,
1139 client);
1140 if (r < 0)
1141 return r;
1142
1143 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1144 client->event_priority);
1145 if (r < 0)
1146 return r;
1147
356779df 1148 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
9021bb9f
TG
1149 if (r < 0)
1150 return r;
1151
a34b57c0
PF
1152 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1153
1154 log_dhcp6_client(client, "T2 expires in %s",
ed19c567 1155 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0
PF
1156
1157 r = sd_event_add_time(client->event,
1158 &client->lease->ia.timeout_t2,
fa94c34b 1159 clock_boottime_or_monotonic(), time_now + timeout,
a34b57c0
PF
1160 10 * USEC_PER_SEC, client_timeout_t2,
1161 client);
1162 if (r < 0)
1163 return r;
1164
1165 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1166 client->event_priority);
1167 if (r < 0)
1168 return r;
1169
356779df 1170 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
9021bb9f
TG
1171 if (r < 0)
1172 return r;
1173
3dc34fcc
PF
1174 client->state = state;
1175
a34b57c0 1176 return 0;
c3e2adea 1177 }
a9aff361 1178
c3e2adea 1179 client->transaction_id = random_u32() & htobe32(0x00ffffff);
346e13a2 1180 client->transaction_start = time_now;
d1b0afe3
PF
1181
1182 r = sd_event_add_time(client->event, &client->timeout_resend,
fa94c34b 1183 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
d1b0afe3
PF
1184 client);
1185 if (r < 0)
1186 return r;
1187
1188 r = sd_event_source_set_priority(client->timeout_resend,
1189 client->event_priority);
1190 if (r < 0)
1191 return r;
1192
356779df 1193 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
9021bb9f
TG
1194 if (r < 0)
1195 return r;
1196
f12abb48
PF
1197 return 0;
1198}
1199
0ae0e5cd 1200int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
f667c150
TG
1201 assert_return(client, -EINVAL);
1202
10c9ce61 1203 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
139b011a
PF
1204
1205 return 0;
1206}
1207
f667c150
TG
1208int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1209 assert_return(client, -EINVAL);
1210
1211 return client->state != DHCP6_STATE_STOPPED;
1212}
1213
0ae0e5cd 1214int sd_dhcp6_client_start(sd_dhcp6_client *client) {
bbfa43ca 1215 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
2f8e7633 1216 int r = 0;
139b011a
PF
1217
1218 assert_return(client, -EINVAL);
1219 assert_return(client->event, -EINVAL);
2f8e7633 1220 assert_return(client->ifindex > 0, -EINVAL);
c601ebf7 1221 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
139b011a 1222
d7c9c21f 1223 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
63348d13 1224 return -EBUSY;
d7c9c21f 1225
c806ffb9 1226 r = client_reset(client);
f12abb48
PF
1227 if (r < 0)
1228 return r;
1229
bbfa43ca
PF
1230 r = client_ensure_iaid(client);
1231 if (r < 0)
1232 return r;
1233
cc22955c
TH
1234 r = client_ensure_duid(client);
1235 if (r < 0)
1236 return r;
1237
2f8e7633 1238 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
bd9a7221
ZJS
1239 if (r < 0) {
1240 _cleanup_free_ char *p = NULL;
1241
1242 (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1243 return log_dhcp6_client_errno(client, r,
1244 "Failed to bind to UDP socket at address %s: %m", strna(p));
1245 }
bbfa43ca
PF
1246
1247 client->fd = r;
1248
1249 r = sd_event_add_io(client->event, &client->receive_message,
1250 client->fd, EPOLLIN, client_receive_message,
1251 client);
1252 if (r < 0)
1253 goto error;
1254
1255 r = sd_event_source_set_priority(client->receive_message,
1256 client->event_priority);
1257 if (r < 0)
1258 goto error;
1259
1260 r = sd_event_source_set_description(client->receive_message,
483d099e 1261 "dhcp6-receive-message");
bbfa43ca
PF
1262 if (r < 0)
1263 goto error;
1264
1265 if (client->information_request)
1266 state = DHCP6_STATE_INFORMATION_REQUEST;
1267
1268 log_dhcp6_client(client, "Started in %s mode",
483d099e
ZJS
1269 client->information_request? "Information request":
1270 "Managed");
bbfa43ca
PF
1271
1272 return client_start(client, state);
1273
1274error:
1275 client_reset(client);
1276 return r;
139b011a
PF
1277}
1278
32d20645 1279int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
139b011a
PF
1280 int r;
1281
1282 assert_return(client, -EINVAL);
1283 assert_return(!client->event, -EBUSY);
1284
1285 if (event)
1286 client->event = sd_event_ref(event);
1287 else {
1288 r = sd_event_default(&client->event);
1289 if (r < 0)
1290 return 0;
1291 }
1292
1293 client->event_priority = priority;
1294
1295 return 0;
1296}
1297
1298int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1299 assert_return(client, -EINVAL);
1300
1301 client->event = sd_event_unref(client->event);
1302
1303 return 0;
1304}
1305
1306sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
a1140666 1307 assert_return(client, NULL);
139b011a
PF
1308
1309 return client->event;
1310}
1311
1312sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
3733eec3
LP
1313
1314 if (!client)
1315 return NULL;
1316
1317 assert(client->n_ref >= 1);
1318 client->n_ref++;
139b011a
PF
1319
1320 return client;
1321}
1322
1323sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
139b011a 1324
3733eec3
LP
1325 if (!client)
1326 return NULL;
139b011a 1327
3733eec3
LP
1328 assert(client->n_ref >= 1);
1329 client->n_ref--;
139b011a 1330
3733eec3 1331 if (client->n_ref > 0)
139b011a 1332 return NULL;
139b011a 1333
3733eec3
LP
1334 client_reset(client);
1335
1336 sd_dhcp6_client_detach_event(client);
3733eec3
LP
1337
1338 free(client->req_opts);
8006aa32 1339 free(client->fqdn);
6b430fdb 1340 return mfree(client);
139b011a
PF
1341}
1342
0ae0e5cd 1343int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
4afd3348 1344 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
da6fe470 1345 size_t t;
139b011a
PF
1346
1347 assert_return(ret, -EINVAL);
1348
1349 client = new0(sd_dhcp6_client, 1);
1350 if (!client)
1351 return -ENOMEM;
1352
3733eec3 1353 client->n_ref = 1;
2c1ab8ca 1354 client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
2f8e7633 1355 client->ifindex = -1;
c806ffb9
ZJS
1356 client->fd = -1;
1357
da6fe470 1358 client->req_opts_len = ELEMENTSOF(default_req_opts);
da6fe470
PF
1359 client->req_opts = new0(be16_t, client->req_opts_len);
1360 if (!client->req_opts)
1361 return -ENOMEM;
1362
1363 for (t = 0; t < client->req_opts_len; t++)
1364 client->req_opts[t] = htobe16(default_req_opts[t]);
1365
139b011a
PF
1366 *ret = client;
1367 client = NULL;
1368
1369 return 0;
1370}