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