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