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