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