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