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