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