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