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