]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp6-lease.c
Merge pull request #32609 from systemd/dependabot/github_actions/github/super-linter-6
[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"
d8ec95c7 11#include "network-common.h"
b5efdb8a 12#include "strv.h"
c74d18e4 13#include "unaligned.h"
3fb2c570 14
65ece4c8
YW
15#define IRT_DEFAULT (1 * USEC_PER_DAY)
16#define IRT_MINIMUM (600 * USEC_PER_SEC)
17
18static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) {
19 assert(lease);
20
21 if (timestamp && triple_timestamp_is_set(timestamp))
22 lease->timestamp = *timestamp;
23 else
fa5a0251 24 triple_timestamp_now(&lease->timestamp);
65ece4c8
YW
25}
26
653ddc1d
YW
27int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
28 assert_return(lease, -EINVAL);
29 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
30 assert_return(clock_supported(clock), -EOPNOTSUPP);
31 assert_return(ret, -EINVAL);
32
33 if (!triple_timestamp_is_set(&lease->timestamp))
34 return -ENODATA;
35
36 *ret = triple_timestamp_by_clock(&lease->timestamp, clock);
37 return 0;
38}
39
65ece4c8 40static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
9132cbd5 41 usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY;
ef4edc15 42
f4fbea7a
YW
43 assert(lease);
44 assert(lease->ia_na || lease->ia_pd);
ef4edc15 45
f4fbea7a 46 if (lease->ia_na) {
9132cbd5
YW
47 t1 = MIN(t1, be32_sec_to_usec(lease->ia_na->header.lifetime_t1, /* max_as_infinity = */ true));
48 t2 = MIN(t2, be32_sec_to_usec(lease->ia_na->header.lifetime_t2, /* max_as_infinity = */ true));
709d6710 49
f4fbea7a 50 LIST_FOREACH(addresses, a, lease->ia_na->addresses)
9132cbd5 51 min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true));
f4fbea7a
YW
52 }
53
54 if (lease->ia_pd) {
9132cbd5
YW
55 t1 = MIN(t1, be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true));
56 t2 = MIN(t2, be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true));
f4fbea7a
YW
57
58 LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
9132cbd5 59 min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true));
f4fbea7a 60 }
709d6710 61
f4fbea7a
YW
62 if (t2 == 0 || t2 > min_valid_lt) {
63 /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
64 * then adjust lifetime with it. */
65 t1 = min_valid_lt / 2;
66 t2 = min_valid_lt / 10 * 8;
709d6710
PF
67 }
68
9132cbd5
YW
69 lease->lifetime_valid = min_valid_lt;
70 lease->lifetime_t1 = t1;
71 lease->lifetime_t2 = t2;
f4fbea7a
YW
72}
73
394fac52
YW
74#define DEFINE_GET_TIME_FUNCTIONS(name, val) \
75 int sd_dhcp6_lease_get_##name( \
76 sd_dhcp6_lease *lease, \
77 uint64_t *ret) { \
78 \
79 assert_return(lease, -EINVAL); \
80 \
81 if (!lease->ia_na && !lease->ia_pd) \
82 return -ENODATA; \
83 \
84 if (ret) \
85 *ret = lease->val; \
86 return 0; \
87 } \
88 \
89 int sd_dhcp6_lease_get_##name##_timestamp( \
90 sd_dhcp6_lease *lease, \
91 clockid_t clock, \
92 uint64_t *ret) { \
93 \
94 usec_t s, t; \
95 int r; \
96 \
97 assert_return(lease, -EINVAL); \
98 \
99 r = sd_dhcp6_lease_get_##name(lease, &s); \
100 if (r < 0) \
101 return r; \
102 \
103 r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
104 if (r < 0) \
105 return r; \
106 \
107 if (ret) \
108 *ret = time_span_to_stamp(s, t); \
109 return 0; \
110 }
f4fbea7a 111
394fac52
YW
112DEFINE_GET_TIME_FUNCTIONS(t1, lifetime_t1);
113DEFINE_GET_TIME_FUNCTIONS(t2, lifetime_t1);
114DEFINE_GET_TIME_FUNCTIONS(valid_lifetime, lifetime_valid);
709d6710 115
65ece4c8
YW
116static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
117 assert(lease);
118
119 if (server_address)
120 lease->server_address = *server_address;
121 else
122 lease->server_address = (struct in6_addr) {};
123}
124
f4fbea7a
YW
125int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
126 assert_return(lease, -EINVAL);
127 assert_return(ret, -EINVAL);
709d6710 128
f4fbea7a 129 *ret = lease->server_address;
709d6710
PF
130 return 0;
131}
132
e5b0b87f 133void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
e5b0b87f 134 assert(ia);
3fb2c570 135
80a226b2 136 LIST_FOREACH(addresses, a, ia->addresses)
e5b0b87f 137 free(a);
3fb2c570 138
e5b0b87f
YW
139 ia->addresses = NULL;
140}
3fb2c570 141
e5b0b87f
YW
142DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
143 if (!ia)
144 return NULL;
3fb2c570 145
e5b0b87f
YW
146 dhcp6_ia_clear_addresses(ia);
147
148 return mfree(ia);
3fb2c570
PF
149}
150
e79b4b85 151int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
93bd7c41 152 uint8_t *clientid = NULL;
e79b4b85 153
049fddfa 154 assert(lease);
93bd7c41 155 assert(id || len == 0);
e79b4b85 156
93bd7c41
YW
157 if (len > 0) {
158 clientid = memdup(id, len);
159 if (!clientid)
160 return -ENOMEM;
161 }
e79b4b85
YW
162
163 free_and_replace(lease->clientid, clientid);
164 lease->clientid_len = len;
165
166 return 0;
167}
168
169int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
049fddfa 170 assert(lease);
e79b4b85
YW
171
172 if (!lease->clientid)
173 return -ENODATA;
174
175 if (ret_id)
176 *ret_id = lease->clientid;
177 if (ret_len)
178 *ret_len = lease->clientid_len;
179
180 return 0;
181}
182
4f81f0d2 183int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
93bd7c41 184 uint8_t *serverid = NULL;
33d36758 185
049fddfa 186 assert(lease);
93bd7c41 187 assert(id || len == 0);
3fb2c570 188
93bd7c41
YW
189 if (len > 0) {
190 serverid = memdup(id, len);
191 if (!serverid)
192 return -ENOMEM;
193 }
3fb2c570 194
33d36758 195 free_and_replace(lease->serverid, serverid);
3fb2c570
PF
196 lease->serverid_len = len;
197
198 return 0;
199}
200
4f81f0d2 201int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
049fddfa 202 assert(lease);
3fb2c570 203
99f1d3fc 204 if (!lease->serverid)
4f81f0d2 205 return -ENODATA;
99f1d3fc 206
4f81f0d2
YW
207 if (ret_id)
208 *ret_id = lease->serverid;
209 if (ret_len)
210 *ret_len = lease->serverid_len;
3fb2c570
PF
211 return 0;
212}
213
214int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
049fddfa 215 assert(lease);
3fb2c570
PF
216
217 lease->preference = preference;
3fb2c570
PF
218 return 0;
219}
220
049fddfa
YW
221int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
222 assert(lease);
223 assert(ret);
3fb2c570 224
049fddfa 225 *ret = lease->preference;
3fb2c570
PF
226 return 0;
227}
228
ed6ee219 229int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
049fddfa 230 assert(lease);
ed6ee219
PF
231
232 lease->rapid_commit = true;
ed6ee219
PF
233 return 0;
234}
235
049fddfa
YW
236int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
237 assert(lease);
238 assert(ret);
ed6ee219 239
049fddfa 240 *ret = lease->rapid_commit;
ed6ee219
PF
241 return 0;
242}
243
d8ec95c7
YW
244int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
245 assert_return(lease, -EINVAL);
246
247 if (!lease->addr_iter)
248 return -ENODATA;
249
250 if (ret)
251 *ret = lease->addr_iter->iaaddr.address;
252 return 0;
253}
254
255int sd_dhcp6_lease_get_address_lifetime(
049fddfa 256 sd_dhcp6_lease *lease,
d8ec95c7
YW
257 usec_t *ret_lifetime_preferred,
258 usec_t *ret_lifetime_valid) {
259
260 const struct iaaddr *a;
049fddfa 261
ea3b3a75 262 assert_return(lease, -EINVAL);
ea3b3a75
PF
263
264 if (!lease->addr_iter)
126277ac 265 return -ENODATA;
ea3b3a75 266
d8ec95c7
YW
267 a = &lease->addr_iter->iaaddr;
268
049fddfa 269 if (ret_lifetime_preferred)
d8ec95c7 270 *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
049fddfa 271 if (ret_lifetime_valid)
d8ec95c7 272 *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
ea3b3a75
PF
273 return 0;
274}
275
d8ec95c7
YW
276int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease) {
277 if (!lease)
278 return false;
279
280 lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
281 return !!lease->addr_iter;
282}
283
284int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease) {
285 if (!lease || !lease->addr_iter)
286 return false;
287
288 lease->addr_iter = lease->addr_iter->addresses_next;
289 return !!lease->addr_iter;
ea3b3a75
PF
290}
291
fb70992d
YW
292int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease) {
293 return lease && lease->ia_na;
294}
295
d8ec95c7 296int sd_dhcp6_lease_get_pd_prefix(
049fddfa
YW
297 sd_dhcp6_lease *lease,
298 struct in6_addr *ret_prefix,
d8ec95c7
YW
299 uint8_t *ret_prefix_len) {
300
301 const struct iapdprefix *a;
049fddfa 302
652bf042 303 assert_return(lease, -EINVAL);
652bf042
PF
304
305 if (!lease->prefix_iter)
126277ac 306 return -ENODATA;
652bf042 307
d8ec95c7
YW
308 a = &lease->prefix_iter->iapdprefix;
309
049fddfa 310 if (ret_prefix)
d8ec95c7 311 *ret_prefix = a->address;
049fddfa 312 if (ret_prefix_len)
d8ec95c7
YW
313 *ret_prefix_len = a->prefixlen;
314 return 0;
315}
316
317int sd_dhcp6_lease_get_pd_lifetime(
318 sd_dhcp6_lease *lease,
319 uint64_t *ret_lifetime_preferred,
320 uint64_t *ret_lifetime_valid) {
321
322 const struct iapdprefix *a;
323
324 assert_return(lease, -EINVAL);
325
326 if (!lease->prefix_iter)
327 return -ENODATA;
328
329 a = &lease->prefix_iter->iapdprefix;
330
049fddfa 331 if (ret_lifetime_preferred)
d8ec95c7 332 *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
049fddfa 333 if (ret_lifetime_valid)
d8ec95c7 334 *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
652bf042
PF
335 return 0;
336}
337
d8ec95c7
YW
338int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease) {
339 if (!lease)
340 return false;
341
342 lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
343 return !!lease->prefix_iter;
344}
345
346int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease) {
347 if (!lease || !lease->prefix_iter)
348 return false;
349
350 lease->prefix_iter = lease->prefix_iter->addresses_next;
351 return !!lease->prefix_iter;
652bf042
PF
352}
353
d8ec95c7
YW
354#define DEFINE_GET_TIMESTAMP2(name) \
355 int sd_dhcp6_lease_get_##name##_lifetime_timestamp( \
356 sd_dhcp6_lease *lease, \
357 clockid_t clock, \
358 uint64_t *ret_lifetime_preferred, \
359 uint64_t *ret_lifetime_valid) { \
360 \
361 usec_t t, p, v; \
362 int r; \
363 \
364 assert_return(lease, -EINVAL); \
365 \
366 r = sd_dhcp6_lease_get_##name##_lifetime( \
367 lease, \
368 ret_lifetime_preferred ? &p : NULL, \
369 ret_lifetime_valid ? &v : NULL); \
370 if (r < 0) \
371 return r; \
372 \
373 r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
374 if (r < 0) \
375 return r; \
376 \
377 if (ret_lifetime_preferred) \
378 *ret_lifetime_preferred = time_span_to_stamp(p, t); \
379 if (ret_lifetime_valid) \
380 *ret_lifetime_valid = time_span_to_stamp(v, t); \
381 return 0; \
382 }
383
384DEFINE_GET_TIMESTAMP2(address);
385DEFINE_GET_TIMESTAMP2(pd);
386
fb70992d
YW
387int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
388 return lease && lease->ia_pd;
389}
390
e210f027 391int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
049fddfa
YW
392 assert(lease);
393 assert(optval || optlen == 0);
7bd8e95d 394
ad3c8420 395 if (optlen == 0)
7bd8e95d
PF
396 return 0;
397
ad3c8420 398 return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
7bd8e95d
PF
399}
400
edeee50b 401int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
7bd8e95d 402 assert_return(lease, -EINVAL);
7bd8e95d 403
edeee50b 404 if (!lease->dns)
126277ac 405 return -ENODATA;
7bd8e95d 406
5656ff9d
YW
407 if (ret)
408 *ret = lease->dns;
409
edeee50b 410 return lease->dns_count;
7bd8e95d
PF
411}
412
41b14f03
YW
413int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
414 _cleanup_strv_free_ char **domains = NULL;
5da1b97f 415 int r;
5da1b97f 416
049fddfa
YW
417 assert(lease);
418 assert(optval || optlen == 0);
5da1b97f 419
b27dcf08 420 if (optlen == 0)
5da1b97f
PF
421 return 0;
422
c43eea9f 423 r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
5da1b97f 424 if (r < 0)
b27dcf08 425 return r;
5da1b97f 426
41b14f03 427 return strv_extend_strv(&lease->domains, domains, true);
5da1b97f
PF
428}
429
edeee50b 430int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
5da1b97f 431 assert_return(lease, -EINVAL);
edeee50b 432 assert_return(ret, -EINVAL);
5da1b97f 433
edeee50b 434 if (!lease->domains)
126277ac 435 return -ENODATA;
5da1b97f 436
edeee50b
YW
437 *ret = lease->domains;
438 return strv_length(lease->domains);
5da1b97f
PF
439}
440
9c3d46bf 441int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
6599680e 442 int r;
6599680e 443
049fddfa
YW
444 assert(lease);
445 assert(optval || optlen == 0);
6599680e 446
b89a3758
YW
447 for (size_t offset = 0; offset < optlen;) {
448 const uint8_t *subval;
449 size_t sublen;
450 uint16_t subopt;
451
452 r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
453 if (r < 0)
454 return r;
6599680e 455
79893116 456 switch (subopt) {
6599680e
PF
457 case DHCP6_NTP_SUBOPTION_SRV_ADDR:
458 case DHCP6_NTP_SUBOPTION_MC_ADDR:
459 if (sublen != 16)
e261d315 460 return -EINVAL;
6599680e 461
ad3c8420 462 r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
b89a3758
YW
463 if (r < 0)
464 return r;
6599680e 465
6599680e
PF
466 break;
467
b89a3758 468 case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
9c3d46bf 469 _cleanup_free_ char *server = NULL;
b89a3758 470
9c3d46bf 471 r = dhcp6_option_parse_domainname(subval, sublen, &server);
6599680e 472 if (r < 0)
b27dcf08 473 return r;
6599680e 474
9c3d46bf
YW
475 if (strv_contains(lease->ntp_fqdn, server))
476 continue;
477
478 r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
479 if (r < 0)
480 return r;
6599680e
PF
481
482 break;
b89a3758 483 }}
6599680e
PF
484 }
485
6599680e
PF
486 return 0;
487}
488
e693e969 489int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
049fddfa
YW
490 assert(lease);
491 assert(optval || optlen == 0);
41e4615d 492
ad3c8420 493 if (optlen == 0)
41e4615d
PF
494 return 0;
495
e693e969
YW
496 /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
497 return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
41e4615d
PF
498}
499
edeee50b 500int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
6599680e 501 assert_return(lease, -EINVAL);
6599680e 502
e693e969 503 if (lease->ntp) {
5656ff9d
YW
504 if (ret)
505 *ret = lease->ntp;
e693e969
YW
506 return lease->ntp_count;
507 }
508
509 if (lease->sntp && !lease->ntp_fqdn) {
510 /* Fallback to the deprecated SNTP option. */
5656ff9d
YW
511 if (ret)
512 *ret = lease->sntp;
e693e969
YW
513 return lease->sntp_count;
514 }
6599680e 515
126277ac 516 return -ENODATA;
6599680e
PF
517}
518
edeee50b 519int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
6599680e 520 assert_return(lease, -EINVAL);
6599680e 521
edeee50b 522 if (!lease->ntp_fqdn)
126277ac 523 return -ENODATA;
6599680e 524
5656ff9d
YW
525 if (ret)
526 *ret = lease->ntp_fqdn;
edeee50b 527 return strv_length(lease->ntp_fqdn);
6599680e
PF
528}
529
3f8227bf 530int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
c43eea9f 531 char *fqdn;
049fddfa 532 int r;
c43eea9f 533
049fddfa
YW
534 assert(lease);
535 assert(optval || optlen == 0);
536
537 if (optlen == 0)
538 return 0;
c43eea9f
BG
539
540 if (optlen < 2)
541 return -ENODATA;
542
543 /* Ignore the flags field, it doesn't carry any useful
544 information for clients. */
545 r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
546 if (r < 0)
547 return r;
548
549 return free_and_replace(lease->fqdn, fqdn);
550}
551
edeee50b 552int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
c43eea9f 553 assert_return(lease, -EINVAL);
edeee50b 554 assert_return(ret, -EINVAL);
c43eea9f 555
edeee50b 556 if (!lease->fqdn)
126277ac 557 return -ENODATA;
c43eea9f 558
edeee50b
YW
559 *ret = lease->fqdn;
560 return 0;
c43eea9f
BG
561}
562
fde78860
RP
563int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
564 _cleanup_free_ char *uri = NULL;
565 int r;
566
567 assert(lease);
568 assert(optval || optlen == 0);
569
570 r = dhcp6_option_parse_string(optval, optlen, &uri);
571 if (r < 0)
572 return r;
573
574 if (uri && !in_charset(uri, URI_VALID))
575 return -EINVAL;
576
577 return free_and_replace(lease->captive_portal, uri);
578}
579
580int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
581 assert_return(lease, -EINVAL);
582 assert_return(ret, -EINVAL);
583
584 if (!lease->captive_portal)
585 return -ENODATA;
586
587 *ret = lease->captive_portal;
588 return 0;
589}
590
6b44099b
NR
591int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) {
592 int r;
593
594 assert_return(lease, -EINVAL);
595
596 if (set_isempty(lease->vendor_options))
597 return -ENODATA;
598
599 if (ret) {
600 if (!lease->sorted_vendor_options) {
601 r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL);
602 if (r < 0)
603 return r;
604 }
605
606 *ret = lease->sorted_vendor_options;
607 }
608
609 return set_size(lease->vendor_options);
610}
611
612static int dhcp6_lease_insert_vendor_option(
613 sd_dhcp6_lease *lease,
614 uint16_t option_code,
615 const void *data,
616 size_t len,
498a6de5 617 uint32_t enterprise_id) {
6b44099b
NR
618
619 _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL;
620
621 assert(lease);
622
623 option = new(sd_dhcp6_option, 1);
624 if (!option)
625 return -ENOMEM;
626
627 *option = (sd_dhcp6_option) {
628 .n_ref = 1,
629 .enterprise_identifier = enterprise_id,
630 .option = option_code,
631 .length = len,
632 };
633 option->data = memdup_suffix0(data, len);
634 if (!option->data)
635 return -ENOMEM;
636
637 return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option));
638}
639
640static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
641 int r;
642 uint32_t enterprise_id;
643
644 assert(lease);
645 assert(optval || optlen == 0);
646
647 if (optlen < sizeof(be32_t))
648 return -EBADMSG;
649
650 enterprise_id = unaligned_read_be32(optval);
651
652 for (size_t offset = 4; offset < optlen;) {
653 const uint8_t *subval;
654 size_t sublen;
655 uint16_t subopt;
656
657 r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
658 if (r < 0)
659 return r;
660
661 r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id);
662 if (r < 0)
663 return r;
664 }
665 return 0;
666}
667
65ece4c8
YW
668static int dhcp6_lease_parse_message(
669 sd_dhcp6_client *client,
670 sd_dhcp6_lease *lease,
671 const DHCP6Message *message,
672 size_t len) {
673
674 usec_t irt = IRT_DEFAULT;
675 int r;
676
677 assert(client);
678 assert(lease);
679 assert(message);
680 assert(len >= sizeof(DHCP6Message));
681
682 len -= sizeof(DHCP6Message);
683 for (size_t offset = 0; offset < len;) {
684 uint16_t optcode;
685 size_t optlen;
686 const uint8_t *optval;
687
81b73359
PK
688 if (len - offset < offsetof(DHCP6Option, data)) {
689 log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
690 break;
691 }
692
65ece4c8
YW
693 r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
694 if (r < 0)
50309ff7
YW
695 return log_dhcp6_client_errno(client, r,
696 "Failed to parse option header at offset %zu of total length %zu: %m",
697 offset, len);
65ece4c8
YW
698
699 switch (optcode) {
700 case SD_DHCP6_OPTION_CLIENTID:
701 if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
702 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
703 dhcp6_message_type_to_string(message->type));
704
705 r = dhcp6_lease_set_clientid(lease, optval, optlen);
706 if (r < 0)
50309ff7 707 return log_dhcp6_client_errno(client, r, "Failed to set client ID: %m");
65ece4c8
YW
708
709 break;
710
711 case SD_DHCP6_OPTION_SERVERID:
712 if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
713 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
714 dhcp6_message_type_to_string(message->type));
715
716 r = dhcp6_lease_set_serverid(lease, optval, optlen);
717 if (r < 0)
50309ff7 718 return log_dhcp6_client_errno(client, r, "Failed to set server ID: %m");
65ece4c8
YW
719
720 break;
721
722 case SD_DHCP6_OPTION_PREFERENCE:
723 if (optlen != 1)
50309ff7 724 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received invalid length for preference.");
65ece4c8
YW
725
726 r = dhcp6_lease_set_preference(lease, optval[0]);
727 if (r < 0)
50309ff7 728 return log_dhcp6_client_errno(client, r, "Failed to set preference: %m");
65ece4c8
YW
729
730 break;
731
732 case SD_DHCP6_OPTION_STATUS_CODE: {
733 _cleanup_free_ char *msg = NULL;
734
735 r = dhcp6_option_parse_status(optval, optlen, &msg);
736 if (r < 0)
50309ff7 737 return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
65ece4c8 738 if (r > 0)
1929c1fc 739 return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
ea9dbf51 740 "Received %s message with non-zero status%s%s",
65ece4c8 741 dhcp6_message_type_to_string(message->type),
ea9dbf51 742 isempty(msg) ? "." : ": ", strempty(msg));
65ece4c8
YW
743 break;
744 }
745 case SD_DHCP6_OPTION_IA_NA: {
746 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
747
748 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
749 log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
750 break;
751 }
752
753 r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
754 if (r == -ENOMEM)
50309ff7
YW
755 return log_oom_debug();
756 if (r < 0) {
757 log_dhcp6_client_errno(client, r, "Failed to parse IA_NA option, ignoring: %m");
65ece4c8 758 continue;
50309ff7 759 }
65ece4c8
YW
760
761 if (lease->ia_na) {
762 log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
763 continue;
764 }
765
766 dhcp6_ia_free(lease->ia_na);
767 lease->ia_na = TAKE_PTR(ia);
768 break;
769 }
770 case SD_DHCP6_OPTION_IA_PD: {
771 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
772
773 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
774 log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
775 break;
776 }
777
778 r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
779 if (r == -ENOMEM)
50309ff7
YW
780 return log_oom_debug();
781 if (r < 0) {
782 log_dhcp6_client_errno(client, r, "Failed to parse IA_PD option, ignoring: %m");
65ece4c8 783 continue;
50309ff7 784 }
65ece4c8
YW
785
786 if (lease->ia_pd) {
787 log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
788 continue;
789 }
790
791 dhcp6_ia_free(lease->ia_pd);
792 lease->ia_pd = TAKE_PTR(ia);
793 break;
794 }
795 case SD_DHCP6_OPTION_RAPID_COMMIT:
50309ff7
YW
796 if (optlen != 0)
797 log_dhcp6_client(client, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen);
798
65ece4c8
YW
799 r = dhcp6_lease_set_rapid_commit(lease);
800 if (r < 0)
50309ff7 801 return log_dhcp6_client_errno(client, r, "Failed to set rapid commit flag: %m");
65ece4c8
YW
802
803 break;
804
f697ab35 805 case SD_DHCP6_OPTION_DNS_SERVER:
65ece4c8
YW
806 r = dhcp6_lease_add_dns(lease, optval, optlen);
807 if (r < 0)
808 log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
809
810 break;
811
f697ab35 812 case SD_DHCP6_OPTION_DOMAIN:
65ece4c8
YW
813 r = dhcp6_lease_add_domains(lease, optval, optlen);
814 if (r < 0)
815 log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
816
817 break;
818
819 case SD_DHCP6_OPTION_NTP_SERVER:
820 r = dhcp6_lease_add_ntp(lease, optval, optlen);
821 if (r < 0)
822 log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
823
824 break;
825
f697ab35 826 case SD_DHCP6_OPTION_SNTP_SERVER:
65ece4c8
YW
827 r = dhcp6_lease_add_sntp(lease, optval, optlen);
828 if (r < 0)
829 log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
830
831 break;
832
fde78860
RP
833 case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
834 r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
835 if (r < 0)
836 log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
837 break;
838
65ece4c8
YW
839 case SD_DHCP6_OPTION_CLIENT_FQDN:
840 r = dhcp6_lease_set_fqdn(lease, optval, optlen);
841 if (r < 0)
842 log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
843
844 break;
845
846 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
847 if (optlen != 4)
50309ff7
YW
848 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
849 "Received information refresh time option with an invalid length (%zu).", optlen);
65ece4c8 850
9132cbd5 851 irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
65ece4c8 852 break;
6b44099b
NR
853
854 case SD_DHCP6_OPTION_VENDOR_OPTS:
855 r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
856 if (r < 0)
857 log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m");
858
859 break;
65ece4c8
YW
860 }
861 }
862
863 uint8_t *clientid;
864 size_t clientid_len;
865 if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
9c9fee80
YW
866 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
867 "%s message does not contain client ID. Ignoring.",
65ece4c8
YW
868 dhcp6_message_type_to_string(message->type));
869
97c3506d 870 if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
9c9fee80
YW
871 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
872 "The client ID in %s message does not match. Ignoring.",
65ece4c8
YW
873 dhcp6_message_type_to_string(message->type));
874
e1774086
YW
875 if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
876 client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
877 log_dhcp6_client(client, "New information request will be refused in %s.",
878 FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
879
880 } else {
65ece4c8
YW
881 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
882 if (r < 0)
883 return log_dhcp6_client_errno(client, r, "%s has no server id",
884 dhcp6_message_type_to_string(message->type));
885
886 if (!lease->ia_na && !lease->ia_pd)
9c9fee80
YW
887 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
888 "No IA_PD prefix or IA_NA address received. Ignoring.");
65ece4c8
YW
889
890 dhcp6_lease_set_lifetime(lease);
891 }
892
65ece4c8
YW
893 return 0;
894}
895
8301aa0b 896static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
5cf67bb4
YW
897 if (!lease)
898 return NULL;
6599680e 899
6b44099b
NR
900 set_free(lease->vendor_options);
901 free(lease->sorted_vendor_options);
e79b4b85 902 free(lease->clientid);
3733eec3 903 free(lease->serverid);
e5b0b87f
YW
904 dhcp6_ia_free(lease->ia_na);
905 dhcp6_ia_free(lease->ia_pd);
3733eec3 906 free(lease->dns);
c43eea9f 907 free(lease->fqdn);
fde78860 908 free(lease->captive_portal);
5cf67bb4 909 strv_free(lease->domains);
3733eec3 910 free(lease->ntp);
5cf67bb4 911 strv_free(lease->ntp_fqdn);
e693e969 912 free(lease->sntp);
3733eec3 913
6b430fdb 914 return mfree(lease);
3fb2c570
PF
915}
916
8301aa0b
YW
917DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
918
3fb2c570
PF
919int dhcp6_lease_new(sd_dhcp6_lease **ret) {
920 sd_dhcp6_lease *lease;
921
049fddfa
YW
922 assert(ret);
923
c9309258 924 lease = new(sd_dhcp6_lease, 1);
3fb2c570
PF
925 if (!lease)
926 return -ENOMEM;
927
c9309258
YW
928 *lease = (sd_dhcp6_lease) {
929 .n_ref = 1,
930 };
3fb2c570 931
3fb2c570
PF
932 *ret = lease;
933 return 0;
934}
65ece4c8
YW
935
936int dhcp6_lease_new_from_message(
937 sd_dhcp6_client *client,
938 const DHCP6Message *message,
939 size_t len,
940 const triple_timestamp *timestamp,
941 const struct in6_addr *server_address,
942 sd_dhcp6_lease **ret) {
943
944 _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
945 int r;
946
947 assert(client);
948 assert(message);
949 assert(len >= sizeof(DHCP6Message));
950 assert(ret);
951
952 r = dhcp6_lease_new(&lease);
953 if (r < 0)
954 return r;
955
956 dhcp6_lease_set_timestamp(lease, timestamp);
957 dhcp6_lease_set_server_address(lease, server_address);
958
959 r = dhcp6_lease_parse_message(client, lease, message, len);
960 if (r < 0)
961 return r;
962
963 *ret = TAKE_PTR(lease);
964 return 0;
965}