]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-lease.c
dhcp: introduce sd_dhcp_lease_get_timestamp()
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp6-lease.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
3fb2c570 2/***
810adae9 3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
3fb2c570
PF
4***/
5
6#include <errno.h>
7
b5efdb8a 8#include "alloc-util.h"
3f09d563 9#include "dhcp6-internal.h"
3fb2c570 10#include "dhcp6-lease-internal.h"
b5efdb8a 11#include "strv.h"
3fb2c570 12
65ece4c8
YW
13#define IRT_DEFAULT (1 * USEC_PER_DAY)
14#define IRT_MINIMUM (600 * USEC_PER_SEC)
15
16static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) {
17 assert(lease);
18
19 if (timestamp && triple_timestamp_is_set(timestamp))
20 lease->timestamp = *timestamp;
21 else
22 triple_timestamp_get(&lease->timestamp);
23}
24
653ddc1d
YW
25int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
26 assert_return(lease, -EINVAL);
27 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
28 assert_return(clock_supported(clock), -EOPNOTSUPP);
29 assert_return(ret, -EINVAL);
30
31 if (!triple_timestamp_is_set(&lease->timestamp))
32 return -ENODATA;
33
34 *ret = triple_timestamp_by_clock(&lease->timestamp, clock);
35 return 0;
36}
37
3bb18e70
YW
38static usec_t sec2usec(uint32_t sec) {
39 return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
40}
41
65ece4c8 42static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
f4fbea7a 43 uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
ef4edc15 44
f4fbea7a
YW
45 assert(lease);
46 assert(lease->ia_na || lease->ia_pd);
ef4edc15 47
f4fbea7a
YW
48 if (lease->ia_na) {
49 t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1));
50 t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2));
709d6710 51
f4fbea7a
YW
52 LIST_FOREACH(addresses, a, lease->ia_na->addresses)
53 min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
54 }
55
56 if (lease->ia_pd) {
57 t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
58 t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
59
60 LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
61 min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
62 }
709d6710 63
f4fbea7a
YW
64 if (t2 == 0 || t2 > min_valid_lt) {
65 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
66 * then adjust lifetime with it. */
67 t1 = min_valid_lt / 2;
68 t2 = min_valid_lt / 10 * 8;
709d6710
PF
69 }
70
3bb18e70
YW
71 lease->lifetime_valid = sec2usec(min_valid_lt);
72 lease->lifetime_t1 = sec2usec(t1);
73 lease->lifetime_t2 = sec2usec(t2);
f4fbea7a
YW
74}
75
3bb18e70 76int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) {
f4fbea7a
YW
77 assert(lease);
78
79 if (!lease->ia_na && !lease->ia_pd)
80 return -ENODATA;
81
82 if (ret_t1)
83 *ret_t1 = lease->lifetime_t1;
84 if (ret_t2)
85 *ret_t2 = lease->lifetime_t2;
3bb18e70
YW
86 if (ret_valid)
87 *ret_valid = lease->lifetime_valid;
f4fbea7a
YW
88 return 0;
89}
709d6710 90
65ece4c8
YW
91static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
92 assert(lease);
93
94 if (server_address)
95 lease->server_address = *server_address;
96 else
97 lease->server_address = (struct in6_addr) {};
98}
99
f4fbea7a
YW
100int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
101 assert_return(lease, -EINVAL);
102 assert_return(ret, -EINVAL);
709d6710 103
f4fbea7a 104 *ret = lease->server_address;
709d6710
PF
105 return 0;
106}
107
e5b0b87f 108void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
e5b0b87f 109 assert(ia);
3fb2c570 110
80a226b2 111 LIST_FOREACH(addresses, a, ia->addresses)
e5b0b87f 112 free(a);
3fb2c570 113
e5b0b87f
YW
114 ia->addresses = NULL;
115}
3fb2c570 116
e5b0b87f
YW
117DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
118 if (!ia)
119 return NULL;
3fb2c570 120
e5b0b87f
YW
121 dhcp6_ia_clear_addresses(ia);
122
123 return mfree(ia);
3fb2c570
PF
124}
125
e79b4b85 126int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
93bd7c41 127 uint8_t *clientid = NULL;
e79b4b85 128
049fddfa 129 assert(lease);
93bd7c41 130 assert(id || len == 0);
e79b4b85 131
93bd7c41
YW
132 if (len > 0) {
133 clientid = memdup(id, len);
134 if (!clientid)
135 return -ENOMEM;
136 }
e79b4b85
YW
137
138 free_and_replace(lease->clientid, clientid);
139 lease->clientid_len = len;
140
141 return 0;
142}
143
144int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
049fddfa 145 assert(lease);
e79b4b85
YW
146
147 if (!lease->clientid)
148 return -ENODATA;
149
150 if (ret_id)
151 *ret_id = lease->clientid;
152 if (ret_len)
153 *ret_len = lease->clientid_len;
154
155 return 0;
156}
157
4f81f0d2 158int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
93bd7c41 159 uint8_t *serverid = NULL;
33d36758 160
049fddfa 161 assert(lease);
93bd7c41 162 assert(id || len == 0);
3fb2c570 163
93bd7c41
YW
164 if (len > 0) {
165 serverid = memdup(id, len);
166 if (!serverid)
167 return -ENOMEM;
168 }
3fb2c570 169
33d36758 170 free_and_replace(lease->serverid, serverid);
3fb2c570
PF
171 lease->serverid_len = len;
172
173 return 0;
174}
175
4f81f0d2 176int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
049fddfa 177 assert(lease);
3fb2c570 178
99f1d3fc 179 if (!lease->serverid)
4f81f0d2 180 return -ENODATA;
99f1d3fc 181
4f81f0d2
YW
182 if (ret_id)
183 *ret_id = lease->serverid;
184 if (ret_len)
185 *ret_len = lease->serverid_len;
3fb2c570
PF
186 return 0;
187}
188
189int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
049fddfa 190 assert(lease);
3fb2c570
PF
191
192 lease->preference = preference;
3fb2c570
PF
193 return 0;
194}
195
049fddfa
YW
196int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
197 assert(lease);
198 assert(ret);
3fb2c570 199
049fddfa 200 *ret = lease->preference;
3fb2c570
PF
201 return 0;
202}
203
ed6ee219 204int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
049fddfa 205 assert(lease);
ed6ee219
PF
206
207 lease->rapid_commit = true;
ed6ee219
PF
208 return 0;
209}
210
049fddfa
YW
211int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
212 assert(lease);
213 assert(ret);
ed6ee219 214
049fddfa 215 *ret = lease->rapid_commit;
ed6ee219
PF
216 return 0;
217}
218
049fddfa
YW
219int sd_dhcp6_lease_get_address(
220 sd_dhcp6_lease *lease,
221 struct in6_addr *ret_addr,
222 uint32_t *ret_lifetime_preferred,
223 uint32_t *ret_lifetime_valid) {
224
ea3b3a75 225 assert_return(lease, -EINVAL);
ea3b3a75
PF
226
227 if (!lease->addr_iter)
126277ac 228 return -ENODATA;
ea3b3a75 229
049fddfa
YW
230 if (ret_addr)
231 *ret_addr = lease->addr_iter->iaaddr.address;
232 if (ret_lifetime_preferred)
233 *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
234 if (ret_lifetime_valid)
235 *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
ea3b3a75
PF
236
237 lease->addr_iter = lease->addr_iter->addresses_next;
ea3b3a75
PF
238 return 0;
239}
240
e7504d95
PF
241void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
242 if (lease)
e5b0b87f 243 lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
ea3b3a75
PF
244}
245
049fddfa
YW
246int sd_dhcp6_lease_get_pd(
247 sd_dhcp6_lease *lease,
248 struct in6_addr *ret_prefix,
249 uint8_t *ret_prefix_len,
250 uint32_t *ret_lifetime_preferred,
251 uint32_t *ret_lifetime_valid) {
252
652bf042 253 assert_return(lease, -EINVAL);
652bf042
PF
254
255 if (!lease->prefix_iter)
126277ac 256 return -ENODATA;
652bf042 257
049fddfa
YW
258 if (ret_prefix)
259 *ret_prefix = lease->prefix_iter->iapdprefix.address;
260 if (ret_prefix_len)
261 *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
262 if (ret_lifetime_preferred)
263 *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
264 if (ret_lifetime_valid)
265 *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
652bf042
PF
266
267 lease->prefix_iter = lease->prefix_iter->addresses_next;
652bf042
PF
268 return 0;
269}
270
271void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
272 if (lease)
e5b0b87f 273 lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
652bf042
PF
274}
275
e210f027 276int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
049fddfa
YW
277 assert(lease);
278 assert(optval || optlen == 0);
7bd8e95d 279
ad3c8420 280 if (optlen == 0)
7bd8e95d
PF
281 return 0;
282
ad3c8420 283 return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
7bd8e95d
PF
284}
285
edeee50b 286int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
7bd8e95d 287 assert_return(lease, -EINVAL);
7bd8e95d 288
edeee50b 289 if (!lease->dns)
126277ac 290 return -ENODATA;
7bd8e95d 291
5656ff9d
YW
292 if (ret)
293 *ret = lease->dns;
294
edeee50b 295 return lease->dns_count;
7bd8e95d
PF
296}
297
41b14f03
YW
298int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
299 _cleanup_strv_free_ char **domains = NULL;
5da1b97f 300 int r;
5da1b97f 301
049fddfa
YW
302 assert(lease);
303 assert(optval || optlen == 0);
5da1b97f 304
b27dcf08 305 if (optlen == 0)
5da1b97f
PF
306 return 0;
307
c43eea9f 308 r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
5da1b97f 309 if (r < 0)
b27dcf08 310 return r;
5da1b97f 311
41b14f03 312 return strv_extend_strv(&lease->domains, domains, true);
5da1b97f
PF
313}
314
edeee50b 315int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
5da1b97f 316 assert_return(lease, -EINVAL);
edeee50b 317 assert_return(ret, -EINVAL);
5da1b97f 318
edeee50b 319 if (!lease->domains)
126277ac 320 return -ENODATA;
5da1b97f 321
edeee50b
YW
322 *ret = lease->domains;
323 return strv_length(lease->domains);
5da1b97f
PF
324}
325
9c3d46bf 326int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
6599680e 327 int r;
6599680e 328
049fddfa
YW
329 assert(lease);
330 assert(optval || optlen == 0);
6599680e 331
b89a3758
YW
332 for (size_t offset = 0; offset < optlen;) {
333 const uint8_t *subval;
334 size_t sublen;
335 uint16_t subopt;
336
337 r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
338 if (r < 0)
339 return r;
6599680e 340
79893116 341 switch (subopt) {
6599680e
PF
342 case DHCP6_NTP_SUBOPTION_SRV_ADDR:
343 case DHCP6_NTP_SUBOPTION_MC_ADDR:
344 if (sublen != 16)
e261d315 345 return -EINVAL;
6599680e 346
ad3c8420 347 r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
b89a3758
YW
348 if (r < 0)
349 return r;
6599680e 350
6599680e
PF
351 break;
352
b89a3758 353 case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
9c3d46bf 354 _cleanup_free_ char *server = NULL;
b89a3758 355
9c3d46bf 356 r = dhcp6_option_parse_domainname(subval, sublen, &server);
6599680e 357 if (r < 0)
b27dcf08 358 return r;
6599680e 359
9c3d46bf
YW
360 if (strv_contains(lease->ntp_fqdn, server))
361 continue;
362
363 r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
364 if (r < 0)
365 return r;
6599680e
PF
366
367 break;
b89a3758 368 }}
6599680e
PF
369 }
370
6599680e
PF
371 return 0;
372}
373
e693e969 374int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
049fddfa
YW
375 assert(lease);
376 assert(optval || optlen == 0);
41e4615d 377
ad3c8420 378 if (optlen == 0)
41e4615d
PF
379 return 0;
380
e693e969
YW
381 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
382 return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
41e4615d
PF
383}
384
edeee50b 385int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
6599680e 386 assert_return(lease, -EINVAL);
6599680e 387
e693e969 388 if (lease->ntp) {
5656ff9d
YW
389 if (ret)
390 *ret = lease->ntp;
e693e969
YW
391 return lease->ntp_count;
392 }
393
394 if (lease->sntp && !lease->ntp_fqdn) {
395 /* Fallback to the deprecated SNTP option. */
5656ff9d
YW
396 if (ret)
397 *ret = lease->sntp;
e693e969
YW
398 return lease->sntp_count;
399 }
6599680e 400
126277ac 401 return -ENODATA;
6599680e
PF
402}
403
edeee50b 404int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
6599680e 405 assert_return(lease, -EINVAL);
6599680e 406
edeee50b 407 if (!lease->ntp_fqdn)
126277ac 408 return -ENODATA;
6599680e 409
5656ff9d
YW
410 if (ret)
411 *ret = lease->ntp_fqdn;
edeee50b 412 return strv_length(lease->ntp_fqdn);
6599680e
PF
413}
414
3f8227bf 415int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
c43eea9f 416 char *fqdn;
049fddfa 417 int r;
c43eea9f 418
049fddfa
YW
419 assert(lease);
420 assert(optval || optlen == 0);
421
422 if (optlen == 0)
423 return 0;
c43eea9f
BG
424
425 if (optlen < 2)
426 return -ENODATA;
427
428 /* Ignore the flags field, it doesn't carry any useful
429 information for clients. */
430 r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
431 if (r < 0)
432 return r;
433
434 return free_and_replace(lease->fqdn, fqdn);
435}
436
edeee50b 437int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
c43eea9f 438 assert_return(lease, -EINVAL);
edeee50b 439 assert_return(ret, -EINVAL);
c43eea9f 440
edeee50b 441 if (!lease->fqdn)
126277ac 442 return -ENODATA;
c43eea9f 443
edeee50b
YW
444 *ret = lease->fqdn;
445 return 0;
c43eea9f
BG
446}
447
fde78860
RP
448int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
449 _cleanup_free_ char *uri = NULL;
450 int r;
451
452 assert(lease);
453 assert(optval || optlen == 0);
454
455 r = dhcp6_option_parse_string(optval, optlen, &uri);
456 if (r < 0)
457 return r;
458
459 if (uri && !in_charset(uri, URI_VALID))
460 return -EINVAL;
461
462 return free_and_replace(lease->captive_portal, uri);
463}
464
465int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
466 assert_return(lease, -EINVAL);
467 assert_return(ret, -EINVAL);
468
469 if (!lease->captive_portal)
470 return -ENODATA;
471
472 *ret = lease->captive_portal;
473 return 0;
474}
475
65ece4c8
YW
476static int dhcp6_lease_parse_message(
477 sd_dhcp6_client *client,
478 sd_dhcp6_lease *lease,
479 const DHCP6Message *message,
480 size_t len) {
481
482 usec_t irt = IRT_DEFAULT;
483 int r;
484
485 assert(client);
486 assert(lease);
487 assert(message);
488 assert(len >= sizeof(DHCP6Message));
489
490 len -= sizeof(DHCP6Message);
491 for (size_t offset = 0; offset < len;) {
492 uint16_t optcode;
493 size_t optlen;
494 const uint8_t *optval;
495
81b73359
PK
496 if (len - offset < offsetof(DHCP6Option, data)) {
497 log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
498 break;
499 }
500
65ece4c8
YW
501 r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
502 if (r < 0)
50309ff7
YW
503 return log_dhcp6_client_errno(client, r,
504 "Failed to parse option header at offset %zu of total length %zu: %m",
505 offset, len);
65ece4c8
YW
506
507 switch (optcode) {
508 case SD_DHCP6_OPTION_CLIENTID:
509 if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
510 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
511 dhcp6_message_type_to_string(message->type));
512
513 r = dhcp6_lease_set_clientid(lease, optval, optlen);
514 if (r < 0)
50309ff7 515 return log_dhcp6_client_errno(client, r, "Failed to set client ID: %m");
65ece4c8
YW
516
517 break;
518
519 case SD_DHCP6_OPTION_SERVERID:
520 if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
521 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
522 dhcp6_message_type_to_string(message->type));
523
524 r = dhcp6_lease_set_serverid(lease, optval, optlen);
525 if (r < 0)
50309ff7 526 return log_dhcp6_client_errno(client, r, "Failed to set server ID: %m");
65ece4c8
YW
527
528 break;
529
530 case SD_DHCP6_OPTION_PREFERENCE:
531 if (optlen != 1)
50309ff7 532 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received invalid length for preference.");
65ece4c8
YW
533
534 r = dhcp6_lease_set_preference(lease, optval[0]);
535 if (r < 0)
50309ff7 536 return log_dhcp6_client_errno(client, r, "Failed to set preference: %m");
65ece4c8
YW
537
538 break;
539
540 case SD_DHCP6_OPTION_STATUS_CODE: {
541 _cleanup_free_ char *msg = NULL;
542
543 r = dhcp6_option_parse_status(optval, optlen, &msg);
544 if (r < 0)
50309ff7 545 return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
65ece4c8 546 if (r > 0)
1929c1fc 547 return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
ea9dbf51 548 "Received %s message with non-zero status%s%s",
65ece4c8 549 dhcp6_message_type_to_string(message->type),
ea9dbf51 550 isempty(msg) ? "." : ": ", strempty(msg));
65ece4c8
YW
551 break;
552 }
553 case SD_DHCP6_OPTION_IA_NA: {
554 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
555
556 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
557 log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
558 break;
559 }
560
561 r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
562 if (r == -ENOMEM)
50309ff7
YW
563 return log_oom_debug();
564 if (r < 0) {
565 log_dhcp6_client_errno(client, r, "Failed to parse IA_NA option, ignoring: %m");
65ece4c8 566 continue;
50309ff7 567 }
65ece4c8
YW
568
569 if (lease->ia_na) {
570 log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
571 continue;
572 }
573
574 dhcp6_ia_free(lease->ia_na);
575 lease->ia_na = TAKE_PTR(ia);
576 break;
577 }
578 case SD_DHCP6_OPTION_IA_PD: {
579 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
580
581 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
582 log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
583 break;
584 }
585
586 r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
587 if (r == -ENOMEM)
50309ff7
YW
588 return log_oom_debug();
589 if (r < 0) {
590 log_dhcp6_client_errno(client, r, "Failed to parse IA_PD option, ignoring: %m");
65ece4c8 591 continue;
50309ff7 592 }
65ece4c8
YW
593
594 if (lease->ia_pd) {
595 log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
596 continue;
597 }
598
599 dhcp6_ia_free(lease->ia_pd);
600 lease->ia_pd = TAKE_PTR(ia);
601 break;
602 }
603 case SD_DHCP6_OPTION_RAPID_COMMIT:
50309ff7
YW
604 if (optlen != 0)
605 log_dhcp6_client(client, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen);
606
65ece4c8
YW
607 r = dhcp6_lease_set_rapid_commit(lease);
608 if (r < 0)
50309ff7 609 return log_dhcp6_client_errno(client, r, "Failed to set rapid commit flag: %m");
65ece4c8
YW
610
611 break;
612
f697ab35 613 case SD_DHCP6_OPTION_DNS_SERVER:
65ece4c8
YW
614 r = dhcp6_lease_add_dns(lease, optval, optlen);
615 if (r < 0)
616 log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
617
618 break;
619
f697ab35 620 case SD_DHCP6_OPTION_DOMAIN:
65ece4c8
YW
621 r = dhcp6_lease_add_domains(lease, optval, optlen);
622 if (r < 0)
623 log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
624
625 break;
626
627 case SD_DHCP6_OPTION_NTP_SERVER:
628 r = dhcp6_lease_add_ntp(lease, optval, optlen);
629 if (r < 0)
630 log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
631
632 break;
633
f697ab35 634 case SD_DHCP6_OPTION_SNTP_SERVER:
65ece4c8
YW
635 r = dhcp6_lease_add_sntp(lease, optval, optlen);
636 if (r < 0)
637 log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
638
639 break;
640
fde78860
RP
641 case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
642 r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
643 if (r < 0)
644 log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
645 break;
646
65ece4c8
YW
647 case SD_DHCP6_OPTION_CLIENT_FQDN:
648 r = dhcp6_lease_set_fqdn(lease, optval, optlen);
649 if (r < 0)
650 log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
651
652 break;
653
654 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
655 if (optlen != 4)
50309ff7
YW
656 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
657 "Received information refresh time option with an invalid length (%zu).", optlen);
65ece4c8 658
6759b627 659 irt = unaligned_read_be32(optval) * USEC_PER_SEC;
65ece4c8
YW
660 break;
661 }
662 }
663
664 uint8_t *clientid;
665 size_t clientid_len;
666 if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
9c9fee80
YW
667 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
668 "%s message does not contain client ID. Ignoring.",
65ece4c8
YW
669 dhcp6_message_type_to_string(message->type));
670
9c9fee80
YW
671 if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
672 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
673 "The client ID in %s message does not match. Ignoring.",
65ece4c8
YW
674 dhcp6_message_type_to_string(message->type));
675
e1774086
YW
676 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
677 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
678 log_dhcp6_client(client, "New information request will be refused in %s.",
679 FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
680
681 } else {
65ece4c8
YW
682 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
683 if (r < 0)
684 return log_dhcp6_client_errno(client, r, "%s has no server id",
685 dhcp6_message_type_to_string(message->type));
686
687 if (!lease->ia_na && !lease->ia_pd)
9c9fee80
YW
688 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
689 "No IA_PD prefix or IA_NA address received. Ignoring.");
65ece4c8
YW
690
691 dhcp6_lease_set_lifetime(lease);
692 }
693
65ece4c8
YW
694 return 0;
695}
696
8301aa0b 697static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
5cf67bb4
YW
698 if (!lease)
699 return NULL;
6599680e 700
e79b4b85 701 free(lease->clientid);
3733eec3 702 free(lease->serverid);
e5b0b87f
YW
703 dhcp6_ia_free(lease->ia_na);
704 dhcp6_ia_free(lease->ia_pd);
3733eec3 705 free(lease->dns);
c43eea9f 706 free(lease->fqdn);
fde78860 707 free(lease->captive_portal);
5cf67bb4 708 strv_free(lease->domains);
3733eec3 709 free(lease->ntp);
5cf67bb4 710 strv_free(lease->ntp_fqdn);
e693e969 711 free(lease->sntp);
3733eec3 712
6b430fdb 713 return mfree(lease);
3fb2c570
PF
714}
715
8301aa0b
YW
716DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
717
3fb2c570
PF
718int dhcp6_lease_new(sd_dhcp6_lease **ret) {
719 sd_dhcp6_lease *lease;
720
049fddfa
YW
721 assert(ret);
722
c9309258 723 lease = new(sd_dhcp6_lease, 1);
3fb2c570
PF
724 if (!lease)
725 return -ENOMEM;
726
c9309258
YW
727 *lease = (sd_dhcp6_lease) {
728 .n_ref = 1,
729 };
3fb2c570 730
3fb2c570
PF
731 *ret = lease;
732 return 0;
733}
65ece4c8
YW
734
735int dhcp6_lease_new_from_message(
736 sd_dhcp6_client *client,
737 const DHCP6Message *message,
738 size_t len,
739 const triple_timestamp *timestamp,
740 const struct in6_addr *server_address,
741 sd_dhcp6_lease **ret) {
742
743 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
744 int r;
745
746 assert(client);
747 assert(message);
748 assert(len >= sizeof(DHCP6Message));
749 assert(ret);
750
751 r = dhcp6_lease_new(&lease);
752 if (r < 0)
753 return r;
754
755 dhcp6_lease_set_timestamp(lease, timestamp);
756 dhcp6_lease_set_server_address(lease, server_address);
757
758 r = dhcp6_lease_parse_message(client, lease, message, len);
759 if (r < 0)
760 return r;
761
762 *ret = TAKE_PTR(lease);
763 return 0;
764}