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