]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-client.c
tree-wide: drop spurious "&"s when passing functions around
[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
0ae0e5cd 901static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
631bbe71 902 sd_dhcp6_client *client = userdata;
3f0c075f 903 DHCP6_CLIENT_DONT_DESTROY(client);
0d43d2fc 904 _cleanup_free_ DHCP6Message *message = NULL;
4edc2c9b
LP
905 ssize_t buflen, len;
906 int r = 0;
631bbe71
PF
907
908 assert(s);
909 assert(client);
910 assert(client->event);
911
4edc2c9b
LP
912 buflen = next_datagram_size_fd(fd);
913 if (buflen < 0)
914 return buflen;
631bbe71 915
0d43d2fc 916 message = malloc(buflen);
631bbe71
PF
917 if (!message)
918 return -ENOMEM;
919
920 len = read(fd, message, buflen);
0d43d2fc
TG
921 if (len < 0) {
922 if (errno == EAGAIN || errno == EINTR)
923 return 0;
924
925 log_dhcp6_client(client, "Could not receive message from UDP socket: %m");
926
927 return -errno;
928 } else if ((size_t)len < sizeof(DHCP6Message))
631bbe71 929 return 0;
631bbe71
PF
930
931 switch(message->type) {
932 case DHCP6_SOLICIT:
933 case DHCP6_REQUEST:
934 case DHCP6_CONFIRM:
935 case DHCP6_RENEW:
936 case DHCP6_REBIND:
937 case DHCP6_RELEASE:
938 case DHCP6_DECLINE:
939 case DHCP6_INFORMATION_REQUEST:
940 case DHCP6_RELAY_FORW:
941 case DHCP6_RELAY_REPL:
942 return 0;
943
944 case DHCP6_ADVERTISE:
945 case DHCP6_REPLY:
946 case DHCP6_RECONFIGURE:
947 break;
948
949 default:
950 log_dhcp6_client(client, "unknown message type %d",
951 message->type);
952 return 0;
953 }
954
955 if (client->transaction_id != (message->transaction_id &
956 htobe32(0x00ffffff)))
957 return 0;
958
959 switch (client->state) {
bbfa43ca
PF
960 case DHCP6_STATE_INFORMATION_REQUEST:
961 r = client_receive_reply(client, message, len);
962 if (r < 0)
963 return 0;
964
10c9ce61 965 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
bbfa43ca
PF
966
967 client_start(client, DHCP6_STATE_STOPPED);
968
969 break;
970
631bbe71
PF
971 case DHCP6_STATE_SOLICITATION:
972 r = client_receive_advertise(client, message, len);
973
ed6ee219 974 if (r == DHCP6_STATE_REQUEST) {
7246333c
PF
975 client_start(client, r);
976
ed6ee219
PF
977 break;
978 }
631bbe71 979
ed6ee219 980 /* fall through for Soliciation Rapid Commit option check */
7246333c 981 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
982 case DHCP6_STATE_RENEW:
983 case DHCP6_STATE_REBIND:
984
a34b57c0
PF
985 r = client_receive_reply(client, message, len);
986 if (r < 0)
987 return 0;
988
989 if (r == DHCP6_STATE_BOUND) {
990
991 r = client_start(client, DHCP6_STATE_BOUND);
992 if (r < 0) {
993 client_stop(client, r);
994 return 0;
995 }
996
10c9ce61 997 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
a34b57c0
PF
998 }
999
1000 break;
1001
1002 case DHCP6_STATE_BOUND:
1003
1004 break;
1005
631bbe71 1006 case DHCP6_STATE_STOPPED:
631bbe71
PF
1007 return 0;
1008 }
1009
1010 if (r >= 0) {
1011 log_dhcp6_client(client, "Recv %s",
1012 dhcp6_message_type_to_string(message->type));
1013 }
1014
a9aff361
PF
1015 return 0;
1016}
1017
0ae0e5cd 1018static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
f12abb48 1019 int r;
a34b57c0
PF
1020 usec_t timeout, time_now;
1021 char time_string[FORMAT_TIMESPAN_MAX];
f12abb48
PF
1022
1023 assert_return(client, -EINVAL);
1024 assert_return(client->event, -EINVAL);
1025 assert_return(client->index > 0, -EINVAL);
c3e2adea 1026 assert_return(client->state != state, -EINVAL);
f12abb48 1027
c3e2adea
PF
1028 client->timeout_resend_expire =
1029 sd_event_source_unref(client->timeout_resend_expire);
1030 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1031 client->retransmit_time = 0;
1032 client->retransmit_count = 0;
f12abb48 1033
38a03f06
LP
1034 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1035 if (r < 0)
1036 return r;
346e13a2 1037
c3e2adea
PF
1038 switch (state) {
1039 case DHCP6_STATE_STOPPED:
bbfa43ca
PF
1040 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1041 client->state = DHCP6_STATE_STOPPED;
a9aff361 1042
bbfa43ca
PF
1043 return 0;
1044 }
9021bb9f 1045
bbfa43ca
PF
1046 /* fall through */
1047 case DHCP6_STATE_SOLICITATION:
c3e2adea
PF
1048 client->state = DHCP6_STATE_SOLICITATION;
1049
7246333c
PF
1050 break;
1051
bbfa43ca 1052 case DHCP6_STATE_INFORMATION_REQUEST:
7246333c 1053 case DHCP6_STATE_REQUEST:
3dc34fcc
PF
1054 case DHCP6_STATE_RENEW:
1055 case DHCP6_STATE_REBIND:
a34b57c0 1056
7246333c
PF
1057 client->state = state;
1058
c3e2adea 1059 break;
a34b57c0
PF
1060
1061 case DHCP6_STATE_BOUND:
1062
a34b57c0
PF
1063 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1064 client->lease->ia.lifetime_t2 == 0xffffffff) {
1065
1066 log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1067 be32toh(client->lease->ia.lifetime_t1),
1068 be32toh(client->lease->ia.lifetime_t2));
1069
1070 return 0;
1071 }
1072
1073 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1074
1075 log_dhcp6_client(client, "T1 expires in %s",
ed19c567 1076 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0
PF
1077
1078 r = sd_event_add_time(client->event,
1079 &client->lease->ia.timeout_t1,
fa94c34b 1080 clock_boottime_or_monotonic(), time_now + timeout,
a34b57c0
PF
1081 10 * USEC_PER_SEC, client_timeout_t1,
1082 client);
1083 if (r < 0)
1084 return r;
1085
1086 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1087 client->event_priority);
1088 if (r < 0)
1089 return r;
1090
356779df 1091 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
9021bb9f
TG
1092 if (r < 0)
1093 return r;
1094
a34b57c0
PF
1095 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1096
1097 log_dhcp6_client(client, "T2 expires in %s",
ed19c567 1098 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
a34b57c0
PF
1099
1100 r = sd_event_add_time(client->event,
1101 &client->lease->ia.timeout_t2,
fa94c34b 1102 clock_boottime_or_monotonic(), time_now + timeout,
a34b57c0
PF
1103 10 * USEC_PER_SEC, client_timeout_t2,
1104 client);
1105 if (r < 0)
1106 return r;
1107
1108 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1109 client->event_priority);
1110 if (r < 0)
1111 return r;
1112
356779df 1113 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
9021bb9f
TG
1114 if (r < 0)
1115 return r;
1116
3dc34fcc
PF
1117 client->state = state;
1118
a34b57c0 1119 return 0;
c3e2adea 1120 }
a9aff361 1121
c3e2adea 1122 client->transaction_id = random_u32() & htobe32(0x00ffffff);
346e13a2 1123 client->transaction_start = time_now;
d1b0afe3
PF
1124
1125 r = sd_event_add_time(client->event, &client->timeout_resend,
fa94c34b 1126 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
d1b0afe3
PF
1127 client);
1128 if (r < 0)
1129 return r;
1130
1131 r = sd_event_source_set_priority(client->timeout_resend,
1132 client->event_priority);
1133 if (r < 0)
1134 return r;
1135
356779df 1136 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
9021bb9f
TG
1137 if (r < 0)
1138 return r;
1139
f12abb48
PF
1140 return 0;
1141}
1142
0ae0e5cd 1143int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
f667c150
TG
1144 assert_return(client, -EINVAL);
1145
10c9ce61 1146 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
139b011a
PF
1147
1148 return 0;
1149}
1150
f667c150
TG
1151int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1152 assert_return(client, -EINVAL);
1153
1154 return client->state != DHCP6_STATE_STOPPED;
1155}
1156
0ae0e5cd 1157int sd_dhcp6_client_start(sd_dhcp6_client *client) {
139b011a 1158 int r = 0;
bbfa43ca 1159 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
139b011a
PF
1160
1161 assert_return(client, -EINVAL);
1162 assert_return(client->event, -EINVAL);
1163 assert_return(client->index > 0, -EINVAL);
c601ebf7 1164 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
139b011a 1165
d7c9c21f 1166 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
63348d13 1167 return -EBUSY;
d7c9c21f 1168
c806ffb9 1169 r = client_reset(client);
f12abb48
PF
1170 if (r < 0)
1171 return r;
1172
bbfa43ca
PF
1173 r = client_ensure_iaid(client);
1174 if (r < 0)
1175 return r;
1176
cc22955c
TH
1177 r = client_ensure_duid(client);
1178 if (r < 0)
1179 return r;
1180
c601ebf7 1181 r = dhcp6_network_bind_udp_socket(client->index, &client->local_address);
bbfa43ca
PF
1182 if (r < 0)
1183 return r;
1184
1185 client->fd = r;
1186
1187 r = sd_event_add_io(client->event, &client->receive_message,
1188 client->fd, EPOLLIN, client_receive_message,
1189 client);
1190 if (r < 0)
1191 goto error;
1192
1193 r = sd_event_source_set_priority(client->receive_message,
1194 client->event_priority);
1195 if (r < 0)
1196 goto error;
1197
1198 r = sd_event_source_set_description(client->receive_message,
483d099e 1199 "dhcp6-receive-message");
bbfa43ca
PF
1200 if (r < 0)
1201 goto error;
1202
1203 if (client->information_request)
1204 state = DHCP6_STATE_INFORMATION_REQUEST;
1205
1206 log_dhcp6_client(client, "Started in %s mode",
483d099e
ZJS
1207 client->information_request? "Information request":
1208 "Managed");
bbfa43ca
PF
1209
1210 return client_start(client, state);
1211
1212error:
1213 client_reset(client);
1214 return r;
139b011a
PF
1215}
1216
32d20645 1217int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
139b011a
PF
1218 int r;
1219
1220 assert_return(client, -EINVAL);
1221 assert_return(!client->event, -EBUSY);
1222
1223 if (event)
1224 client->event = sd_event_ref(event);
1225 else {
1226 r = sd_event_default(&client->event);
1227 if (r < 0)
1228 return 0;
1229 }
1230
1231 client->event_priority = priority;
1232
1233 return 0;
1234}
1235
1236int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1237 assert_return(client, -EINVAL);
1238
1239 client->event = sd_event_unref(client->event);
1240
1241 return 0;
1242}
1243
1244sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1245 if (!client)
1246 return NULL;
1247
1248 return client->event;
1249}
1250
1251sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
3733eec3
LP
1252
1253 if (!client)
1254 return NULL;
1255
1256 assert(client->n_ref >= 1);
1257 client->n_ref++;
139b011a
PF
1258
1259 return client;
1260}
1261
1262sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
139b011a 1263
3733eec3
LP
1264 if (!client)
1265 return NULL;
139b011a 1266
3733eec3
LP
1267 assert(client->n_ref >= 1);
1268 client->n_ref--;
139b011a 1269
3733eec3 1270 if (client->n_ref > 0)
139b011a 1271 return NULL;
139b011a 1272
3733eec3
LP
1273 client_reset(client);
1274
1275 sd_dhcp6_client_detach_event(client);
3733eec3
LP
1276
1277 free(client->req_opts);
1278 free(client);
1279
1280 return NULL;
139b011a
PF
1281}
1282
0ae0e5cd 1283int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
4afd3348 1284 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
da6fe470 1285 size_t t;
139b011a
PF
1286
1287 assert_return(ret, -EINVAL);
1288
1289 client = new0(sd_dhcp6_client, 1);
1290 if (!client)
1291 return -ENOMEM;
1292
3733eec3 1293 client->n_ref = 1;
139b011a 1294
2c1ab8ca 1295 client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
f12abb48 1296
139b011a
PF
1297 client->index = -1;
1298
c806ffb9
ZJS
1299 client->fd = -1;
1300
da6fe470
PF
1301 client->req_opts_len = ELEMENTSOF(default_req_opts);
1302
1303 client->req_opts = new0(be16_t, client->req_opts_len);
1304 if (!client->req_opts)
1305 return -ENOMEM;
1306
1307 for (t = 0; t < client->req_opts_len; t++)
1308 client->req_opts[t] = htobe16(default_req_opts[t]);
1309
139b011a
PF
1310 *ret = client;
1311 client = NULL;
1312
1313 return 0;
1314}