]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-radv.c
sd-radv: avoid redefinition of struct in6_addr
[thirdparty/systemd.git] / src / libsystemd-network / sd-radv.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright (C) 2017 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 #include <netinet/in.h>
23 #include <arpa/inet.h>
24
25 #include "sd-radv.h"
26
27 #include "macro.h"
28 #include "alloc-util.h"
29 #include "dns-domain.h"
30 #include "fd-util.h"
31 #include "icmp6-util.h"
32 #include "in-addr-util.h"
33 #include "radv-internal.h"
34 #include "socket-util.h"
35 #include "string-util.h"
36 #include "strv.h"
37 #include "util.h"
38 #include "random-util.h"
39
40 _public_ int sd_radv_new(sd_radv **ret) {
41 _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
42
43 assert_return(ret, -EINVAL);
44
45 ra = new0(sd_radv, 1);
46 if (!ra)
47 return -ENOMEM;
48
49 ra->n_ref = 1;
50 ra->fd = -1;
51
52 LIST_HEAD_INIT(ra->prefixes);
53
54 *ret = ra;
55 ra = NULL;
56
57 return 0;
58 }
59
60 _public_ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
61 int r;
62
63 assert_return(ra, -EINVAL);
64 assert_return(!ra->event, -EBUSY);
65
66 if (event)
67 ra->event = sd_event_ref(event);
68 else {
69 r = sd_event_default(&ra->event);
70 if (r < 0)
71 return 0;
72 }
73
74 ra->event_priority = priority;
75
76 return 0;
77 }
78
79 _public_ int sd_radv_detach_event(sd_radv *ra) {
80
81 assert_return(ra, -EINVAL);
82
83 ra->event = sd_event_unref(ra->event);
84 return 0;
85 }
86
87 _public_ sd_event *sd_radv_get_event(sd_radv *ra) {
88 assert_return(ra, NULL);
89
90 return ra->event;
91 }
92
93 static void radv_reset(sd_radv *ra) {
94
95 ra->timeout_event_source =
96 sd_event_source_unref(ra->timeout_event_source);
97
98 ra->recv_event_source =
99 sd_event_source_unref(ra->recv_event_source);
100
101 ra->ra_sent = 0;
102 }
103
104 _public_ sd_radv *sd_radv_ref(sd_radv *ra) {
105 if (!ra)
106 return NULL;
107
108 assert(ra->n_ref > 0);
109 ra->n_ref++;
110
111 return ra;
112 }
113
114 _public_ sd_radv *sd_radv_unref(sd_radv *ra) {
115 if (!ra)
116 return NULL;
117
118 assert(ra->n_ref > 0);
119 ra->n_ref--;
120
121 if (ra->n_ref > 0)
122 return NULL;
123
124 while (ra->prefixes) {
125 sd_radv_prefix *p = ra->prefixes;
126
127 LIST_REMOVE(prefix, ra->prefixes, p);
128 sd_radv_prefix_unref(p);
129 }
130
131 free(ra->rdnss);
132 free(ra->dnssl);
133
134 radv_reset(ra);
135
136 sd_radv_detach_event(ra);
137 return mfree(ra);
138 }
139
140 static int radv_send(sd_radv *ra, const struct in6_addr *dst,
141 const uint32_t router_lifetime) {
142 static const struct ether_addr mac_zero = {};
143 sd_radv_prefix *p;
144 struct sockaddr_in6 dst_addr = {
145 .sin6_family = AF_INET6,
146 .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
147 };
148 struct nd_router_advert adv = {};
149 struct {
150 struct nd_opt_hdr opthdr;
151 struct ether_addr slladdr;
152 } _packed_ opt_mac = {
153 .opthdr = {
154 .nd_opt_type = ND_OPT_SOURCE_LINKADDR,
155 .nd_opt_len = (sizeof(struct nd_opt_hdr) +
156 sizeof(struct ether_addr) - 1) /8 + 1,
157 },
158 };
159 struct nd_opt_mtu opt_mtu = {
160 .nd_opt_mtu_type = ND_OPT_MTU,
161 .nd_opt_mtu_len = 1,
162 };
163 /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, RDNSS
164 and DNSSL */
165 struct iovec iov[5 + ra->n_prefixes];
166 struct msghdr msg = {
167 .msg_name = &dst_addr,
168 .msg_namelen = sizeof(dst_addr),
169 .msg_iov = iov,
170 };
171
172 if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst))
173 dst_addr.sin6_addr = *dst;
174
175 adv.nd_ra_type = ND_ROUTER_ADVERT;
176 adv.nd_ra_curhoplimit = ra->hop_limit;
177 adv.nd_ra_flags_reserved = ra->flags;
178 adv.nd_ra_router_lifetime = htobe16(router_lifetime);
179 iov[msg.msg_iovlen].iov_base = &adv;
180 iov[msg.msg_iovlen].iov_len = sizeof(adv);
181 msg.msg_iovlen++;
182
183 /* MAC address is optional, either because the link does not use L2
184 addresses or load sharing is desired. See RFC 4861, Section 4.2 */
185 if (memcmp(&mac_zero, &ra->mac_addr, sizeof(mac_zero))) {
186 opt_mac.slladdr = ra->mac_addr;
187 iov[msg.msg_iovlen].iov_base = &opt_mac;
188 iov[msg.msg_iovlen].iov_len = sizeof(opt_mac);
189 msg.msg_iovlen++;
190 }
191
192 if (ra->mtu) {
193 opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu);
194 iov[msg.msg_iovlen].iov_base = &opt_mtu;
195 iov[msg.msg_iovlen].iov_len = sizeof(opt_mtu);
196 msg.msg_iovlen++;
197 }
198
199 LIST_FOREACH(prefix, p, ra->prefixes) {
200 iov[msg.msg_iovlen].iov_base = &p->opt;
201 iov[msg.msg_iovlen].iov_len = sizeof(p->opt);
202 msg.msg_iovlen++;
203 }
204
205 if (ra->rdnss) {
206 iov[msg.msg_iovlen].iov_base = ra->rdnss;
207 iov[msg.msg_iovlen].iov_len = ra->rdnss->length * 8;
208 msg.msg_iovlen++;
209 }
210
211 if (ra->dnssl) {
212 iov[msg.msg_iovlen].iov_base = ra->dnssl;
213 iov[msg.msg_iovlen].iov_len = ra->dnssl->length * 8;
214 msg.msg_iovlen++;
215 }
216
217 if (sendmsg(ra->fd, &msg, 0) < 0)
218 return -errno;
219
220 return 0;
221 }
222
223 static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
224 sd_radv *ra = userdata;
225 _cleanup_free_ char *addr = NULL;
226 struct in6_addr src;
227 triple_timestamp timestamp;
228 int r;
229 ssize_t buflen;
230 _cleanup_free_ char *buf = NULL;
231
232 assert(s);
233 assert(ra);
234 assert(ra->event);
235
236 buflen = next_datagram_size_fd(fd);
237
238 if ((unsigned) buflen < sizeof(struct nd_router_solicit))
239 return log_radv("Too short packet received");
240
241 buf = new0(char, buflen);
242 if (!buf)
243 return 0;
244
245 r = icmp6_receive(fd, buf, buflen, &src, &timestamp);
246 if (r < 0) {
247 switch (r) {
248 case -EADDRNOTAVAIL:
249 (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
250 log_radv("Received RS from non-link-local address %s. Ignoring", addr);
251 break;
252
253 case -EMULTIHOP:
254 log_radv("Received RS with invalid hop limit. Ignoring.");
255 break;
256
257 case -EPFNOSUPPORT:
258 log_radv("Received invalid source address from ICMPv6 socket. Ignoring.");
259 break;
260
261 default:
262 log_radv_warning_errno(r, "Error receiving from ICMPv6 socket: %m");
263 break;
264 }
265
266 return 0;
267 }
268
269 (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
270
271 r = radv_send(ra, &src, ra->lifetime);
272 if (r < 0)
273 log_radv_warning_errno(r, "Unable to send solicited Router Advertisment to %s: %m", addr);
274 else
275 log_radv("Sent solicited Router Advertisement to %s", addr);
276
277 return 0;
278 }
279
280 static usec_t radv_compute_timeout(usec_t min, usec_t max) {
281 assert_return(min <= max, SD_RADV_DEFAULT_MIN_TIMEOUT_USEC);
282
283 return min + (random_u32() % (max - min));
284 }
285
286 static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
287 int r;
288 sd_radv *ra = userdata;
289 usec_t min_timeout = SD_RADV_DEFAULT_MIN_TIMEOUT_USEC;
290 usec_t max_timeout = SD_RADV_DEFAULT_MAX_TIMEOUT_USEC;
291 usec_t time_now, timeout;
292 char time_string[FORMAT_TIMESPAN_MAX];
293
294 assert(s);
295 assert(ra);
296 assert(ra->event);
297
298 ra->timeout_event_source = sd_event_source_unref(ra->timeout_event_source);
299
300 r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
301 if (r < 0)
302 goto fail;
303
304 r = radv_send(ra, NULL, ra->lifetime);
305 if (r < 0)
306 log_radv_warning_errno(r, "Unable to send Router Advertisement: %m");
307
308 /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
309 if (ra->ra_sent < SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) {
310 max_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
311 min_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC / 3;
312 }
313
314 timeout = radv_compute_timeout(min_timeout, max_timeout);
315
316 log_radv("Next Router Advertisement in %s",
317 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
318 timeout, USEC_PER_SEC));
319
320 r = sd_event_add_time(ra->event, &ra->timeout_event_source,
321 clock_boottime_or_monotonic(),
322 time_now + timeout, MSEC_PER_SEC,
323 radv_timeout, ra);
324 if (r < 0)
325 goto fail;
326
327 r = sd_event_source_set_priority(ra->timeout_event_source,
328 ra->event_priority);
329 if (r < 0)
330 goto fail;
331
332 r = sd_event_source_set_description(ra->timeout_event_source,
333 "radv-timeout");
334 if (r < 0)
335 goto fail;
336
337 ra->ra_sent++;
338
339 fail:
340 if (r < 0)
341 sd_radv_stop(ra);
342
343 return 0;
344 }
345
346 _public_ int sd_radv_stop(sd_radv *ra) {
347 int r;
348
349 assert_return(ra, -EINVAL);
350
351 log_radv("Stopping IPv6 Router Advertisement daemon");
352
353 /* RFC 4861, Section 6.2.5, send at least one Router Advertisement
354 with zero lifetime */
355 r = radv_send(ra, NULL, 0);
356 if (r < 0)
357 log_radv_warning_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m");
358
359 radv_reset(ra);
360 ra->fd = safe_close(ra->fd);
361 ra->state = SD_RADV_STATE_IDLE;
362
363 return 0;
364 }
365
366 _public_ int sd_radv_start(sd_radv *ra) {
367 int r = 0;
368
369 assert_return(ra, -EINVAL);
370 assert_return(ra->event, -EINVAL);
371 assert_return(ra->ifindex > 0, -EINVAL);
372
373 if (ra->state != SD_RADV_STATE_IDLE)
374 return 0;
375
376 r = sd_event_add_time(ra->event, &ra->timeout_event_source,
377 clock_boottime_or_monotonic(), 0, 0,
378 radv_timeout, ra);
379 if (r < 0)
380 goto fail;
381
382 r = sd_event_source_set_priority(ra->timeout_event_source,
383 ra->event_priority);
384 if (r < 0)
385 goto fail;
386
387 (void) sd_event_source_set_description(ra->timeout_event_source,
388 "radv-timeout");
389
390 r = icmp6_bind_router_advertisement(ra->ifindex);
391 if (r < 0)
392 goto fail;
393
394 ra->fd = r;
395
396 r = sd_event_add_io(ra->event, &ra->recv_event_source, ra->fd, EPOLLIN, radv_recv, ra);
397 if (r < 0)
398 goto fail;
399
400 r = sd_event_source_set_priority(ra->recv_event_source, ra->event_priority);
401 if (r < 0)
402 goto fail;
403
404 (void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message");
405
406 ra->state = SD_RADV_STATE_ADVERTISING;
407
408 log_radv("Started IPv6 Router Advertisement daemon");
409
410 return 0;
411
412 fail:
413 radv_reset(ra);
414
415 return r;
416 }
417
418 _public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
419 assert_return(ra, -EINVAL);
420 assert_return(ifindex >= -1, -EINVAL);
421
422 if (ra->state != SD_RADV_STATE_IDLE)
423 return -EBUSY;
424
425 ra->ifindex = ifindex;
426
427 return 0;
428 }
429
430 _public_ int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
431 assert_return(ra, -EINVAL);
432
433 if (ra->state != SD_RADV_STATE_IDLE)
434 return -EBUSY;
435
436 if (mac_addr)
437 ra->mac_addr = *mac_addr;
438 else
439 zero(ra->mac_addr);
440
441 return 0;
442 }
443
444 _public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
445 assert_return(ra, -EINVAL);
446 assert_return(mtu >= 1280, -EINVAL);
447
448 if (ra->state != SD_RADV_STATE_IDLE)
449 return -EBUSY;
450
451 ra->mtu = mtu;
452
453 return 0;
454 }
455
456 _public_ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
457 assert_return(ra, -EINVAL);
458
459 if (ra->state != SD_RADV_STATE_IDLE)
460 return -EBUSY;
461
462 ra->hop_limit = hop_limit;
463
464 return 0;
465 }
466
467 _public_ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime) {
468 assert_return(ra, -EINVAL);
469
470 if (ra->state != SD_RADV_STATE_IDLE)
471 return -EBUSY;
472
473 /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the
474 preference value MUST be set to (00) by the sender..." */
475 if (router_lifetime == 0 &&
476 (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
477 return -ETIME;
478
479 ra->lifetime = router_lifetime;
480
481 return 0;
482 }
483
484 _public_ int sd_radv_set_managed_information(sd_radv *ra, int managed) {
485 assert_return(ra, -EINVAL);
486
487 if (ra->state != SD_RADV_STATE_IDLE)
488 return -EBUSY;
489
490 SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
491
492 return 0;
493 }
494
495 _public_ int sd_radv_set_other_information(sd_radv *ra, int other) {
496 assert_return(ra, -EINVAL);
497
498 if (ra->state != SD_RADV_STATE_IDLE)
499 return -EBUSY;
500
501 SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
502
503 return 0;
504 }
505
506 _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
507 int r = 0;
508
509 assert_return(ra, -EINVAL);
510 assert_return(IN_SET(preference,
511 SD_NDISC_PREFERENCE_LOW,
512 SD_NDISC_PREFERENCE_MEDIUM,
513 SD_NDISC_PREFERENCE_HIGH), -EINVAL);
514
515 ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
516
517 return r;
518 }
519
520 _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
521 sd_radv_prefix *cur;
522 _cleanup_free_ char *addr_p = NULL;
523
524 assert_return(ra, -EINVAL);
525
526 if (!p)
527 return -EINVAL;
528
529 LIST_FOREACH(prefix, cur, ra->prefixes) {
530 int r;
531
532 r = in_addr_prefix_intersect(AF_INET6,
533 (union in_addr_union*) &cur->opt.in6_addr,
534 cur->opt.prefixlen,
535 (union in_addr_union*) &p->opt.in6_addr,
536 p->opt.prefixlen);
537 if (r > 0) {
538 _cleanup_free_ char *addr_cur = NULL;
539
540 (void) in_addr_to_string(AF_INET6,
541 (union in_addr_union*) &cur->opt.in6_addr,
542 &addr_cur);
543 (void) in_addr_to_string(AF_INET6,
544 (union in_addr_union*) &p->opt.in6_addr,
545 &addr_p);
546
547 log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u",
548 addr_cur, cur->opt.prefixlen,
549 addr_p, p->opt.prefixlen);
550
551 return -EEXIST;
552 }
553 }
554
555 p = sd_radv_prefix_ref(p);
556
557 LIST_APPEND(prefix, ra->prefixes, p);
558
559 ra->n_prefixes++;
560
561 (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p);
562 log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen);
563
564 return 0;
565 }
566
567 _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime,
568 const struct in6_addr *dns, size_t n_dns) {
569 _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
570 size_t len;
571
572 assert_return(ra, -EINVAL);
573 assert_return(n_dns < 128, -EINVAL);
574
575 if (!dns || n_dns == 0) {
576 ra->rdnss = mfree(ra->rdnss);
577 ra->n_rdnss = 0;
578
579 return 0;
580 }
581
582 len = sizeof(struct sd_radv_opt_dns) + sizeof(struct in6_addr) * n_dns;
583
584 opt_rdnss = malloc0(len);
585 if (!opt_rdnss)
586 return -ENOMEM;
587
588 opt_rdnss->type = SD_RADV_OPT_RDNSS;
589 opt_rdnss->length = len / 8;
590 opt_rdnss->lifetime = htobe32(lifetime);
591
592 memcpy(opt_rdnss + 1, dns, n_dns * sizeof(struct in6_addr));
593
594 free(ra->rdnss);
595 ra->rdnss = opt_rdnss;
596 opt_rdnss = NULL;
597
598 ra->n_rdnss = n_dns;
599
600 return 0;
601 }
602
603 _public_ int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime,
604 char **search_list) {
605 _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL;
606 size_t len = 0;
607 char **s;
608 uint8_t *p;
609
610 assert_return(ra, -EINVAL);
611
612 if (!search_list || *search_list == NULL) {
613 ra->dnssl = mfree(ra->dnssl);
614
615 return 0;
616 }
617
618 STRV_FOREACH(s, search_list)
619 len += strlen(*s) + 2;
620
621 len = (sizeof(struct sd_radv_opt_dns) + len + 7) & ~0x7;
622
623 opt_dnssl = malloc0(len);
624 if (!opt_dnssl)
625 return -ENOMEM;
626
627 opt_dnssl->type = SD_RADV_OPT_DNSSL;
628 opt_dnssl->length = len / 8;
629 opt_dnssl->lifetime = htobe32(lifetime);
630
631 p = (uint8_t *)(opt_dnssl + 1);
632 len -= sizeof(struct sd_radv_opt_dns);
633
634 STRV_FOREACH(s, search_list) {
635 int r;
636
637 r = dns_name_to_wire_format(*s, p, len, false);
638 if (r < 0)
639 return r;
640
641 if (len < (size_t)r)
642 return -ENOBUFS;
643
644 p += r;
645 len -= r;
646 }
647
648 free(ra->dnssl);
649 ra->dnssl = opt_dnssl;
650 opt_dnssl = NULL;
651
652 return 0;
653 }
654
655 _public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
656 _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
657
658 assert_return(ret, -EINVAL);
659
660 p = new0(sd_radv_prefix, 1);
661 if (!p)
662 return -ENOMEM;
663
664 p->n_ref = 1;
665
666 p->opt.type = ND_OPT_PREFIX_INFORMATION;
667 p->opt.length = (sizeof(p->opt) - 1) /8 + 1;
668
669 p->opt.prefixlen = 64;
670
671 /* RFC 4861, Section 6.2.1 */
672 SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, true);
673 SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, true);
674 p->opt.preferred_lifetime = htobe32(604800);
675 p->opt.valid_lifetime = htobe32(2592000);
676
677 LIST_INIT(prefix, p);
678
679 *ret = p;
680 p = NULL;
681
682 return 0;
683 }
684
685 _public_ sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *p) {
686 if (!p)
687 return NULL;
688
689 assert(p->n_ref > 0);
690 p->n_ref++;
691
692 return p;
693 }
694
695 _public_ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *p) {
696 if (!p)
697 return NULL;
698
699 assert(p->n_ref > 0);
700 p->n_ref--;
701
702 if (p->n_ref > 0)
703 return NULL;
704
705 return mfree(p);
706 }
707
708 _public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, struct in6_addr *in6_addr,
709 unsigned char prefixlen) {
710 assert_return(p, -EINVAL);
711 assert_return(in6_addr, -EINVAL);
712
713 if (prefixlen < 3 || prefixlen > 128)
714 return -EINVAL;
715
716 if (prefixlen > 64)
717 /* unusual but allowed, log it */
718 log_radv("Unusual prefix length %d greater than 64", prefixlen);
719
720 p->opt.in6_addr = *in6_addr;
721 p->opt.prefixlen = prefixlen;
722
723 return 0;
724 }
725
726 _public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
727 assert_return(p, -EINVAL);
728
729 SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink);
730
731 return 0;
732 }
733
734 _public_ int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
735 int address_autoconfiguration) {
736 assert_return(p, -EINVAL);
737
738 SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
739
740 return 0;
741 }
742
743 _public_ int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p,
744 uint32_t valid_lifetime) {
745 assert_return(p, -EINVAL);
746
747 p->opt.valid_lifetime = htobe32(valid_lifetime);
748
749 return 0;
750 }
751
752 _public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,
753 uint32_t preferred_lifetime) {
754 assert_return(p, -EINVAL);
755
756 p->opt.preferred_lifetime = htobe32(preferred_lifetime);
757
758 return 0;
759 }