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