]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/ndisc-router.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd-network / ndisc-router.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2014 Intel Corporation. All rights reserved.
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <netinet/icmp6.h>
22
23 #include "sd-ndisc.h"
24
25 #include "alloc-util.h"
26 #include "dns-domain.h"
27 #include "hostname-util.h"
28 #include "missing.h"
29 #include "ndisc-internal.h"
30 #include "ndisc-router.h"
31 #include "strv.h"
32
33 _public_ sd_ndisc_router* sd_ndisc_router_ref(sd_ndisc_router *rt) {
34 if (!rt)
35 return NULL;
36
37 assert(rt->n_ref > 0);
38 rt->n_ref++;
39
40 return rt;
41 }
42
43 _public_ sd_ndisc_router* sd_ndisc_router_unref(sd_ndisc_router *rt) {
44 if (!rt)
45 return NULL;
46
47 assert(rt->n_ref > 0);
48 rt->n_ref--;
49
50 if (rt->n_ref > 0)
51 return NULL;
52
53 return mfree(rt);
54 }
55
56 sd_ndisc_router *ndisc_router_new(size_t raw_size) {
57 sd_ndisc_router *rt;
58
59 rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
60 if (!rt)
61 return NULL;
62
63 rt->raw_size = raw_size;
64 rt->n_ref = 1;
65
66 return rt;
67 }
68
69 _public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) {
70 _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
71 int r;
72
73 assert_return(ret, -EINVAL);
74 assert_return(raw || raw_size <= 0, -EINVAL);
75
76 rt = ndisc_router_new(raw_size);
77 if (!rt)
78 return -ENOMEM;
79
80 memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size);
81 r = ndisc_router_parse(rt);
82 if (r < 0)
83 return r;
84
85 *ret = rt;
86 rt = NULL;
87
88 return r;
89 }
90
91 _public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
92 assert_return(rt, -EINVAL);
93 assert_return(ret_addr, -EINVAL);
94
95 if (IN6_IS_ADDR_UNSPECIFIED(&rt->address))
96 return -ENODATA;
97
98 *ret_addr = rt->address;
99 return 0;
100 }
101
102 _public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
103 assert_return(rt, -EINVAL);
104 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
105 assert_return(clock_supported(clock), -EOPNOTSUPP);
106 assert_return(ret, -EINVAL);
107
108 if (!triple_timestamp_is_set(&rt->timestamp))
109 return -ENODATA;
110
111 *ret = triple_timestamp_by_clock(&rt->timestamp, clock);
112 return 0;
113 }
114
115 _public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
116 assert_return(rt, -EINVAL);
117 assert_return(ret, -EINVAL);
118 assert_return(size, -EINVAL);
119
120 *ret = NDISC_ROUTER_RAW(rt);
121 *size = rt->raw_size;
122
123 return 0;
124 }
125
126 int ndisc_router_parse(sd_ndisc_router *rt) {
127 struct nd_router_advert *a;
128 const uint8_t *p;
129 bool has_mtu = false, has_flag_extension = false;
130 size_t left;
131
132 assert(rt);
133
134 if (rt->raw_size < sizeof(struct nd_router_advert)) {
135 log_ndisc("Too small to be a router advertisement, ignoring.");
136 return -EBADMSG;
137 }
138
139 /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
140 a = NDISC_ROUTER_RAW(rt);
141
142 if (a->nd_ra_type != ND_ROUTER_ADVERT) {
143 log_ndisc("Received ND packet that is not a router advertisement, ignoring.");
144 return -EBADMSG;
145 }
146
147 if (a->nd_ra_code != 0) {
148 log_ndisc("Received ND packet with wrong RA code, ignoring.");
149 return -EBADMSG;
150 }
151
152 rt->hop_limit = a->nd_ra_curhoplimit;
153 rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */
154 rt->lifetime = be16toh(a->nd_ra_router_lifetime);
155
156 rt->preference = (rt->flags >> 3) & 3;
157 if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
158 rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
159
160 p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
161 left = rt->raw_size - sizeof(struct nd_router_advert);
162
163 for (;;) {
164 uint8_t type;
165 size_t length;
166
167 if (left == 0)
168 break;
169
170 if (left < 2) {
171 log_ndisc("Option lacks header, ignoring datagram.");
172 return -EBADMSG;
173 }
174
175 type = p[0];
176 length = p[1] * 8;
177
178 if (length == 0) {
179 log_ndisc("Zero-length option, ignoring datagram.");
180 return -EBADMSG;
181 }
182 if (left < length) {
183 log_ndisc("Option truncated, ignoring datagram.");
184 return -EBADMSG;
185 }
186
187 switch (type) {
188
189 case SD_NDISC_OPTION_PREFIX_INFORMATION:
190
191 if (length != 4*8) {
192 log_ndisc("Prefix option of invalid size, ignoring datagram.");
193 return -EBADMSG;
194 }
195
196 if (p[2] > 128) {
197 log_ndisc("Bad prefix length, ignoring datagram.");
198 return -EBADMSG;
199 }
200
201 break;
202
203 case SD_NDISC_OPTION_MTU: {
204 uint32_t m;
205
206 if (has_mtu) {
207 log_ndisc("MTU option specified twice, ignoring.");
208 continue;
209 }
210
211 if (length != 8) {
212 log_ndisc("MTU option of invalid size, ignoring datagram.");
213 return -EBADMSG;
214 }
215
216 m = be32toh(*(uint32_t*) (p + 4));
217 if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
218 rt->mtu = m;
219
220 has_mtu = true;
221 break;
222 }
223
224 case SD_NDISC_OPTION_ROUTE_INFORMATION:
225 if (length < 1*8 || length > 3*8) {
226 log_ndisc("Route information option of invalid size, ignoring datagram.");
227 return -EBADMSG;
228 }
229
230 if (p[2] > 128) {
231 log_ndisc("Bad route prefix length, ignoring datagram.");
232 return -EBADMSG;
233 }
234
235 break;
236
237 case SD_NDISC_OPTION_RDNSS:
238 if (length < 3*8 || (length % (2*8)) != 1*8) {
239 log_ndisc("RDNSS option has invalid size.");
240 return -EBADMSG;
241 }
242
243 break;
244
245 case SD_NDISC_OPTION_FLAGS_EXTENSION:
246
247 if (has_flag_extension) {
248 log_ndisc("Flags extension option specified twice, ignoring.");
249 continue;
250 }
251
252 if (length < 1*8) {
253 log_ndisc("Flags extension option has invalid size.");
254 return -EBADMSG;
255 }
256
257 /* Add in the additional flags bits */
258 rt->flags |=
259 ((uint64_t) p[2] << 8) |
260 ((uint64_t) p[3] << 16) |
261 ((uint64_t) p[4] << 24) |
262 ((uint64_t) p[5] << 32) |
263 ((uint64_t) p[6] << 40) |
264 ((uint64_t) p[7] << 48);
265
266 has_flag_extension = true;
267 break;
268
269 case SD_NDISC_OPTION_DNSSL:
270 if (length < 2*8) {
271 log_ndisc("DNSSL option has invalid size.");
272 return -EBADMSG;
273 }
274
275 break;
276 }
277
278 p += length, left -= length;
279 }
280
281 rt->rindex = sizeof(struct nd_router_advert);
282 return 0;
283 }
284
285 _public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
286 assert_return(rt, -EINVAL);
287 assert_return(ret, -EINVAL);
288
289 *ret = rt->hop_limit;
290 return 0;
291 }
292
293 _public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) {
294 assert_return(rt, -EINVAL);
295 assert_return(ret_flags, -EINVAL);
296
297 *ret_flags = rt->flags;
298 return 0;
299 }
300
301 _public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) {
302 assert_return(rt, -EINVAL);
303 assert_return(ret_lifetime, -EINVAL);
304
305 *ret_lifetime = rt->lifetime;
306 return 0;
307 }
308
309 _public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
310 assert_return(rt, -EINVAL);
311 assert_return(ret, -EINVAL);
312
313 *ret = rt->preference;
314 return 0;
315 }
316
317 _public_ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
318 assert_return(rt, -EINVAL);
319 assert_return(ret, -EINVAL);
320
321 if (rt->mtu <= 0)
322 return -ENODATA;
323
324 *ret = rt->mtu;
325 return 0;
326 }
327
328 _public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
329 assert_return(rt, -EINVAL);
330
331 assert(rt->raw_size >= sizeof(struct nd_router_advert));
332 rt->rindex = sizeof(struct nd_router_advert);
333
334 return rt->rindex < rt->raw_size;
335 }
336
337 _public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
338 size_t length;
339
340 assert_return(rt, -EINVAL);
341
342 if (rt->rindex == rt->raw_size) /* EOF */
343 return -ESPIPE;
344
345 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
346 return -EBADMSG;
347
348 length = NDISC_ROUTER_OPTION_LENGTH(rt);
349 if (rt->rindex + length > rt->raw_size)
350 return -EBADMSG;
351
352 rt->rindex += length;
353 return rt->rindex < rt->raw_size;
354 }
355
356 _public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
357 assert_return(rt, -EINVAL);
358 assert_return(ret, -EINVAL);
359
360 if (rt->rindex == rt->raw_size) /* EOF */
361 return -ESPIPE;
362
363 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
364 return -EBADMSG;
365
366 *ret = NDISC_ROUTER_OPTION_TYPE(rt);
367 return 0;
368 }
369
370 _public_ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
371 uint8_t k;
372 int r;
373
374 assert_return(rt, -EINVAL);
375
376 r = sd_ndisc_router_option_get_type(rt, &k);
377 if (r < 0)
378 return r;
379
380 return type == k;
381 }
382
383 _public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
384 size_t length;
385
386 assert_return(rt, -EINVAL);
387 assert_return(ret, -EINVAL);
388 assert_return(size, -EINVAL);
389
390 /* Note that this returns the full option, including the option header */
391
392 if (rt->rindex + 2 > rt->raw_size)
393 return -EBADMSG;
394
395 length = NDISC_ROUTER_OPTION_LENGTH(rt);
396 if (rt->rindex + length > rt->raw_size)
397 return -EBADMSG;
398
399 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
400 *size = length;
401
402 return 0;
403 }
404
405 static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
406 struct nd_opt_prefix_info *ri;
407 size_t length;
408 int r;
409
410 assert(rt);
411 assert(ret);
412
413 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
414 if (r < 0)
415 return r;
416 if (r == 0)
417 return -EMEDIUMTYPE;
418
419 length = NDISC_ROUTER_OPTION_LENGTH(rt);
420 if (length != sizeof(struct nd_opt_prefix_info))
421 return -EBADMSG;
422
423 ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
424 if (ri->nd_opt_pi_prefix_len > 128)
425 return -EBADMSG;
426
427 *ret = ri;
428 return 0;
429 }
430
431 _public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
432 struct nd_opt_prefix_info *ri;
433 int r;
434
435 assert_return(rt, -EINVAL);
436 assert_return(ret, -EINVAL);
437
438 r = get_prefix_info(rt, &ri);
439 if (r < 0)
440 return r;
441
442 *ret = be32toh(ri->nd_opt_pi_valid_time);
443 return 0;
444 }
445
446 _public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
447 struct nd_opt_prefix_info *pi;
448 int r;
449
450 assert_return(rt, -EINVAL);
451 assert_return(ret, -EINVAL);
452
453 r = get_prefix_info(rt, &pi);
454 if (r < 0)
455 return r;
456
457 *ret = be32toh(pi->nd_opt_pi_preferred_time);
458 return 0;
459 }
460
461 _public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
462 struct nd_opt_prefix_info *pi;
463 uint8_t flags;
464 int r;
465
466 assert_return(rt, -EINVAL);
467 assert_return(ret, -EINVAL);
468
469 r = get_prefix_info(rt, &pi);
470 if (r < 0)
471 return r;
472
473 flags = pi->nd_opt_pi_flags_reserved;
474
475 if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) {
476 log_ndisc("Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
477 flags &= ~ND_OPT_PI_FLAG_AUTO;
478 }
479
480 *ret = flags;
481 return 0;
482 }
483
484 _public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
485 struct nd_opt_prefix_info *pi;
486 int r;
487
488 assert_return(rt, -EINVAL);
489 assert_return(ret_addr, -EINVAL);
490
491 r = get_prefix_info(rt, &pi);
492 if (r < 0)
493 return r;
494
495 *ret_addr = pi->nd_opt_pi_prefix;
496 return 0;
497 }
498
499 _public_ int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
500 struct nd_opt_prefix_info *pi;
501 int r;
502
503 assert_return(rt, -EINVAL);
504 assert_return(ret, -EINVAL);
505
506 r = get_prefix_info(rt, &pi);
507 if (r < 0)
508 return r;
509
510 if (pi->nd_opt_pi_prefix_len > 128)
511 return -EBADMSG;
512
513 *ret = pi->nd_opt_pi_prefix_len;
514 return 0;
515 }
516
517 static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
518 uint8_t *ri;
519 size_t length;
520 int r;
521
522 assert(rt);
523 assert(ret);
524
525 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
526 if (r < 0)
527 return r;
528 if (r == 0)
529 return -EMEDIUMTYPE;
530
531 length = NDISC_ROUTER_OPTION_LENGTH(rt);
532 if (length < 1*8 || length > 3*8)
533 return -EBADMSG;
534
535 ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
536
537 if (ri[2] > 128)
538 return -EBADMSG;
539
540 *ret = ri;
541 return 0;
542 }
543
544 _public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
545 uint8_t *ri;
546 int r;
547
548 assert_return(rt, -EINVAL);
549 assert_return(ret, -EINVAL);
550
551 r = get_route_info(rt, &ri);
552 if (r < 0)
553 return r;
554
555 *ret = be32toh(*(uint32_t*) (ri + 4));
556 return 0;
557 }
558
559 _public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
560 uint8_t *ri;
561 int r;
562
563 assert_return(rt, -EINVAL);
564 assert_return(ret_addr, -EINVAL);
565
566 r = get_route_info(rt, &ri);
567 if (r < 0)
568 return r;
569
570 zero(*ret_addr);
571 memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
572
573 return 0;
574 }
575
576 _public_ int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
577 uint8_t *ri;
578 int r;
579
580 assert_return(rt, -EINVAL);
581 assert_return(ret, -EINVAL);
582
583 r = get_route_info(rt, &ri);
584 if (r < 0)
585 return r;
586
587 *ret = ri[2];
588 return 0;
589 }
590
591 _public_ int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
592 uint8_t *ri;
593 int r;
594
595 assert_return(rt, -EINVAL);
596 assert_return(ret, -EINVAL);
597
598 r = get_route_info(rt, &ri);
599 if (r < 0)
600 return r;
601
602 *ret = (ri[3] >> 3) & 3;
603 if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
604 *ret = SD_NDISC_PREFERENCE_MEDIUM;
605
606 return 0;
607 }
608
609 static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
610 size_t length;
611 int r;
612
613 assert(rt);
614 assert(ret);
615
616 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
617 if (r < 0)
618 return r;
619 if (r == 0)
620 return -EMEDIUMTYPE;
621
622 length = NDISC_ROUTER_OPTION_LENGTH(rt);
623 if (length < 3*8 || (length % (2*8)) != 1*8)
624 return -EBADMSG;
625
626 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
627 return 0;
628 }
629
630 _public_ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
631 uint8_t *ri;
632 int r;
633
634 assert_return(rt, -EINVAL);
635 assert_return(ret, -EINVAL);
636
637 r = get_rdnss_info(rt, &ri);
638 if (r < 0)
639 return r;
640
641 *ret = (const struct in6_addr*) (ri + 8);
642 return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
643 }
644
645 _public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
646 uint8_t *ri;
647 int r;
648
649 assert_return(rt, -EINVAL);
650 assert_return(ret, -EINVAL);
651
652 r = get_rdnss_info(rt, &ri);
653 if (r < 0)
654 return r;
655
656 *ret = be32toh(*(uint32_t*) (ri + 4));
657 return 0;
658 }
659
660 static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
661 size_t length;
662 int r;
663
664 assert(rt);
665 assert(ret);
666
667 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
668 if (r < 0)
669 return r;
670 if (r == 0)
671 return -EMEDIUMTYPE;
672
673 length = NDISC_ROUTER_OPTION_LENGTH(rt);
674 if (length < 2*8)
675 return -EBADMSG;
676
677 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
678 return 0;
679 }
680
681 _public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
682 _cleanup_strv_free_ char **l = NULL;
683 _cleanup_free_ char *e = NULL;
684 size_t allocated = 0, n = 0, left;
685 uint8_t *ri, *p;
686 bool first = true;
687 int r;
688 unsigned k = 0;
689
690 assert_return(rt, -EINVAL);
691 assert_return(ret, -EINVAL);
692
693 r = get_dnssl_info(rt, &ri);
694 if (r < 0)
695 return r;
696
697 p = ri + 8;
698 left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
699
700 for (;;) {
701 if (left == 0) {
702
703 if (n > 0) /* Not properly NUL terminated */
704 return -EBADMSG;
705
706 break;
707 }
708
709 if (*p == 0) {
710 /* Found NUL termination */
711
712 if (n > 0) {
713 _cleanup_free_ char *normalized = NULL;
714
715 e[n] = 0;
716 r = dns_name_normalize(e, &normalized);
717 if (r < 0)
718 return r;
719
720 /* Ignore the root domain name or "localhost" and friends */
721 if (!is_localhost(normalized) &&
722 !dns_name_is_root(normalized)) {
723
724 if (strv_push(&l, normalized) < 0)
725 return -ENOMEM;
726
727 normalized = NULL;
728 k++;
729 }
730 }
731
732 n = 0;
733 first = true;
734 p++, left--;
735 continue;
736 }
737
738 /* Check for compression (which is not allowed) */
739 if (*p > 63)
740 return -EBADMSG;
741
742 if (1U + *p + 1U > left)
743 return -EBADMSG;
744
745 if (!GREEDY_REALLOC(e, allocated, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
746 return -ENOMEM;
747
748 if (first)
749 first = false;
750 else
751 e[n++] = '.';
752
753 r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
754 if (r < 0)
755 return r;
756
757 n += r;
758
759 left -= 1 + *p;
760 p += 1 + *p;
761 }
762
763 if (strv_isempty(l)) {
764 *ret = NULL;
765 return 0;
766 }
767
768 *ret = l;
769 l = NULL;
770
771 return k;
772 }
773
774 _public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) {
775 uint8_t *ri;
776 int r;
777
778 assert_return(rt, -EINVAL);
779 assert_return(ret_sec, -EINVAL);
780
781 r = get_dnssl_info(rt, &ri);
782 if (r < 0)
783 return r;
784
785 *ret_sec = be32toh(*(uint32_t*) (ri + 4));
786 return 0;
787 }