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