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