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