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