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