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