]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
Merge pull request #10094 from keszybz/wants-loading
[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 size_t pos = 0;
833 int r;
834 bool clientid = false;
835 uint32_t lt_t1 = ~0, lt_t2 = ~0;
836
837 assert(client);
838 assert(message);
839 assert(len >= sizeof(DHCP6Message));
840 assert(lease);
841
842 len -= sizeof(DHCP6Message);
843
844 while (pos < len) {
845 DHCP6Option *option = (DHCP6Option *)&message->options[pos];
846 uint16_t optcode, optlen;
847 int status;
848 uint8_t *optval;
849 be32_t iaid_lease;
850
851 if (len < pos + offsetof(DHCP6Option, data) ||
852 len < pos + offsetof(DHCP6Option, data) + be16toh(option->len))
853 return -ENOBUFS;
854
855 optcode = be16toh(option->code);
856 optlen = be16toh(option->len);
857 optval = option->data;
858
859 switch (optcode) {
860 case SD_DHCP6_OPTION_CLIENTID:
861 if (clientid) {
862 log_dhcp6_client(client, "%s contains multiple clientids",
863 dhcp6_message_type_to_string(message->type));
864 return -EINVAL;
865 }
866
867 if (optlen != client->duid_len ||
868 memcmp(&client->duid, optval, optlen) != 0) {
869 log_dhcp6_client(client, "%s DUID does not match",
870 dhcp6_message_type_to_string(message->type));
871
872 return -EINVAL;
873 }
874 clientid = true;
875
876 break;
877
878 case SD_DHCP6_OPTION_SERVERID:
879 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
880 if (r >= 0) {
881 log_dhcp6_client(client, "%s contains multiple serverids",
882 dhcp6_message_type_to_string(message->type));
883 return -EINVAL;
884 }
885
886 r = dhcp6_lease_set_serverid(lease, optval, optlen);
887 if (r < 0)
888 return r;
889
890 break;
891
892 case SD_DHCP6_OPTION_PREFERENCE:
893 if (optlen != 1)
894 return -EINVAL;
895
896 r = dhcp6_lease_set_preference(lease, optval[0]);
897 if (r < 0)
898 return r;
899
900 break;
901
902 case SD_DHCP6_OPTION_STATUS_CODE:
903 status = dhcp6_option_parse_status(option, optlen);
904 if (status) {
905 log_dhcp6_client(client, "%s Status %s",
906 dhcp6_message_type_to_string(message->type),
907 dhcp6_message_status_to_string(status));
908 dhcp6_lease_free_ia(&lease->ia);
909 dhcp6_lease_free_ia(&lease->pd);
910
911 return -EINVAL;
912 }
913
914 break;
915
916 case SD_DHCP6_OPTION_IA_NA:
917 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
918 log_dhcp6_client(client, "Information request ignoring IA NA option");
919
920 break;
921 }
922
923 r = dhcp6_option_parse_ia(option, &lease->ia);
924 if (r < 0 && r != -ENOMSG)
925 return r;
926
927 r = dhcp6_lease_get_iaid(lease, &iaid_lease);
928 if (r < 0)
929 return r;
930
931 if (client->ia_na.ia_na.id != iaid_lease) {
932 log_dhcp6_client(client, "%s has wrong IAID for IA NA",
933 dhcp6_message_type_to_string(message->type));
934 return -EINVAL;
935 }
936
937 if (lease->ia.addresses) {
938 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
939 lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
940 }
941
942 break;
943
944 case SD_DHCP6_OPTION_IA_PD:
945 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
946 log_dhcp6_client(client, "Information request ignoring IA PD option");
947
948 break;
949 }
950
951 r = dhcp6_option_parse_ia(option, &lease->pd);
952 if (r < 0 && r != -ENOMSG)
953 return r;
954
955 r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
956 if (r < 0)
957 return r;
958
959 if (client->ia_pd.ia_pd.id != iaid_lease) {
960 log_dhcp6_client(client, "%s has wrong IAID for IA PD",
961 dhcp6_message_type_to_string(message->type));
962 return -EINVAL;
963 }
964
965 if (lease->pd.addresses) {
966 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
967 lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
968 }
969
970 break;
971
972 case SD_DHCP6_OPTION_RAPID_COMMIT:
973 r = dhcp6_lease_set_rapid_commit(lease);
974 if (r < 0)
975 return r;
976
977 break;
978
979 case SD_DHCP6_OPTION_DNS_SERVERS:
980 r = dhcp6_lease_set_dns(lease, optval, optlen);
981 if (r < 0)
982 return r;
983
984 break;
985
986 case SD_DHCP6_OPTION_DOMAIN_LIST:
987 r = dhcp6_lease_set_domains(lease, optval, optlen);
988 if (r < 0)
989 return r;
990
991 break;
992
993 case SD_DHCP6_OPTION_NTP_SERVER:
994 r = dhcp6_lease_set_ntp(lease, optval, optlen);
995 if (r < 0)
996 return r;
997
998 break;
999
1000 case SD_DHCP6_OPTION_SNTP_SERVERS:
1001 r = dhcp6_lease_set_sntp(lease, optval, optlen);
1002 if (r < 0)
1003 return r;
1004
1005 break;
1006 }
1007
1008 pos += offsetof(DHCP6Option, data) + optlen;
1009 }
1010
1011 if (!clientid) {
1012 log_dhcp6_client(client, "%s has incomplete options",
1013 dhcp6_message_type_to_string(message->type));
1014 return -EINVAL;
1015 }
1016
1017 if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
1018 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1019 if (r < 0) {
1020 log_dhcp6_client(client, "%s has no server id",
1021 dhcp6_message_type_to_string(message->type));
1022 return -EINVAL;
1023 }
1024
1025 } else {
1026 if (lease->ia.addresses) {
1027 lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
1028 lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
1029 }
1030
1031 if (lease->pd.addresses) {
1032 lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
1033 lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
1034 }
1035 }
1036
1037 return 0;
1038 }
1039
1040 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
1041 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1042 bool rapid_commit;
1043 int r;
1044
1045 assert(client);
1046 assert(reply);
1047
1048 if (reply->type != DHCP6_REPLY)
1049 return 0;
1050
1051 r = dhcp6_lease_new(&lease);
1052 if (r < 0)
1053 return -ENOMEM;
1054
1055 r = client_parse_message(client, reply, len, lease);
1056 if (r < 0)
1057 return r;
1058
1059 if (client->state == DHCP6_STATE_SOLICITATION) {
1060 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1061 if (r < 0)
1062 return r;
1063
1064 if (!rapid_commit)
1065 return 0;
1066 }
1067
1068 sd_dhcp6_lease_unref(client->lease);
1069 client->lease = TAKE_PTR(lease);
1070
1071 return DHCP6_STATE_BOUND;
1072 }
1073
1074 static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
1075 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1076 uint8_t pref_advertise = 0, pref_lease = 0;
1077 int r;
1078
1079 if (advertise->type != DHCP6_ADVERTISE)
1080 return 0;
1081
1082 r = dhcp6_lease_new(&lease);
1083 if (r < 0)
1084 return r;
1085
1086 r = client_parse_message(client, advertise, len, lease);
1087 if (r < 0)
1088 return r;
1089
1090 r = dhcp6_lease_get_preference(lease, &pref_advertise);
1091 if (r < 0)
1092 return r;
1093
1094 r = dhcp6_lease_get_preference(client->lease, &pref_lease);
1095
1096 if (r < 0 || pref_advertise > pref_lease) {
1097 sd_dhcp6_lease_unref(client->lease);
1098 client->lease = TAKE_PTR(lease);
1099 r = 0;
1100 }
1101
1102 if (pref_advertise == 255 || client->retransmit_count > 1)
1103 r = DHCP6_STATE_REQUEST;
1104
1105 return r;
1106 }
1107
1108 static int client_receive_message(
1109 sd_event_source *s,
1110 int fd, uint32_t
1111 revents,
1112 void *userdata) {
1113
1114 sd_dhcp6_client *client = userdata;
1115 DHCP6_CLIENT_DONT_DESTROY(client);
1116 _cleanup_free_ DHCP6Message *message = NULL;
1117 ssize_t buflen, len;
1118 int r = 0;
1119
1120 assert(s);
1121 assert(client);
1122 assert(client->event);
1123
1124 buflen = next_datagram_size_fd(fd);
1125 if (buflen < 0)
1126 return buflen;
1127
1128 message = malloc(buflen);
1129 if (!message)
1130 return -ENOMEM;
1131
1132 len = recv(fd, message, buflen, 0);
1133 if (len < 0) {
1134 if (IN_SET(errno, EAGAIN, EINTR))
1135 return 0;
1136
1137 return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
1138
1139 }
1140 if ((size_t) len < sizeof(DHCP6Message)) {
1141 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
1142 return 0;
1143 }
1144
1145 switch(message->type) {
1146 case DHCP6_SOLICIT:
1147 case DHCP6_REQUEST:
1148 case DHCP6_CONFIRM:
1149 case DHCP6_RENEW:
1150 case DHCP6_REBIND:
1151 case DHCP6_RELEASE:
1152 case DHCP6_DECLINE:
1153 case DHCP6_INFORMATION_REQUEST:
1154 case DHCP6_RELAY_FORW:
1155 case DHCP6_RELAY_REPL:
1156 return 0;
1157
1158 case DHCP6_ADVERTISE:
1159 case DHCP6_REPLY:
1160 case DHCP6_RECONFIGURE:
1161 break;
1162
1163 default:
1164 log_dhcp6_client(client, "Unknown message type %d", message->type);
1165 return 0;
1166 }
1167
1168 if (client->transaction_id != (message->transaction_id &
1169 htobe32(0x00ffffff)))
1170 return 0;
1171
1172 switch (client->state) {
1173 case DHCP6_STATE_INFORMATION_REQUEST:
1174 r = client_receive_reply(client, message, len);
1175 if (r < 0)
1176 return 0;
1177
1178 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
1179
1180 client_start(client, DHCP6_STATE_STOPPED);
1181
1182 break;
1183
1184 case DHCP6_STATE_SOLICITATION:
1185 r = client_receive_advertise(client, message, len);
1186
1187 if (r == DHCP6_STATE_REQUEST) {
1188 client_start(client, r);
1189
1190 break;
1191 }
1192
1193 _fallthrough_; /* for Soliciation Rapid Commit option check */
1194 case DHCP6_STATE_REQUEST:
1195 case DHCP6_STATE_RENEW:
1196 case DHCP6_STATE_REBIND:
1197
1198 r = client_receive_reply(client, message, len);
1199 if (r < 0)
1200 return 0;
1201
1202 if (r == DHCP6_STATE_BOUND) {
1203
1204 r = client_start(client, DHCP6_STATE_BOUND);
1205 if (r < 0) {
1206 client_stop(client, r);
1207 return 0;
1208 }
1209
1210 client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
1211 }
1212
1213 break;
1214
1215 case DHCP6_STATE_BOUND:
1216
1217 break;
1218
1219 case DHCP6_STATE_STOPPED:
1220 return 0;
1221 }
1222
1223 log_dhcp6_client(client, "Recv %s",
1224 dhcp6_message_type_to_string(message->type));
1225
1226 return 0;
1227 }
1228
1229 static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
1230 uint32_t *lifetime_t2) {
1231 assert_return(client, -EINVAL);
1232 assert_return(client->lease, -EINVAL);
1233
1234 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
1235 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
1236 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
1237
1238 return 0;
1239 }
1240
1241 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
1242 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
1243 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
1244
1245 return 0;
1246 }
1247
1248 return -ENOMSG;
1249 }
1250
1251 static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
1252 int r;
1253 usec_t timeout, time_now;
1254 char time_string[FORMAT_TIMESPAN_MAX];
1255 uint32_t lifetime_t1, lifetime_t2;
1256
1257 assert_return(client, -EINVAL);
1258 assert_return(client->event, -EINVAL);
1259 assert_return(client->ifindex > 0, -EINVAL);
1260 assert_return(client->state != state, -EINVAL);
1261
1262 client->timeout_resend_expire =
1263 sd_event_source_unref(client->timeout_resend_expire);
1264 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1265 client->retransmit_time = 0;
1266 client->retransmit_count = 0;
1267
1268 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1269 if (r < 0)
1270 return r;
1271
1272 if (!client->receive_message) {
1273 r = sd_event_add_io(client->event, &client->receive_message,
1274 client->fd, EPOLLIN, client_receive_message,
1275 client);
1276 if (r < 0)
1277 goto error;
1278
1279 r = sd_event_source_set_priority(client->receive_message,
1280 client->event_priority);
1281 if (r < 0)
1282 goto error;
1283
1284 r = sd_event_source_set_description(client->receive_message,
1285 "dhcp6-receive-message");
1286 if (r < 0)
1287 goto error;
1288 }
1289
1290 switch (state) {
1291 case DHCP6_STATE_STOPPED:
1292 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1293 client->state = DHCP6_STATE_STOPPED;
1294
1295 return 0;
1296 }
1297
1298 _fallthrough_;
1299 case DHCP6_STATE_SOLICITATION:
1300 client->state = DHCP6_STATE_SOLICITATION;
1301
1302 break;
1303
1304 case DHCP6_STATE_INFORMATION_REQUEST:
1305 case DHCP6_STATE_REQUEST:
1306 case DHCP6_STATE_RENEW:
1307 case DHCP6_STATE_REBIND:
1308
1309 client->state = state;
1310
1311 break;
1312
1313 case DHCP6_STATE_BOUND:
1314
1315 r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
1316 if (r < 0)
1317 goto error;
1318
1319 if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
1320 log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
1321 lifetime_t1, lifetime_t2);
1322
1323 return 0;
1324 }
1325
1326 timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
1327
1328 log_dhcp6_client(client, "T1 expires in %s",
1329 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1330
1331 r = sd_event_add_time(client->event,
1332 &client->timeout_t1,
1333 clock_boottime_or_monotonic(), time_now + timeout,
1334 10 * USEC_PER_SEC, client_timeout_t1,
1335 client);
1336 if (r < 0)
1337 goto error;
1338
1339 r = sd_event_source_set_priority(client->timeout_t1,
1340 client->event_priority);
1341 if (r < 0)
1342 goto error;
1343
1344 r = sd_event_source_set_description(client->timeout_t1, "dhcp6-t1-timeout");
1345 if (r < 0)
1346 goto error;
1347
1348 timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
1349
1350 log_dhcp6_client(client, "T2 expires in %s",
1351 format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1352
1353 r = sd_event_add_time(client->event,
1354 &client->timeout_t2,
1355 clock_boottime_or_monotonic(), time_now + timeout,
1356 10 * USEC_PER_SEC, client_timeout_t2,
1357 client);
1358 if (r < 0)
1359 goto error;
1360
1361 r = sd_event_source_set_priority(client->timeout_t2,
1362 client->event_priority);
1363 if (r < 0)
1364 goto error;
1365
1366 r = sd_event_source_set_description(client->timeout_t2, "dhcp6-t2-timeout");
1367 if (r < 0)
1368 goto error;
1369
1370 client->state = state;
1371
1372 return 0;
1373 }
1374
1375 client->transaction_id = random_u32() & htobe32(0x00ffffff);
1376 client->transaction_start = time_now;
1377
1378 r = sd_event_add_time(client->event, &client->timeout_resend,
1379 clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1380 client);
1381 if (r < 0)
1382 goto error;
1383
1384 r = sd_event_source_set_priority(client->timeout_resend,
1385 client->event_priority);
1386 if (r < 0)
1387 goto error;
1388
1389 r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1390 if (r < 0)
1391 goto error;
1392
1393 return 0;
1394
1395 error:
1396 client_reset(client);
1397 return r;
1398 }
1399
1400 int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1401 assert_return(client, -EINVAL);
1402
1403 client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1404
1405 client->fd = safe_close(client->fd);
1406
1407 return 0;
1408 }
1409
1410 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1411 assert_return(client, -EINVAL);
1412
1413 return client->state != DHCP6_STATE_STOPPED;
1414 }
1415
1416 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1417 enum DHCP6State state = DHCP6_STATE_SOLICITATION;
1418 int r = 0;
1419
1420 assert_return(client, -EINVAL);
1421 assert_return(client->event, -EINVAL);
1422 assert_return(client->ifindex > 0, -EINVAL);
1423 assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
1424
1425 if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
1426 return -EBUSY;
1427
1428 if (!client->information_request && !client->request)
1429 return -EINVAL;
1430
1431 r = client_reset(client);
1432 if (r < 0)
1433 return r;
1434
1435 r = client_ensure_iaid(client);
1436 if (r < 0)
1437 return r;
1438
1439 r = client_ensure_duid(client);
1440 if (r < 0)
1441 return r;
1442
1443 if (client->fd < 0) {
1444 r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1445 if (r < 0) {
1446 _cleanup_free_ char *p = NULL;
1447
1448 (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1449 return log_dhcp6_client_errno(client, r,
1450 "Failed to bind to UDP socket at address %s: %m", strna(p));
1451 }
1452
1453 client->fd = r;
1454 }
1455
1456 if (client->information_request)
1457 state = DHCP6_STATE_INFORMATION_REQUEST;
1458
1459 log_dhcp6_client(client, "Started in %s mode",
1460 client->information_request? "Information request":
1461 "Managed");
1462
1463 return client_start(client, state);
1464 }
1465
1466 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
1467 int r;
1468
1469 assert_return(client, -EINVAL);
1470 assert_return(!client->event, -EBUSY);
1471
1472 if (event)
1473 client->event = sd_event_ref(event);
1474 else {
1475 r = sd_event_default(&client->event);
1476 if (r < 0)
1477 return 0;
1478 }
1479
1480 client->event_priority = priority;
1481
1482 return 0;
1483 }
1484
1485 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1486 assert_return(client, -EINVAL);
1487
1488 client->event = sd_event_unref(client->event);
1489
1490 return 0;
1491 }
1492
1493 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1494 assert_return(client, NULL);
1495
1496 return client->event;
1497 }
1498
1499 static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1500 assert(client);
1501
1502 client_reset(client);
1503
1504 client->fd = safe_close(client->fd);
1505
1506 sd_dhcp6_client_detach_event(client);
1507
1508 free(client->req_opts);
1509 free(client->fqdn);
1510 return mfree(client);
1511 }
1512
1513 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
1514
1515 int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1516 _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1517 size_t t;
1518
1519 assert_return(ret, -EINVAL);
1520
1521 client = new0(sd_dhcp6_client, 1);
1522 if (!client)
1523 return -ENOMEM;
1524
1525 client->n_ref = 1;
1526 client->ia_na.type = SD_DHCP6_OPTION_IA_NA;
1527 client->ia_pd.type = SD_DHCP6_OPTION_IA_PD;
1528 client->ifindex = -1;
1529 client->request = DHCP6_REQUEST_IA_NA;
1530 client->fd = -1;
1531
1532 client->req_opts_len = ELEMENTSOF(default_req_opts);
1533 client->req_opts = new0(be16_t, client->req_opts_len);
1534 if (!client->req_opts)
1535 return -ENOMEM;
1536
1537 for (t = 0; t < client->req_opts_len; t++)
1538 client->req_opts[t] = htobe16(default_req_opts[t]);
1539
1540 *ret = TAKE_PTR(client);
1541
1542 return 0;
1543 }