]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
dhcp6: read OPTION_INFORMATION_REFRESH_TIME option
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <errno.h>
7 #include <string.h>
8 #include <sys/ioctl.h>
9 #include <linux/if_arp.h>
10 #include <linux/if_infiniband.h>
11
12 #include "sd-dhcp6-client.h"
13
14 #include "alloc-util.h"
15 #include "dhcp-identifier.h"
16 #include "dhcp6-internal.h"
17 #include "dhcp6-lease-internal.h"
18 #include "dhcp6-protocol.h"
19 #include "dns-domain.h"
20 #include "event-util.h"
21 #include "fd-util.h"
22 #include "hostname-util.h"
23 #include "in-addr-util.h"
24 #include "network-internal.h"
25 #include "random-util.h"
26 #include "socket-util.h"
27 #include "string-table.h"
28 #include "util.h"
29
30 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
31
32 #define IRT_DEFAULT 1 * USEC_PER_DAY
33 #define IRT_MINIMUM 600 * USEC_PER_SEC
34
35 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
36 enum {
37 DHCP6_REQUEST_IA_NA = 1,
38 DHCP6_REQUEST_IA_TA = 2, /* currently not used */
39 DHCP6_REQUEST_IA_PD = 4,
40 };
41
42 struct sd_dhcp6_client {
43 unsigned n_ref;
44
45 enum DHCP6State state;
46 sd_event *event;
47 int event_priority;
48 int ifindex;
49 struct in6_addr local_address;
50 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
51 size_t mac_addr_len;
52 uint16_t arp_type;
53 DHCP6IA ia_na;
54 DHCP6IA ia_pd;
55 sd_event_source *timeout_t1;
56 sd_event_source *timeout_t2;
57 unsigned request;
58 be32_t transaction_id;
59 usec_t transaction_start;
60 struct sd_dhcp6_lease *lease;
61 int fd;
62 bool information_request;
63 bool iaid_set;
64 be16_t *req_opts;
65 size_t req_opts_allocated;
66 size_t req_opts_len;
67 char *fqdn;
68 sd_event_source *receive_message;
69 usec_t retransmit_time;
70 uint8_t retransmit_count;
71 sd_event_source *timeout_resend;
72 sd_event_source *timeout_resend_expire;
73 sd_dhcp6_client_callback_t callback;
74 void *userdata;
75 struct duid duid;
76 size_t duid_len;
77 usec_t information_request_time_usec;
78 usec_t information_refresh_time_usec;
79 };
80
81 static const uint16_t default_req_opts[] = {
82 SD_DHCP6_OPTION_DNS_SERVERS,
83 SD_DHCP6_OPTION_DOMAIN_LIST,
84 SD_DHCP6_OPTION_NTP_SERVER,
85 SD_DHCP6_OPTION_SNTP_SERVERS,
86 };
87
88 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
89 [DHCP6_SOLICIT] = "SOLICIT",
90 [DHCP6_ADVERTISE] = "ADVERTISE",
91 [DHCP6_REQUEST] = "REQUEST",
92 [DHCP6_CONFIRM] = "CONFIRM",
93 [DHCP6_RENEW] = "RENEW",
94 [DHCP6_REBIND] = "REBIND",
95 [DHCP6_REPLY] = "REPLY",
96 [DHCP6_RELEASE] = "RELEASE",
97 [DHCP6_DECLINE] = "DECLINE",
98 [DHCP6_RECONFIGURE] = "RECONFIGURE",
99 [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
100 [DHCP6_RELAY_FORW] = "RELAY-FORW",
101 [DHCP6_RELAY_REPL] = "RELAY-REPL",
102 };
103
104 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
105
106 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
107 [DHCP6_STATUS_SUCCESS] = "Success",
108 [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
109 [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
110 [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
111 [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
112 [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
113 };
114
115 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
116
117 #define DHCP6_CLIENT_DONT_DESTROY(client) \
118 _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
119
120 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
121
122 int sd_dhcp6_client_set_callback(
123 sd_dhcp6_client *client,
124 sd_dhcp6_client_callback_t cb,
125 void *userdata) {
126
127 assert_return(client, -EINVAL);
128
129 client->callback = cb;
130 client->userdata = userdata;
131
132 return 0;
133 }
134
135 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
136
137 assert_return(client, -EINVAL);
138 assert_return(ifindex >= -1, -EINVAL);
139 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
140
141 client->ifindex = ifindex;
142 return 0;
143 }
144
145 int sd_dhcp6_client_set_local_address(
146 sd_dhcp6_client *client,
147 const struct in6_addr *local_address) {
148
149 assert_return(client, -EINVAL);
150 assert_return(local_address, -EINVAL);
151 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
152
153 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
154
155 client->local_address = *local_address;
156
157 return 0;
158 }
159
160 int sd_dhcp6_client_set_mac(
161 sd_dhcp6_client *client,
162 const uint8_t *addr, size_t addr_len,
163 uint16_t arp_type) {
164
165 assert_return(client, -EINVAL);
166 assert_return(addr, -EINVAL);
167 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
168 assert_return(arp_type > 0, -EINVAL);
169
170 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
171
172 if (arp_type == ARPHRD_ETHER)
173 assert_return(addr_len == ETH_ALEN, -EINVAL);
174 else if (arp_type == ARPHRD_INFINIBAND)
175 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
176 else
177 return -EINVAL;
178
179 if (client->mac_addr_len == addr_len &&
180 memcmp(&client->mac_addr, addr, addr_len) == 0)
181 return 0;
182
183 memcpy(&client->mac_addr, addr, addr_len);
184 client->mac_addr_len = addr_len;
185 client->arp_type = arp_type;
186
187 return 0;
188 }
189
190 static int client_ensure_duid(sd_dhcp6_client *client) {
191 if (client->duid_len != 0)
192 return 0;
193
194 return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
195 }
196
197 /**
198 * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
199 * without further modification. Otherwise, if duid_type is supported, DUID
200 * is set based on that type. Otherwise, an error is returned.
201 */
202 static int dhcp6_client_set_duid_internal(
203 sd_dhcp6_client *client,
204 uint16_t duid_type,
205 const void *duid,
206 size_t duid_len,
207 usec_t llt_time) {
208 int r;
209
210 assert_return(client, -EINVAL);
211 assert_return(duid_len == 0 || duid != NULL, -EINVAL);
212 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
213
214 if (duid != NULL) {
215 r = dhcp_validate_duid_len(duid_type, duid_len, true);
216 if (r < 0) {
217 r = dhcp_validate_duid_len(duid_type, duid_len, false);
218 if (r < 0)
219 return r;
220 log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
221 }
222
223 client->duid.type = htobe16(duid_type);
224 memcpy(&client->duid.raw.data, duid, duid_len);
225 client->duid_len = sizeof(client->duid.type) + duid_len;
226 } else
227 switch (duid_type) {
228 case DUID_TYPE_LLT:
229 if (client->mac_addr_len == 0)
230 return -EOPNOTSUPP;
231
232 r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
233 if (r < 0)
234 return r;
235 break;
236 case DUID_TYPE_EN:
237 r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
238 if (r < 0)
239 return r;
240 break;
241 case DUID_TYPE_LL:
242 if (client->mac_addr_len == 0)
243 return -EOPNOTSUPP;
244
245 r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
246 if (r < 0)
247 return r;
248 break;
249 case DUID_TYPE_UUID:
250 r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
251 if (r < 0)
252 return r;
253 break;
254 default:
255 return -EINVAL;
256 }
257
258 return 0;
259 }
260
261 int sd_dhcp6_client_set_duid(
262 sd_dhcp6_client *client,
263 uint16_t duid_type,
264 const void *duid,
265 size_t duid_len) {
266 return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
267 }
268
269 int sd_dhcp6_client_set_duid_llt(
270 sd_dhcp6_client *client,
271 usec_t llt_time) {
272 return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
273 }
274
275 int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
276 assert_return(client, -EINVAL);
277 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
278
279 client->ia_na.ia_na.id = htobe32(iaid);
280 client->ia_pd.ia_pd.id = htobe32(iaid);
281 client->iaid_set = true;
282
283 return 0;
284 }
285
286 int sd_dhcp6_client_set_fqdn(
287 sd_dhcp6_client *client,
288 const char *fqdn) {
289
290 assert_return(client, -EINVAL);
291
292 /* Make sure FQDN qualifies as DNS and as Linux hostname */
293 if (fqdn &&
294 !(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0))
295 return -EINVAL;
296
297 return free_and_strdup(&client->fqdn, fqdn);
298 }
299
300 int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
301 assert_return(client, -EINVAL);
302 assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
303
304 client->information_request = enabled;
305
306 return 0;
307 }
308
309 int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
310 assert_return(client, -EINVAL);
311 assert_return(enabled, -EINVAL);
312
313 *enabled = client->information_request;
314
315 return 0;
316 }
317
318 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
319 size_t t;
320
321 assert_return(client, -EINVAL);
322 assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
323
324 switch(option) {
325
326 case SD_DHCP6_OPTION_DNS_SERVERS:
327 case SD_DHCP6_OPTION_DOMAIN_LIST:
328 case SD_DHCP6_OPTION_SNTP_SERVERS:
329 case SD_DHCP6_OPTION_NTP_SERVER:
330 case SD_DHCP6_OPTION_RAPID_COMMIT:
331 break;
332
333 default:
334 return -EINVAL;
335 }
336
337 for (t = 0; t < client->req_opts_len; t++)
338 if (client->req_opts[t] == htobe16(option))
339 return -EEXIST;
340
341 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
342 client->req_opts_len + 1))
343 return -ENOMEM;
344
345 client->req_opts[client->req_opts_len++] = htobe16(option);
346
347 return 0;
348 }
349
350 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
351 assert_return(client, -EINVAL);
352 assert_return(delegation, -EINVAL);
353
354 *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
355
356 return 0;
357 }
358
359 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
360 assert_return(client, -EINVAL);
361
362 SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
363
364 return 0;
365 }
366
367 int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
368 assert_return(client, -EINVAL);
369 assert_return(request, -EINVAL);
370
371 *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
372
373 return 0;
374 }
375
376 int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
377 assert_return(client, -EINVAL);
378
379 SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
380
381 return 0;
382 }
383
384 int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
385 assert_return(client, -EINVAL);
386
387 client->transaction_id = transaction_id;
388
389 return 0;
390 }
391
392 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
393 assert_return(client, -EINVAL);
394
395 if (!client->lease)
396 return -ENOMSG;
397
398 if (ret)
399 *ret = client->lease;
400
401 return 0;
402 }
403
404 static void client_notify(sd_dhcp6_client *client, int event) {
405 assert(client);
406
407 if (client->callback)
408 client->callback(client, event, client->userdata);
409 }
410
411 static int client_reset(sd_dhcp6_client *client) {
412 assert(client);
413
414 client->lease = sd_dhcp6_lease_unref(client->lease);
415
416 client->receive_message =
417 sd_event_source_unref(client->receive_message);
418
419 client->transaction_id = 0;
420 client->transaction_start = 0;
421
422 client->retransmit_time = 0;
423 client->retransmit_count = 0;
424
425 (void) event_source_disable(client->timeout_resend);
426 (void) event_source_disable(client->timeout_resend_expire);
427 (void) event_source_disable(client->timeout_t1);
428 (void) event_source_disable(client->timeout_t2);
429
430 client->state = DHCP6_STATE_STOPPED;
431
432 return 0;
433 }
434
435 static void client_stop(sd_dhcp6_client *client, int error) {
436 DHCP6_CLIENT_DONT_DESTROY(client);
437
438 assert(client);
439
440 client_notify(client, error);
441
442 client_reset(client);
443 }
444
445 static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
446 _cleanup_free_ DHCP6Message *message = NULL;
447 struct in6_addr all_servers =
448 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
449 size_t len, optlen = 512;
450 uint8_t *opt;
451 int r;
452 usec_t elapsed_usec;
453 be16_t elapsed_time;
454
455 assert(client);
456
457 len = sizeof(DHCP6Message) + optlen;
458
459 message = malloc0(len);
460 if (!message)
461 return -ENOMEM;
462
463 opt = (uint8_t *)(message + 1);
464
465 message->transaction_id = client->transaction_id;
466
467 switch(client->state) {
468 case DHCP6_STATE_INFORMATION_REQUEST:
469 message->type = DHCP6_INFORMATION_REQUEST;
470
471 break;
472
473 case DHCP6_STATE_SOLICITATION:
474 message->type = DHCP6_SOLICIT;
475
476 r = dhcp6_option_append(&opt, &optlen,
477 SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
478 if (r < 0)
479 return r;
480
481 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
482 r = dhcp6_option_append_ia(&opt, &optlen,
483 &client->ia_na);
484 if (r < 0)
485 return r;
486 }
487
488 if (client->fqdn) {
489 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
490 if (r < 0)
491 return r;
492 }
493
494 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
495 r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
496 if (r < 0)
497 return r;
498
499 opt += r;
500 optlen -= r;
501 }
502
503 break;
504
505 case DHCP6_STATE_REQUEST:
506 case DHCP6_STATE_RENEW:
507
508 if (client->state == DHCP6_STATE_REQUEST)
509 message->type = DHCP6_REQUEST;
510 else
511 message->type = DHCP6_RENEW;
512
513 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
514 client->lease->serverid_len,
515 client->lease->serverid);
516 if (r < 0)
517 return r;
518
519 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
520 r = dhcp6_option_append_ia(&opt, &optlen,
521 &client->lease->ia);
522 if (r < 0)
523 return r;
524 }
525
526 if (client->fqdn) {
527 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
528 if (r < 0)
529 return r;
530 }
531
532 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
533 r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
534 if (r < 0)
535 return r;
536
537 opt += r;
538 optlen -= r;
539 }
540
541 break;
542
543 case DHCP6_STATE_REBIND:
544 message->type = DHCP6_REBIND;
545
546 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
547 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
548 if (r < 0)
549 return r;
550 }
551
552 if (client->fqdn) {
553 r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
554 if (r < 0)
555 return r;
556 }
557
558 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
559 r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
560 if (r < 0)
561 return r;
562
563 opt += r;
564 optlen -= r;
565 }
566
567 break;
568
569 case DHCP6_STATE_STOPPED:
570 case DHCP6_STATE_BOUND:
571 return -EINVAL;
572 }
573
574 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
575 client->req_opts_len * sizeof(be16_t),
576 client->req_opts);
577 if (r < 0)
578 return r;
579
580 assert(client->duid_len);
581 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
582 client->duid_len, &client->duid);
583 if (r < 0)
584 return r;
585
586 elapsed_usec = time_now - client->transaction_start;
587 if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
588 elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
589 else
590 elapsed_time = 0xffff;
591
592 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
593 sizeof(elapsed_time), &elapsed_time);
594 if (r < 0)
595 return r;
596
597 r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
598 len - optlen);
599 if (r < 0)
600 return r;
601
602 log_dhcp6_client(client, "Sent %s",
603 dhcp6_message_type_to_string(message->type));
604
605 return 0;
606 }
607
608 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
609 sd_dhcp6_client *client = userdata;
610
611 assert(s);
612 assert(client);
613 assert(client->lease);
614
615 (void) event_source_disable(client->timeout_t2);
616
617 log_dhcp6_client(client, "Timeout T2");
618
619 client_start(client, DHCP6_STATE_REBIND);
620
621 return 0;
622 }
623
624 static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
625 sd_dhcp6_client *client = userdata;
626
627 assert(s);
628 assert(client);
629 assert(client->lease);
630
631 (void) event_source_disable(client->timeout_t1);
632
633 log_dhcp6_client(client, "Timeout T1");
634
635 client_start(client, DHCP6_STATE_RENEW);
636
637 return 0;
638 }
639
640 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
641 sd_dhcp6_client *client = userdata;
642 DHCP6_CLIENT_DONT_DESTROY(client);
643 enum DHCP6State state;
644
645 assert(s);
646 assert(client);
647 assert(client->event);
648
649 state = client->state;
650
651 client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
652
653 /* RFC 3315, section 18.1.4., says that "...the client may choose to
654 use a Solicit message to locate a new DHCP server..." */
655 if (state == DHCP6_STATE_REBIND)
656 client_start(client, DHCP6_STATE_SOLICITATION);
657
658 return 0;
659 }
660
661 static usec_t client_timeout_compute_random(usec_t val) {
662 return val - val / 10 +
663 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
664 }
665
666 static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
667 int r = 0;
668 sd_dhcp6_client *client = userdata;
669 usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
670 usec_t max_retransmit_duration = 0;
671 uint8_t max_retransmit_count = 0;
672 char time_string[FORMAT_TIMESPAN_MAX];
673 uint32_t expire = 0;
674
675 assert(s);
676 assert(client);
677 assert(client->event);
678
679 (void) event_source_disable(client->timeout_resend);
680
681 switch (client->state) {
682 case DHCP6_STATE_INFORMATION_REQUEST:
683 init_retransmit_time = DHCP6_INF_TIMEOUT;
684 max_retransmit_time = DHCP6_INF_MAX_RT;
685
686 break;
687
688 case DHCP6_STATE_SOLICITATION:
689
690 if (client->retransmit_count && client->lease) {
691 client_start(client, DHCP6_STATE_REQUEST);
692 return 0;
693 }
694
695 init_retransmit_time = DHCP6_SOL_TIMEOUT;
696 max_retransmit_time = DHCP6_SOL_MAX_RT;
697
698 break;
699
700 case DHCP6_STATE_REQUEST:
701 init_retransmit_time = DHCP6_REQ_TIMEOUT;
702 max_retransmit_time = DHCP6_REQ_MAX_RT;
703 max_retransmit_count = DHCP6_REQ_MAX_RC;
704
705 break;
706
707 case DHCP6_STATE_RENEW:
708 init_retransmit_time = DHCP6_REN_TIMEOUT;
709 max_retransmit_time = DHCP6_REN_MAX_RT;
710
711 /* RFC 3315, section 18.1.3. says max retransmit duration will
712 be the remaining time until T2. Instead of setting MRD,
713 wait for T2 to trigger with the same end result */
714
715 break;
716
717 case DHCP6_STATE_REBIND:
718 init_retransmit_time = DHCP6_REB_TIMEOUT;
719 max_retransmit_time = DHCP6_REB_MAX_RT;
720
721 if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
722 r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
723 &expire);
724 if (r < 0) {
725 client_stop(client, r);
726 return 0;
727 }
728 max_retransmit_duration = expire * USEC_PER_SEC;
729 }
730
731 break;
732
733 case DHCP6_STATE_STOPPED:
734 case DHCP6_STATE_BOUND:
735 return 0;
736 }
737
738 if (max_retransmit_count &&
739 client->retransmit_count >= max_retransmit_count) {
740 client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
741 return 0;
742 }
743
744 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
745 if (r < 0)
746 goto error;
747
748 r = client_send_message(client, time_now);
749 if (r >= 0)
750 client->retransmit_count++;
751
752 if (!client->retransmit_time) {
753 client->retransmit_time =
754 client_timeout_compute_random(init_retransmit_time);
755
756 if (client->state == DHCP6_STATE_SOLICITATION)
757 client->retransmit_time += init_retransmit_time / 10;
758
759 } else {
760 if (max_retransmit_time &&
761 client->retransmit_time > max_retransmit_time / 2)
762 client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
763 else
764 client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
765 }
766
767 log_dhcp6_client(client, "Next retransmission in %s",
768 format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
769
770 r = event_reset_time(client->event, &client->timeout_resend,
771 clock_boottime_or_monotonic(),
772 time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
773 client_timeout_resend, client,
774 client->event_priority, "dhcp6-resend-timer", true);
775 if (r < 0)
776 goto error;
777
778 if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
779
780 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
781 max_retransmit_duration / USEC_PER_SEC);
782
783 r = event_reset_time(client->event, &client->timeout_resend_expire,
784 clock_boottime_or_monotonic(),
785 time_now + max_retransmit_duration, USEC_PER_SEC,
786 client_timeout_resend_expire, client,
787 client->event_priority, "dhcp6-resend-expire-timer", true);
788 if (r < 0)
789 goto error;
790 }
791
792 error:
793 if (r < 0)
794 client_stop(client, r);
795
796 return 0;
797 }
798
799 static int client_ensure_iaid(sd_dhcp6_client *client) {
800 int r;
801 uint32_t iaid;
802
803 assert(client);
804
805 if (client->iaid_set)
806 return 0;
807
808 r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
809 if (r < 0)
810 return r;
811
812 client->ia_na.ia_na.id = iaid;
813 client->ia_pd.ia_pd.id = iaid;
814 client->iaid_set = true;
815
816 return 0;
817 }
818
819 static int client_parse_message(
820 sd_dhcp6_client *client,
821 DHCP6Message *message,
822 size_t len,
823 sd_dhcp6_lease *lease) {
824
825 uint32_t lt_t1 = ~0, lt_t2 = ~0;
826 bool clientid = false;
827 size_t pos = 0;
828 usec_t irt = IRT_DEFAULT;
829 int r;
830
831 assert(client);
832 assert(message);
833 assert(len >= sizeof(DHCP6Message));
834 assert(lease);
835
836 len -= sizeof(DHCP6Message);
837
838 while (pos < len) {
839 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
840 uint16_t optcode, optlen;
841 be32_t iaid_lease;
842 uint8_t *optval;
843 int status;
844
845 if (len < pos + offsetof(DHCP6Option, data))
846 return -ENOBUFS;
847
848 optcode = be16toh(option->code);
849 optlen = be16toh(option->len);
850 optval = option->data;
851
852 if (len < pos + offsetof(DHCP6Option, data) + optlen)
853 return -ENOBUFS;
854
855 switch (optcode) {
856 case SD_DHCP6_OPTION_CLIENTID:
857 if (clientid) {
858 log_dhcp6_client(client, "%s contains multiple clientids",
859 dhcp6_message_type_to_string(message->type));
860 return -EINVAL;
861 }
862
863 if (optlen != client->duid_len ||
864 memcmp(&client->duid, optval, optlen) != 0) {
865 log_dhcp6_client(client, "%s DUID does not match",
866 dhcp6_message_type_to_string(message->type));
867
868 return -EINVAL;
869 }
870 clientid = true;
871
872 break;
873
874 case SD_DHCP6_OPTION_SERVERID:
875 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
876 if (r >= 0) {
877 log_dhcp6_client(client, "%s contains multiple serverids",
878 dhcp6_message_type_to_string(message->type));
879 return -EINVAL;
880 }
881
882 r = dhcp6_lease_set_serverid(lease, optval, optlen);
883 if (r < 0)
884 return r;
885
886 break;
887
888 case SD_DHCP6_OPTION_PREFERENCE:
889 if (optlen != 1)
890 return -EINVAL;
891
892 r = dhcp6_lease_set_preference(lease, optval[0]);
893 if (r < 0)
894 return r;
895
896 break;
897
898 case SD_DHCP6_OPTION_STATUS_CODE:
899 status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
900 if (status < 0)
901 return status;
902
903 if (status > 0) {
904 log_dhcp6_client(client, "%s Status %s",
905 dhcp6_message_type_to_string(message->type),
906 dhcp6_message_status_to_string(status));
907
908 return -EINVAL;
909 }
910
911 break;
912
913 case SD_DHCP6_OPTION_IA_NA:
914 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
915 log_dhcp6_client(client, "Information request ignoring IA NA option");
916
917 break;
918 }
919
920 r = dhcp6_option_parse_ia(option, &lease->ia);
921 if (r < 0 && r != -ENOMSG)
922 return r;
923
924 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
925 if (r < 0)
926 return r;
927
928 if (client->ia_na.ia_na.id != iaid_lease) {
929 log_dhcp6_client(client, "%s has wrong IAID for IA NA",
930 dhcp6_message_type_to_string(message->type));
931 return -EINVAL;
932 }
933
934 if (lease->ia.addresses) {
935 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
936 lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
937 }
938
939 break;
940
941 case SD_DHCP6_OPTION_IA_PD:
942 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
943 log_dhcp6_client(client, "Information request ignoring IA PD option");
944
945 break;
946 }
947
948 r = dhcp6_option_parse_ia(option, &lease->pd);
949 if (r < 0 && r != -ENOMSG)
950 return r;
951
952 r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
953 if (r < 0)
954 return r;
955
956 if (client->ia_pd.ia_pd.id != iaid_lease) {
957 log_dhcp6_client(client, "%s has wrong IAID for IA PD",
958 dhcp6_message_type_to_string(message->type));
959 return -EINVAL;
960 }
961
962 if (lease->pd.addresses) {
963 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
964 lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
965 }
966
967 break;
968
969 case SD_DHCP6_OPTION_RAPID_COMMIT:
970 r = dhcp6_lease_set_rapid_commit(lease);
971 if (r < 0)
972 return r;
973
974 break;
975
976 case SD_DHCP6_OPTION_DNS_SERVERS:
977 r = dhcp6_lease_set_dns(lease, optval, optlen);
978 if (r < 0)
979 return r;
980
981 break;
982
983 case SD_DHCP6_OPTION_DOMAIN_LIST:
984 r = dhcp6_lease_set_domains(lease, optval, optlen);
985 if (r < 0)
986 return r;
987
988 break;
989
990 case SD_DHCP6_OPTION_NTP_SERVER:
991 r = dhcp6_lease_set_ntp(lease, optval, optlen);
992 if (r < 0)
993 return r;
994
995 break;
996
997 case SD_DHCP6_OPTION_SNTP_SERVERS:
998 r = dhcp6_lease_set_sntp(lease, optval, optlen);
999 if (r < 0)
1000 return r;
1001
1002 break;
1003
1004 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
1005 irt = be32toh(*(be32_t *) optval) * USEC_PER_SEC;
1006 break;
1007 }
1008
1009 pos += offsetof(DHCP6Option, data) + optlen;
1010 }
1011
1012 if (!clientid) {
1013 log_dhcp6_client(client, "%s has incomplete options",
1014 dhcp6_message_type_to_string(message->type));
1015 return -EINVAL;
1016 }
1017
1018 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
1019 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1020 if (r < 0) {
1021 log_dhcp6_client(client, "%s has no server id",
1022 dhcp6_message_type_to_string(message->type));
1023 return -EINVAL;
1024 }
1025
1026 } else {
1027 if (lease->ia.addresses) {
1028 lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
1029 lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
1030 }
1031
1032 if (lease->pd.addresses) {
1033 lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
1034 lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
1035 }
1036 }
1037
1038 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
1039
1040 return 0;
1041 }
1042
1043 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
1044 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1045 bool rapid_commit;
1046 int r;
1047
1048 assert(client);
1049 assert(reply);
1050
1051 if (reply->type != DHCP6_REPLY)
1052 return 0;
1053
1054 r = dhcp6_lease_new(&lease);
1055 if (r < 0)
1056 return -ENOMEM;
1057
1058 r = client_parse_message(client, reply, len, lease);
1059 if (r < 0)
1060 return r;
1061
1062 if (client->state == DHCP6_STATE_SOLICITATION) {
1063 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1064 if (r < 0)
1065 return r;
1066
1067 if (!rapid_commit)
1068 return 0;
1069 }
1070
1071 sd_dhcp6_lease_unref(client->lease);
1072 client->lease = TAKE_PTR(lease);
1073
1074 return DHCP6_STATE_BOUND;
1075 }
1076
1077 static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
1078 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1079 uint8_t pref_advertise = 0, pref_lease = 0;
1080 int r;
1081
1082 if (advertise->type != DHCP6_ADVERTISE)
1083 return 0;
1084
1085 r = dhcp6_lease_new(&lease);
1086 if (r < 0)
1087 return r;
1088
1089 r = client_parse_message(client, advertise, len, lease);
1090 if (r < 0)
1091 return r;
1092
1093 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1094 if (r < 0)
1095 return r;
1096
1097 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
1098
1099 if (r < 0 || pref_advertise > pref_lease) {
1100 sd_dhcp6_lease_unref(client->lease);
1101 client->lease = TAKE_PTR(lease);
1102 r = 0;
1103 }
1104
1105 if (pref_advertise == 255 || client->retransmit_count > 1)
1106 r = DHCP6_STATE_REQUEST;
1107
1108 return r;
1109 }
1110
1111 static int client_receive_message(
1112 sd_event_source *s,
1113 int fd, uint32_t
1114 revents,
1115 void *userdata) {
1116
1117 sd_dhcp6_client *client = userdata;
1118 DHCP6_CLIENT_DONT_DESTROY(client);
1119 _cleanup_free_ DHCP6Message *message = NULL;
1120 ssize_t buflen, len;
1121 int r = 0;
1122
1123 assert(s);
1124 assert(client);
1125 assert(client->event);
1126
1127 buflen = next_datagram_size_fd(fd);
1128 if (buflen == -ENETDOWN) {
1129 /* the link is down. Don't return an error or the I/O event
1130 source will be disconnected and we won't be able to receive
1131 packets again when the link comes back. */
1132 return 0;
1133 }
1134 if (buflen < 0)
1135 return buflen;
1136
1137 message = malloc(buflen);
1138 if (!message)
1139 return -ENOMEM;
1140
1141 len = recv(fd, message, buflen, 0);
1142 if (len < 0) {
1143 /* see comment above for why we shouldn't error out on ENETDOWN. */
1144 if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
1145 return 0;
1146
1147 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
1148
1149 }
1150 if ((size_t) len < sizeof(DHCP6Message)) {
1151 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
1152 return 0;
1153 }
1154
1155 switch(message->type) {
1156 case DHCP6_SOLICIT:
1157 case DHCP6_REQUEST:
1158 case DHCP6_CONFIRM:
1159 case DHCP6_RENEW:
1160 case DHCP6_REBIND:
1161 case DHCP6_RELEASE:
1162 case DHCP6_DECLINE:
1163 case DHCP6_INFORMATION_REQUEST:
1164 case DHCP6_RELAY_FORW:
1165 case DHCP6_RELAY_REPL:
1166 return 0;
1167
1168 case DHCP6_ADVERTISE:
1169 case DHCP6_REPLY:
1170 case DHCP6_RECONFIGURE:
1171 break;
1172
1173 default:
1174 log_dhcp6_client(client, "Unknown message type %d", message->type);
1175 return 0;
1176 }
1177
1178 if (client->transaction_id != (message->transaction_id &
1179 htobe32(0x00ffffff)))
1180 return 0;
1181
1182 switch (client->state) {
1183 case DHCP6_STATE_INFORMATION_REQUEST:
1184 r = client_receive_reply(client, message, len);
1185 if (r < 0)
1186 return 0;
1187
1188 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
1189
1190 client_start(client, DHCP6_STATE_STOPPED);
1191
1192 break;
1193
1194 case DHCP6_STATE_SOLICITATION:
1195 r = client_receive_advertise(client, message, len);
1196
1197 if (r == DHCP6_STATE_REQUEST) {
1198 client_start(client, r);
1199
1200 break;
1201 }
1202
1203 _fallthrough_; /* for Soliciation Rapid Commit option check */
1204 case DHCP6_STATE_REQUEST:
1205 case DHCP6_STATE_RENEW:
1206 case DHCP6_STATE_REBIND:
1207
1208 r = client_receive_reply(client, message, len);
1209 if (r < 0)
1210 return 0;
1211
1212 if (r == DHCP6_STATE_BOUND) {
1213
1214 r = client_start(client, DHCP6_STATE_BOUND);
1215 if (r < 0) {
1216 client_stop(client, r);
1217 return 0;
1218 }
1219
1220 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
1221 }
1222
1223 break;
1224
1225 case DHCP6_STATE_BOUND:
1226
1227 break;
1228
1229 case DHCP6_STATE_STOPPED:
1230 return 0;
1231 }
1232
1233 log_dhcp6_client(client, "Recv %s",
1234 dhcp6_message_type_to_string(message->type));
1235
1236 return 0;
1237 }
1238
1239 static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
1240 uint32_t *lifetime_t2) {
1241 assert_return(client, -EINVAL);
1242 assert_return(client->lease, -EINVAL);
1243
1244 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
1245 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
1246 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
1247
1248 return 0;
1249 }
1250
1251 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
1252 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
1253 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
1254
1255 return 0;
1256 }
1257
1258 return -ENOMSG;
1259 }
1260
1261 static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
1262 int r;
1263 usec_t timeout, time_now;
1264 char time_string[FORMAT_TIMESPAN_MAX];
1265 uint32_t lifetime_t1, lifetime_t2;
1266
1267 assert_return(client, -EINVAL);
1268 assert_return(client->event, -EINVAL);
1269 assert_return(client->ifindex > 0, -EINVAL);
1270 assert_return(client->state != state, -EINVAL);
1271
1272 (void) event_source_disable(client->timeout_resend_expire);
1273 (void) event_source_disable(client->timeout_resend);
1274 client->retransmit_time = 0;
1275 client->retransmit_count = 0;
1276
1277 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1278 if (r < 0)
1279 return r;
1280
1281 if (!client->receive_message) {
1282 r = sd_event_add_io(client->event, &client->receive_message,
1283 client->fd, EPOLLIN, client_receive_message,
1284 client);
1285 if (r < 0)
1286 goto error;
1287
1288 r = sd_event_source_set_priority(client->receive_message,
1289 client->event_priority);
1290 if (r < 0)
1291 goto error;
1292
1293 r = sd_event_source_set_description(client->receive_message,
1294 "dhcp6-receive-message");
1295 if (r < 0)
1296 goto error;
1297 }
1298
1299 switch (state) {
1300 case DHCP6_STATE_STOPPED:
1301 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1302 client->state = DHCP6_STATE_STOPPED;
1303
1304 return 0;
1305 }
1306
1307 _fallthrough_;
1308 case DHCP6_STATE_SOLICITATION:
1309 client->state = DHCP6_STATE_SOLICITATION;
1310
1311 break;
1312
1313 case DHCP6_STATE_INFORMATION_REQUEST:
1314 case DHCP6_STATE_REQUEST:
1315 case DHCP6_STATE_RENEW:
1316 case DHCP6_STATE_REBIND:
1317
1318 client->state = state;
1319
1320 break;
1321
1322 case DHCP6_STATE_BOUND:
1323
1324 r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
1325 if (r < 0)
1326 goto error;
1327
1328 if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
1329 log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
1330 lifetime_t1, lifetime_t2);
1331
1332 return 0;
1333 }
1334
1335 timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
1336
1337 log_dhcp6_client(client, "T1 expires in %s",
1338 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1339
1340 r = event_reset_time(client->event, &client->timeout_t1,
1341 clock_boottime_or_monotonic(),
1342 time_now + timeout, 10 * USEC_PER_SEC,
1343 client_timeout_t1, client,
1344 client->event_priority, "dhcp6-t1-timeout", true);
1345 if (r < 0)
1346 goto error;
1347
1348 timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
1349
1350 log_dhcp6_client(client, "T2 expires in %s",
1351 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1352
1353 r = event_reset_time(client->event, &client->timeout_t2,
1354 clock_boottime_or_monotonic(),
1355 time_now + timeout, 10 * USEC_PER_SEC,
1356 client_timeout_t2, client,
1357 client->event_priority, "dhcp6-t2-timeout", true);
1358 if (r < 0)
1359 goto error;
1360
1361 client->state = state;
1362
1363 return 0;
1364 }
1365
1366 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1367 client->transaction_start = time_now;
1368
1369 r = event_reset_time(client->event, &client->timeout_resend,
1370 clock_boottime_or_monotonic(),
1371 0, 0,
1372 client_timeout_resend, client,
1373 client->event_priority, "dhcp6-resend-timeout", true);
1374 if (r < 0)
1375 goto error;
1376
1377 return 0;
1378
1379 error:
1380 client_reset(client);
1381 return r;
1382 }
1383
1384 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1385 assert_return(client, -EINVAL);
1386
1387 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1388
1389 client->fd = safe_close(client->fd);
1390
1391 return 0;
1392 }
1393
1394 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1395 assert_return(client, -EINVAL);
1396
1397 return client->state != DHCP6_STATE_STOPPED;
1398 }
1399
1400 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1401 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
1402 int r = 0;
1403
1404 assert_return(client, -EINVAL);
1405 assert_return(client->event, -EINVAL);
1406 assert_return(client->ifindex > 0, -EINVAL);
1407 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
1408
1409 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
1410 return -EBUSY;
1411
1412 if (!client->information_request && !client->request)
1413 return -EINVAL;
1414
1415 r = client_reset(client);
1416 if (r < 0)
1417 return r;
1418
1419 r = client_ensure_iaid(client);
1420 if (r < 0)
1421 return r;
1422
1423 r = client_ensure_duid(client);
1424 if (r < 0)
1425 return r;
1426
1427 if (client->fd < 0) {
1428 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1429 if (r < 0) {
1430 _cleanup_free_ char *p = NULL;
1431
1432 (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1433 return log_dhcp6_client_errno(client, r,
1434 "Failed to bind to UDP socket at address %s: %m", strna(p));
1435 }
1436
1437 client->fd = r;
1438 }
1439
1440 if (client->information_request) {
1441 usec_t t = now(CLOCK_MONOTONIC);
1442
1443 if (t < usec_add(client->information_request_time_usec, client->information_refresh_time_usec))
1444 return 0;
1445
1446 client->information_request_time_usec = t;
1447 state = DHCP6_STATE_INFORMATION_REQUEST;
1448 }
1449
1450 log_dhcp6_client(client, "Started in %s mode",
1451 client->information_request? "Information request":
1452 "Managed");
1453
1454 return client_start(client, state);
1455 }
1456
1457 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
1458 int r;
1459
1460 assert_return(client, -EINVAL);
1461 assert_return(!client->event, -EBUSY);
1462
1463 if (event)
1464 client->event = sd_event_ref(event);
1465 else {
1466 r = sd_event_default(&client->event);
1467 if (r < 0)
1468 return 0;
1469 }
1470
1471 client->event_priority = priority;
1472
1473 return 0;
1474 }
1475
1476 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1477 assert_return(client, -EINVAL);
1478
1479 client->event = sd_event_unref(client->event);
1480
1481 return 0;
1482 }
1483
1484 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1485 assert_return(client, NULL);
1486
1487 return client->event;
1488 }
1489
1490 static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1491 assert(client);
1492
1493 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1494 client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
1495 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1496 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1497
1498 client_reset(client);
1499
1500 client->fd = safe_close(client->fd);
1501
1502 sd_dhcp6_client_detach_event(client);
1503
1504 free(client->req_opts);
1505 free(client->fqdn);
1506 return mfree(client);
1507 }
1508
1509 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1510
1511 int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1512 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1513 _cleanup_free_ be16_t *req_opts = NULL;
1514 size_t t;
1515
1516 assert_return(ret, -EINVAL);
1517
1518 req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
1519 if (!req_opts)
1520 return -ENOMEM;
1521
1522 for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
1523 req_opts[t] = htobe16(default_req_opts[t]);
1524
1525 client = new(sd_dhcp6_client, 1);
1526 if (!client)
1527 return -ENOMEM;
1528
1529 *client = (sd_dhcp6_client) {
1530 .n_ref = 1,
1531 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1532 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1533 .ifindex = -1,
1534 .request = DHCP6_REQUEST_IA_NA,
1535 .fd = -1,
1536 .req_opts_len = ELEMENTSOF(default_req_opts),
1537 .req_opts = TAKE_PTR(req_opts),
1538 };
1539
1540 *ret = TAKE_PTR(client);
1541
1542 return 0;
1543 }