]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-radv.c
sd-radv: expose sd_radv_send()
[thirdparty/systemd.git] / src / libsystemd-network / sd-radv.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
04473969 2/***
810adae9 3 Copyright © 2017 Intel Corporation. All rights reserved.
04473969
PF
4***/
5
6#include <netinet/icmp6.h>
7#include <netinet/in.h>
204f99d2 8#include <arpa/inet.h>
04473969
PF
9
10#include "sd-radv.h"
11
12#include "alloc-util.h"
e965d6ab 13#include "dns-domain.h"
ae25915d 14#include "ether-addr-util.h"
807a8ede 15#include "event-util.h"
04473969
PF
16#include "fd-util.h"
17#include "icmp6-util.h"
18#include "in-addr-util.h"
bd1ae178 19#include "iovec-util.h"
5cfa2c3d 20#include "macro.h"
0a970718 21#include "memory-util.h"
cc2bcbf6 22#include "ndisc-router-solicit-internal.h"
61a9fa8f 23#include "network-common.h"
04473969 24#include "radv-internal.h"
5cfa2c3d 25#include "random-util.h"
04473969
PF
26#include "socket-util.h"
27#include "string-util.h"
e965d6ab 28#include "strv.h"
1925f829 29#include "unaligned.h"
04473969 30
17347053 31int sd_radv_new(sd_radv **ret) {
204f99d2
PF
32 _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
33
34 assert_return(ret, -EINVAL);
35
78f9d24f 36 ra = new(sd_radv, 1);
204f99d2
PF
37 if (!ra)
38 return -ENOMEM;
39
78f9d24f
YW
40 *ra = (sd_radv) {
41 .n_ref = 1,
254d1313 42 .fd = -EBADF,
72db0a71 43 .lifetime_usec = RADV_DEFAULT_ROUTER_LIFETIME_USEC,
78f9d24f 44 };
204f99d2 45
1cc6c93a 46 *ret = TAKE_PTR(ra);
204f99d2
PF
47
48 return 0;
49}
50
17347053 51int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
204f99d2
PF
52 int r;
53
54 assert_return(ra, -EINVAL);
55 assert_return(!ra->event, -EBUSY);
56
57 if (event)
58 ra->event = sd_event_ref(event);
59 else {
60 r = sd_event_default(&ra->event);
61 if (r < 0)
62 return 0;
63 }
64
65 ra->event_priority = priority;
66
67 return 0;
68}
69
17347053 70int sd_radv_detach_event(sd_radv *ra) {
204f99d2
PF
71
72 assert_return(ra, -EINVAL);
73
74 ra->event = sd_event_unref(ra->event);
75 return 0;
76}
77
17347053 78sd_event *sd_radv_get_event(sd_radv *ra) {
204f99d2
PF
79 assert_return(ra, NULL);
80
81 return ra->event;
82}
83
17347053 84int sd_radv_is_running(sd_radv *ra) {
8e91738f
YW
85 if (!ra)
86 return false;
96fe813c 87
d6fc0d67 88 return ra->state != RADV_STATE_IDLE;
96fe813c
YW
89}
90
204fb681 91static void radv_reset(sd_radv *ra) {
c4b6dda0 92 assert(ra);
204fb681 93
807a8ede 94 (void) event_source_disable(ra->timeout_event_source);
204fb681 95
eb2f7502 96 ra->recv_event_source = sd_event_source_disable_unref(ra->recv_event_source);
88d5a3db 97
204fb681
PF
98 ra->ra_sent = 0;
99}
100
8301aa0b 101static sd_radv *radv_free(sd_radv *ra) {
e866e17b
LP
102 if (!ra)
103 return NULL;
204f99d2 104
9aad490e
DT
105 LIST_CLEAR(prefix, ra->prefixes, sd_radv_prefix_unref);
106 LIST_CLEAR(prefix, ra->route_prefixes, sd_radv_route_prefix_unref);
1a6b1214 107 LIST_CLEAR(prefix, ra->pref64_prefixes, sd_radv_pref64_prefix_unref);
69d7eba1 108
e9c6da38 109 free(ra->rdnss);
f9aa5417 110 free(ra->dnssl);
e9c6da38 111
204fb681
PF
112 radv_reset(ra);
113
eb2f7502 114 sd_event_source_unref(ra->timeout_event_source);
204f99d2 115 sd_radv_detach_event(ra);
c4b6dda0
LP
116
117 ra->fd = safe_close(ra->fd);
61a9fa8f 118 free(ra->ifname);
c4b6dda0 119
204f99d2
PF
120 return mfree(ra);
121}
122
8301aa0b
YW
123DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free);
124
09457670 125static bool router_lifetime_is_valid(usec_t lifetime_usec) {
36653443 126 assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
09457670
YW
127 return lifetime_usec == 0 ||
128 (lifetime_usec >= RADV_MIN_ROUTER_LIFETIME_USEC &&
129 lifetime_usec <= RADV_MAX_ROUTER_LIFETIME_USEC);
130}
131
19f3cc86
YW
132static int radv_send_router_on_stop(sd_radv *ra) {
133 static const struct nd_router_advert adv = {
134 .nd_ra_type = ND_ROUTER_ADVERT,
135 };
136
137 _cleanup_set_free_ Set *options = NULL;
138 usec_t time_now;
139 int r;
140
141 assert(ra);
142
143 r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
144 if (r < 0)
145 return r;
146
147 if (!ether_addr_is_null(&ra->mac_addr)) {
148 r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &ra->mac_addr);
149 if (r < 0)
150 return r;
151 }
152
153 return ndisc_send(ra->fd, &IN6_ADDR_ALL_NODES_MULTICAST, &adv.nd_ra_hdr, options, time_now);
154}
155
69628e3b 156static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
36653443 157 assert(ra);
36653443 158
77baf5ae
PF
159 struct sockaddr_in6 dst_addr = {
160 .sin6_family = AF_INET6,
2c28eb02 161 .sin6_addr = IN6_ADDR_ALL_NODES_MULTICAST,
77baf5ae 162 };
36653443
YW
163 struct nd_router_advert adv = {
164 .nd_ra_type = ND_ROUTER_ADVERT,
69628e3b 165 .nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec),
36653443
YW
166 .nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec),
167 };
77baf5ae
PF
168 struct {
169 struct nd_opt_hdr opthdr;
170 struct ether_addr slladdr;
171 } _packed_ opt_mac = {
172 .opthdr = {
173 .nd_opt_type = ND_OPT_SOURCE_LINKADDR,
36653443 174 .nd_opt_len = DIV_ROUND_UP(sizeof(struct nd_opt_hdr) + sizeof(struct ether_addr), 8),
77baf5ae 175 },
36653443 176 .slladdr = ra->mac_addr,
77baf5ae
PF
177 };
178 struct nd_opt_mtu opt_mtu = {
179 .nd_opt_mtu_type = ND_OPT_MTU,
180 .nd_opt_mtu_len = 1,
36653443 181 .nd_opt_mtu_mtu = htobe32(ra->mtu),
77baf5ae 182 };
ac63c8df
YW
183 /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, N pref64 prefixes, RDNSS,
184 * DNSSL, and home agent. */
185 struct iovec iov[6 + ra->n_prefixes + ra->n_route_prefixes + ra->n_pref64_prefixes];
77baf5ae
PF
186 struct msghdr msg = {
187 .msg_name = &dst_addr,
188 .msg_namelen = sizeof(dst_addr),
189 .msg_iov = iov,
190 };
d601b566
PF
191 usec_t time_now;
192 int r;
193
ba4e0427 194 r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
d601b566
PF
195 if (r < 0)
196 return r;
77baf5ae 197
94876904 198 if (dst && in6_addr_is_set(dst))
77baf5ae 199 dst_addr.sin6_addr = *dst;
88d5a3db 200
36653443
YW
201 /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
202 * simultaneously in the structured initializer in the above. */
77baf5ae
PF
203 adv.nd_ra_curhoplimit = ra->hop_limit;
204 adv.nd_ra_flags_reserved = ra->flags;
5cfa2c3d 205 iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
77baf5ae 206
36653443
YW
207 /* MAC address is optional, either because the link does not use L2 addresses or load sharing is
208 * desired. See RFC 4861, Section 4.2. */
209 if (!ether_addr_is_null(&ra->mac_addr))
5cfa2c3d 210 iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mac, sizeof(opt_mac));
77baf5ae 211
36653443 212 if (ra->mtu > 0)
5cfa2c3d 213 iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mtu, sizeof(opt_mtu));
77baf5ae
PF
214
215 LIST_FOREACH(prefix, p, ra->prefixes) {
95e104e0 216 usec_t lifetime_valid_usec, lifetime_preferred_usec;
d601b566 217
95e104e0
YW
218 lifetime_valid_usec = MIN(usec_sub_unsigned(p->valid_until, time_now),
219 p->lifetime_valid_usec);
220
221 lifetime_preferred_usec = MIN3(usec_sub_unsigned(p->preferred_until, time_now),
222 p->lifetime_preferred_usec,
223 lifetime_valid_usec);
224
225 p->opt.lifetime_valid = usec_to_be32_sec(lifetime_valid_usec);
226 p->opt.lifetime_preferred = usec_to_be32_sec(lifetime_preferred_usec);
d601b566 227
5cfa2c3d 228 iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
77baf5ae
PF
229 }
230
95e104e0
YW
231 LIST_FOREACH(prefix, rt, ra->route_prefixes) {
232 rt->opt.lifetime = usec_to_be32_sec(MIN(usec_sub_unsigned(rt->valid_until, time_now),
233 rt->lifetime_usec));
234
203d4df5 235 iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt));
95e104e0 236 }
203d4df5 237
1925f829
SS
238 LIST_FOREACH(prefix, p, ra->pref64_prefixes)
239 iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
240
5cfa2c3d
LP
241 if (ra->rdnss)
242 iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8);
e9c6da38 243
5cfa2c3d
LP
244 if (ra->dnssl)
245 iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->dnssl, ra->dnssl->length * 8);
e965d6ab 246
6a6d27bc
SS
247 if (FLAGS_SET(ra->flags, ND_RA_FLAG_HOME_AGENT)) {
248 ra->home_agent.nd_opt_home_agent_info_type = ND_OPT_HOME_AGENT_INFO;
249 ra->home_agent.nd_opt_home_agent_info_len = 1;
250
251 /* 0 means to place the current Router Lifetime value */
252 if (ra->home_agent.nd_opt_home_agent_info_lifetime == 0)
253 ra->home_agent.nd_opt_home_agent_info_lifetime = adv.nd_ra_router_lifetime;
254
255 iov[msg.msg_iovlen++] = IOVEC_MAKE(&ra->home_agent, sizeof(ra->home_agent));
256 }
257
77baf5ae
PF
258 if (sendmsg(ra->fd, &msg, 0) < 0)
259 return -errno;
204fb681 260
77baf5ae 261 return 0;
204fb681
PF
262}
263
cc2bcbf6 264static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
88d5a3db 265 int r;
88d5a3db 266
cc2bcbf6
YW
267 assert(ra);
268 assert(packet);
88d5a3db 269
cc2bcbf6
YW
270 if (icmp6_packet_get_type(packet) != ND_ROUTER_SOLICIT)
271 return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), "Received ICMP6 packet with unexpected type, ignoring.");
88d5a3db 272
cc2bcbf6
YW
273 _cleanup_(sd_ndisc_router_solicit_unrefp) sd_ndisc_router_solicit *rs = NULL;
274 rs = ndisc_router_solicit_new(packet);
275 if (!rs)
276 return log_oom_debug();
88d5a3db 277
cc2bcbf6 278 r = ndisc_router_solicit_parse(ra, rs);
1f2db2e3 279 if (r < 0)
cc2bcbf6 280 return r;
88d5a3db 281
cc2bcbf6
YW
282 struct in6_addr src = {};
283 r = sd_ndisc_router_solicit_get_sender_address(rs, &src);
284 if (r < 0 && r != -ENODATA) /* null address is allowed */
285 return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m");
50ba4e40
YW
286 if (r >= 0 && in6_addr_equal(&src, &ra->ipv6ll))
287 /* This should be definitely caused by a misconfiguration. If we send RA to ourself, the
288 * kernel complains about that. Let's ignore the packet. */
289 return log_radv_errno(ra, SYNTHETIC_ERRNO(EADDRINUSE), "Received RS from the same interface, ignoring.");
88d5a3db 290
69628e3b 291 r = radv_send_router(ra, &src);
cc2bcbf6
YW
292 if (r < 0)
293 return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src));
88d5a3db 294
cc2bcbf6
YW
295 log_radv(ra, "Sent solicited Router Advertisement to %s.", IN6_ADDR_TO_STRING(&src));
296 return 0;
297}
88d5a3db 298
cc2bcbf6
YW
299static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
300 _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
301 sd_radv *ra = ASSERT_PTR(userdata);
302 int r;
cfffddea 303
cc2bcbf6 304 assert(fd >= 0);
4961f566 305
cc2bcbf6
YW
306 r = icmp6_packet_receive(fd, &packet);
307 if (r < 0) {
308 log_radv_errno(ra, r, "Failed to receive ICMPv6 packet, ignoring: %m");
309 return 0;
310 }
88d5a3db 311
cc2bcbf6 312 (void) radv_process_packet(ra, packet);
88d5a3db
PF
313 return 0;
314}
315
204fb681 316static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
99534007 317 sd_radv *ra = ASSERT_PTR(userdata);
16e4dce6
YW
318
319 if (sd_radv_send(ra) < 0)
320 (void) sd_radv_stop(ra);
321
322 return 0;
323}
324
325int sd_radv_send(sd_radv *ra) {
326 usec_t min_timeout, max_timeout, time_now, timeout;
7003b114 327 int r;
204fb681 328
16e4dce6
YW
329 assert_return(ra, -EINVAL);
330 assert_return(ra->event, -EINVAL);
331 assert_return(sd_radv_is_running(ra), -EINVAL);
80477557 332 assert(router_lifetime_is_valid(ra->lifetime_usec));
204fb681 333
ba4e0427 334 r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
204fb681 335 if (r < 0)
16e4dce6 336 return r;
204fb681 337
69628e3b 338 r = radv_send_router(ra, NULL);
204fb681 339 if (r < 0)
16e4dce6
YW
340 return log_radv_errno(ra, r, "Unable to send Router Advertisement: %m");
341
342 ra->ra_sent++;
204fb681
PF
343
344 /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
16e4dce6 345 if (ra->ra_sent <= RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
d6fc0d67 346 max_timeout = RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
80477557 347 else
03ec10fd 348 max_timeout = RADV_DEFAULT_MAX_TIMEOUT_USEC;
204fb681 349
ef90b6a4 350 /* RFC 4861, Section 6.2.1, lifetime must be at least MaxRtrAdvInterval,
80477557
YW
351 * so lower the interval here */
352 if (ra->lifetime_usec > 0)
353 max_timeout = MIN(max_timeout, ra->lifetime_usec);
354
355 if (max_timeout >= 9 * USEC_PER_SEC)
ef90b6a4 356 min_timeout = max_timeout / 3;
80477557
YW
357 else
358 min_timeout = max_timeout * 3 / 4;
ef90b6a4 359
80477557
YW
360 /* RFC 4861, Section 6.2.1.
361 * MaxRtrAdvInterval MUST be no less than 4 seconds and no greater than 1800 seconds.
362 * MinRtrAdvInterval MUST be no less than 3 seconds and no greater than .75 * MaxRtrAdvInterval. */
363 assert(max_timeout >= RADV_MIN_MAX_TIMEOUT_USEC);
364 assert(max_timeout <= RADV_MAX_MAX_TIMEOUT_USEC);
365 assert(min_timeout >= RADV_MIN_MIN_TIMEOUT_USEC);
366 assert(min_timeout <= max_timeout * 3 / 4);
ef90b6a4 367
80477557 368 timeout = min_timeout + random_u64_range(max_timeout - min_timeout);
16e4dce6
YW
369 log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.",
370 FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
371
372 return event_reset_time(
373 ra->event, &ra->timeout_event_source,
374 CLOCK_BOOTTIME,
375 usec_add(time_now, timeout), MSEC_PER_SEC,
376 radv_timeout, ra,
377 ra->event_priority, "radv-timeout", true);
204fb681
PF
378}
379
17347053 380int sd_radv_stop(sd_radv *ra) {
204fb681
PF
381 int r;
382
c8bae363
YW
383 if (!ra)
384 return 0;
204f99d2 385
d6fc0d67 386 if (ra->state == RADV_STATE_IDLE)
6f8a8b84
SS
387 return 0;
388
35388783 389 log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
204f99d2 390
36653443
YW
391 /* RFC 4861, Section 6.2.5:
392 * the router SHOULD transmit one or more (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final
393 * multicast Router Advertisements on the interface with a Router Lifetime field of zero. */
19f3cc86 394 r = radv_send_router_on_stop(ra);
290696e5 395 if (r < 0)
abb977a6 396 log_radv_errno(ra, r, "Unable to send last Router Advertisement with router lifetime set to zero, ignoring: %m");
204fb681
PF
397
398 radv_reset(ra);
77baf5ae 399 ra->fd = safe_close(ra->fd);
d6fc0d67 400 ra->state = RADV_STATE_IDLE;
204f99d2
PF
401
402 return 0;
403}
404
36653443
YW
405static int radv_setup_recv_event(sd_radv *ra) {
406 int r;
407
408 assert(ra);
409 assert(ra->event);
410 assert(ra->ifindex > 0);
411
412 _cleanup_close_ int fd = -EBADF;
413 fd = icmp6_bind(ra->ifindex, /* is_router = */ true);
414 if (fd < 0)
415 return fd;
416
417 _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
418 r = sd_event_add_io(ra->event, &s, fd, EPOLLIN, radv_recv, ra);
419 if (r < 0)
420 return r;
421
422 r = sd_event_source_set_priority(s, ra->event_priority);
423 if (r < 0)
424 return r;
425
426 (void) sd_event_source_set_description(s, "radv-receive-message");
427
428 ra->fd = TAKE_FD(fd);
429 ra->recv_event_source = TAKE_PTR(s);
430 return 0;
431}
432
17347053 433int sd_radv_start(sd_radv *ra) {
f474884c 434 int r;
204fb681 435
204f99d2
PF
436 assert_return(ra, -EINVAL);
437 assert_return(ra->event, -EINVAL);
438 assert_return(ra->ifindex > 0, -EINVAL);
439
d6fc0d67 440 if (ra->state != RADV_STATE_IDLE)
204f99d2
PF
441 return 0;
442
36653443
YW
443 r = radv_setup_recv_event(ra);
444 if (r < 0)
445 goto fail;
446
807a8ede 447 r = event_reset_time(ra->event, &ra->timeout_event_source,
ba4e0427 448 CLOCK_BOOTTIME,
807a8ede
YW
449 0, 0,
450 radv_timeout, ra,
451 ra->event_priority, "radv-timeout", true);
204fb681
PF
452 if (r < 0)
453 goto fail;
454
d6fc0d67 455 ra->state = RADV_STATE_ADVERTISING;
204f99d2 456
35388783 457 log_radv(ra, "Started IPv6 Router Advertisement daemon");
204f99d2
PF
458
459 return 0;
204fb681
PF
460
461 fail:
462 radv_reset(ra);
463
464 return r;
204f99d2
PF
465}
466
17347053 467int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
204f99d2 468 assert_return(ra, -EINVAL);
7fa69c0a 469 assert_return(ifindex > 0, -EINVAL);
204f99d2 470
d6fc0d67 471 if (ra->state != RADV_STATE_IDLE)
204f99d2
PF
472 return -EBUSY;
473
474 ra->ifindex = ifindex;
475
476 return 0;
477}
478
61a9fa8f
YW
479int sd_radv_set_ifname(sd_radv *ra, const char *ifname) {
480 assert_return(ra, -EINVAL);
481 assert_return(ifname, -EINVAL);
482
483 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
484 return -EINVAL;
485
486 return free_and_strdup(&ra->ifname, ifname);
487}
488
5977b71f
YW
489int sd_radv_get_ifname(sd_radv *ra, const char **ret) {
490 int r;
491
492 assert_return(ra, -EINVAL);
61a9fa8f 493
5977b71f
YW
494 r = get_ifname(ra->ifindex, &ra->ifname);
495 if (r < 0)
496 return r;
497
498 if (ret)
499 *ret = ra->ifname;
500
501 return 0;
61a9fa8f
YW
502}
503
50ba4e40
YW
504int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr) {
505 assert_return(ra, -EINVAL);
506 assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL);
507
508 if (addr)
509 ra->ipv6ll = *addr;
510 else
511 zero(ra->ipv6ll);
512
513 return 0;
514}
515
17347053 516int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
204f99d2
PF
517 assert_return(ra, -EINVAL);
518
d6fc0d67 519 if (ra->state != RADV_STATE_IDLE)
204f99d2
PF
520 return -EBUSY;
521
522 if (mac_addr)
523 ra->mac_addr = *mac_addr;
524 else
525 zero(ra->mac_addr);
526
527 return 0;
528}
529
17347053 530int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
204f99d2
PF
531 assert_return(ra, -EINVAL);
532 assert_return(mtu >= 1280, -EINVAL);
533
204f99d2
PF
534 ra->mtu = mtu;
535
536 return 0;
537}
538
17347053 539int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
204f99d2
PF
540 assert_return(ra, -EINVAL);
541
d6fc0d67 542 if (ra->state != RADV_STATE_IDLE)
204f99d2
PF
543 return -EBUSY;
544
545 ra->hop_limit = hop_limit;
546
547 return 0;
548}
549
eca280c8 550int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
fdc4c67c
SS
551 assert_return(ra, -EINVAL);
552
553 if (ra->state != RADV_STATE_IDLE)
554 return -EBUSY;
555
eca280c8
YW
556 if (usec > RADV_MAX_RETRANSMIT_USEC)
557 return -EINVAL;
fdc4c67c 558
eca280c8 559 ra->retransmit_usec = usec;
fdc4c67c
SS
560 return 0;
561}
562
eca280c8 563int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec) {
204f99d2
PF
564 assert_return(ra, -EINVAL);
565
d6fc0d67 566 if (ra->state != RADV_STATE_IDLE)
204f99d2
PF
567 return -EBUSY;
568
eca280c8 569 if (!router_lifetime_is_valid(usec))
09457670
YW
570 return -EINVAL;
571
ac138551
YW
572 /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
573 * to (00) by the sender..." */
eca280c8 574 if (usec == 0 &&
204f99d2 575 (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
09457670 576 return -EINVAL;
204f99d2 577
eca280c8 578 ra->lifetime_usec = usec;
204f99d2
PF
579 return 0;
580}
581
17347053 582int sd_radv_set_managed_information(sd_radv *ra, int managed) {
204f99d2
PF
583 assert_return(ra, -EINVAL);
584
d6fc0d67 585 if (ra->state != RADV_STATE_IDLE)
204f99d2
PF
586 return -EBUSY;
587
588 SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
589
590 return 0;
591}
592
17347053 593int sd_radv_set_other_information(sd_radv *ra, int other) {
204f99d2
PF
594 assert_return(ra, -EINVAL);
595
d6fc0d67 596 if (ra->state != RADV_STATE_IDLE)
204f99d2
PF
597 return -EBUSY;
598
599 SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
600
601 return 0;
602}
603
17347053 604int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
204f99d2
PF
605 assert_return(ra, -EINVAL);
606 assert_return(IN_SET(preference,
607 SD_NDISC_PREFERENCE_LOW,
608 SD_NDISC_PREFERENCE_MEDIUM,
609 SD_NDISC_PREFERENCE_HIGH), -EINVAL);
610
dd1b1870
YW
611 /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
612 * to (00) by the sender..." */
7003b114 613 if (ra->lifetime_usec == 0 && preference != SD_NDISC_PREFERENCE_MEDIUM)
dd1b1870
YW
614 return -EINVAL;
615
204f99d2
PF
616 ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
617
dd1b1870 618 return 0;
204f99d2
PF
619}
620
6a6d27bc
SS
621int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
622 assert_return(ra, -EINVAL);
623
624 if (ra->state != RADV_STATE_IDLE)
625 return -EBUSY;
626
627 SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
628
629 return 0;
630}
631
632int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
633 assert_return(ra, -EINVAL);
634
635 if (ra->state != RADV_STATE_IDLE)
636 return -EBUSY;
637
638 ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
639
640 return 0;
641}
642
eca280c8 643int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
6a6d27bc
SS
644 assert_return(ra, -EINVAL);
645
646 if (ra->state != RADV_STATE_IDLE)
647 return -EBUSY;
648
eca280c8
YW
649 if (lifetime_usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC)
650 return -EINVAL;
6a6d27bc 651
eca280c8 652 ra->home_agent.nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime_usec);
6a6d27bc
SS
653 return 0;
654}
655
17347053 656int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
03677889 657 sd_radv_prefix *found = NULL;
d601b566 658 int r;
204f99d2
PF
659
660 assert_return(ra, -EINVAL);
acbb5500 661 assert_return(p, -EINVAL);
204f99d2 662
59ea6e57 663 /* Refuse prefixes that don't have a prefix set */
94876904 664 if (in6_addr_is_null(&p->opt.in6_addr))
59ea6e57
LP
665 return -ENOEXEC;
666
c71384a9 667 const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
5380707a 668
204f99d2 669 LIST_FOREACH(prefix, cur, ra->prefixes) {
204f99d2 670 r = in_addr_prefix_intersect(AF_INET6,
c633628d 671 (const union in_addr_union*) &cur->opt.in6_addr,
204f99d2 672 cur->opt.prefixlen,
c633628d 673 (const union in_addr_union*) &p->opt.in6_addr,
204f99d2 674 p->opt.prefixlen);
5380707a
YW
675 if (r < 0)
676 return r;
677 if (r == 0)
678 continue;
204f99d2 679
95e104e0 680 if (cur->opt.prefixlen == p->opt.prefixlen) {
56aa5143 681 found = cur;
b9f27a05 682 break;
95e104e0 683 }
d601b566 684
35388783 685 return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
95e104e0 686 "IPv6 prefix %s conflicts with %s, ignoring.",
c71384a9
ZJS
687 addr_p,
688 IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
204f99d2
PF
689 }
690
56aa5143 691 if (found) {
b9f27a05
YW
692 /* p and cur may be equivalent. First increment the reference counter. */
693 sd_radv_prefix_ref(p);
694
695 /* Then, remove the old entry. */
56aa5143
YW
696 LIST_REMOVE(prefix, ra->prefixes, found);
697 sd_radv_prefix_unref(found);
b9f27a05
YW
698
699 /* Finally, add the new entry. */
700 LIST_APPEND(prefix, ra->prefixes, p);
701
702 log_radv(ra, "Updated/replaced IPv6 prefix %s (preferred: %s, valid: %s)",
c71384a9 703 addr_p,
b9f27a05
YW
704 FORMAT_TIMESPAN(p->lifetime_preferred_usec, USEC_PER_SEC),
705 FORMAT_TIMESPAN(p->lifetime_valid_usec, USEC_PER_SEC));
706 } else {
707 /* The prefix is new. Let's simply add it. */
708
709 sd_radv_prefix_ref(p);
710 LIST_APPEND(prefix, ra->prefixes, p);
711 ra->n_prefixes++;
204f99d2 712
c71384a9 713 log_radv(ra, "Added prefix %s", addr_p);
b9f27a05 714 }
059d7b6e
YW
715
716 if (ra->state == RADV_STATE_IDLE)
717 return 0;
718
059d7b6e 719 if (ra->ra_sent == 0)
d601b566 720 return 0;
d601b566 721
97efde65 722 /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */
69628e3b 723 r = radv_send_router(ra, NULL);
059d7b6e 724 if (r < 0)
abb977a6 725 log_radv_errno(ra, r, "Unable to send Router Advertisement for added prefix %s, ignoring: %m", addr_p);
059d7b6e 726 else
c71384a9 727 log_radv(ra, "Sent Router Advertisement for added/updated prefix %s.", addr_p);
97efde65 728
204f99d2
PF
729 return 0;
730}
731
95931532
YW
732void sd_radv_remove_prefix(
733 sd_radv *ra,
734 const struct in6_addr *prefix,
735 unsigned char prefixlen) {
34c169c4 736
95931532
YW
737 if (!ra)
738 return;
34c169c4 739
95931532
YW
740 if (!prefix)
741 return;
742
743 LIST_FOREACH(prefix, cur, ra->prefixes) {
34c169c4
PF
744 if (prefixlen != cur->opt.prefixlen)
745 continue;
746
94876904 747 if (!in6_addr_equal(prefix, &cur->opt.in6_addr))
34c169c4
PF
748 continue;
749
750 LIST_REMOVE(prefix, ra->prefixes, cur);
751 ra->n_prefixes--;
62bbbedf 752 sd_radv_prefix_unref(cur);
95931532 753 return;
34c169c4 754 }
34c169c4
PF
755}
756
17347053 757int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
03677889 758 sd_radv_route_prefix *found = NULL;
203d4df5
SS
759 int r;
760
761 assert_return(ra, -EINVAL);
acbb5500 762 assert_return(p, -EINVAL);
203d4df5 763
c71384a9 764 const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
203d4df5
SS
765
766 LIST_FOREACH(prefix, cur, ra->route_prefixes) {
203d4df5 767 r = in_addr_prefix_intersect(AF_INET6,
c633628d 768 (const union in_addr_union*) &cur->opt.in6_addr,
203d4df5 769 cur->opt.prefixlen,
c633628d 770 (const union in_addr_union*) &p->opt.in6_addr,
203d4df5
SS
771 p->opt.prefixlen);
772 if (r < 0)
773 return r;
774 if (r == 0)
775 continue;
776
95e104e0 777 if (cur->opt.prefixlen == p->opt.prefixlen) {
56aa5143 778 found = cur;
b9f27a05 779 break;
95e104e0 780 }
203d4df5 781
35388783 782 return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
95e104e0 783 "IPv6 route prefix %s conflicts with %s, ignoring.",
c71384a9
ZJS
784 addr_p,
785 IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
203d4df5
SS
786 }
787
56aa5143 788 if (found) {
b9f27a05
YW
789 /* p and cur may be equivalent. First increment the reference counter. */
790 sd_radv_route_prefix_ref(p);
791
792 /* Then, remove the old entry. */
56aa5143
YW
793 LIST_REMOVE(prefix, ra->route_prefixes, found);
794 sd_radv_route_prefix_unref(found);
b9f27a05
YW
795
796 /* Finally, add the new entry. */
797 LIST_APPEND(prefix, ra->route_prefixes, p);
798
799 log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)",
800 strna(addr_p),
801 FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
802 } else {
803 /* The route prefix is new. Let's simply add it. */
804
805 sd_radv_route_prefix_ref(p);
806 LIST_APPEND(prefix, ra->route_prefixes, p);
807 ra->n_route_prefixes++;
808
809 log_radv(ra, "Added route prefix %s", strna(addr_p));
810 }
059d7b6e
YW
811
812 if (ra->state == RADV_STATE_IDLE)
813 return 0;
814
059d7b6e 815 if (ra->ra_sent == 0)
203d4df5 816 return 0;
203d4df5 817
97efde65 818 /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
69628e3b 819 r = radv_send_router(ra, NULL);
059d7b6e 820 if (r < 0)
abb977a6 821 log_radv_errno(ra, r, "Unable to send Router Advertisement for added route prefix %s, ignoring: %m",
b9f27a05 822 strna(addr_p));
059d7b6e 823 else
b9f27a05 824 log_radv(ra, "Sent Router Advertisement for added route prefix %s.", strna(addr_p));
97efde65 825
203d4df5
SS
826 return 0;
827}
828
1925f829
SS
829int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
830 sd_radv_pref64_prefix *found = NULL;
831 int r;
832
833 assert_return(ra, -EINVAL);
834 assert_return(p, -EINVAL);
835
836 const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen);
837
838 LIST_FOREACH(prefix, cur, ra->pref64_prefixes) {
839 r = in_addr_prefix_intersect(AF_INET6,
840 (const union in_addr_union*) &cur->in6_addr,
841 cur->prefixlen,
842 (const union in_addr_union*) &p->in6_addr,
843 p->prefixlen);
844 if (r < 0)
845 return r;
846 if (r == 0)
847 continue;
848
849 if (cur->prefixlen == p->prefixlen) {
850 found = cur;
851 break;
852 }
853
854 return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
855 "IPv6 PREF64 prefix %s conflicts with %s, ignoring.",
856 addr_p,
857 IN6_ADDR_PREFIX_TO_STRING(&cur->in6_addr, cur->prefixlen));
858 }
859
860 if (found) {
861 /* p and cur may be equivalent. First increment the reference counter. */
862 sd_radv_pref64_prefix_ref(p);
863
864 /* Then, remove the old entry. */
865 LIST_REMOVE(prefix, ra->pref64_prefixes, found);
866 sd_radv_pref64_prefix_unref(found);
867
868 /* Finally, add the new entry. */
869 LIST_APPEND(prefix, ra->pref64_prefixes, p);
870
871 log_radv(ra, "Updated/replaced IPv6 PREF64 prefix %s (lifetime: %s)",
872 strna(addr_p),
873 FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
874 } else {
875 /* The route prefix is new. Let's simply add it. */
876
877 sd_radv_pref64_prefix_ref(p);
878 LIST_APPEND(prefix, ra->pref64_prefixes, p);
879 ra->n_pref64_prefixes++;
880
881 log_radv(ra, "Added PREF64 prefix %s", strna(addr_p));
882 }
883
884 if (ra->state == RADV_STATE_IDLE)
885 return 0;
886
887 if (ra->ra_sent == 0)
888 return 0;
889
890 /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
69628e3b 891 r = radv_send_router(ra, NULL);
1925f829
SS
892 if (r < 0)
893 log_radv_errno(ra, r, "Unable to send Router Advertisement for added PREF64 prefix %s, ignoring: %m",
894 strna(addr_p));
895 else
896 log_radv(ra, "Sent Router Advertisement for added PREF64 prefix %s.", strna(addr_p));
897
898 return 0;
899}
900
faaf3d66
YW
901int sd_radv_set_rdnss(
902 sd_radv *ra,
eca280c8 903 uint64_t lifetime_usec,
faaf3d66
YW
904 const struct in6_addr *dns,
905 size_t n_dns) {
906
e9c6da38
PF
907 _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
908 size_t len;
909
910 assert_return(ra, -EINVAL);
911 assert_return(n_dns < 128, -EINVAL);
912
eca280c8
YW
913 if (lifetime_usec > RADV_RDNSS_MAX_LIFETIME_USEC)
914 return -EINVAL;
915
e9c6da38
PF
916 if (!dns || n_dns == 0) {
917 ra->rdnss = mfree(ra->rdnss);
918 ra->n_rdnss = 0;
919
920 return 0;
921 }
922
923 len = sizeof(struct sd_radv_opt_dns) + sizeof(struct in6_addr) * n_dns;
924
925 opt_rdnss = malloc0(len);
926 if (!opt_rdnss)
927 return -ENOMEM;
928
d6fc0d67 929 opt_rdnss->type = RADV_OPT_RDNSS;
e9c6da38 930 opt_rdnss->length = len / 8;
eca280c8 931 opt_rdnss->lifetime = usec_to_be32_sec(lifetime_usec);
e9c6da38
PF
932
933 memcpy(opt_rdnss + 1, dns, n_dns * sizeof(struct in6_addr));
934
1cc6c93a 935 free_and_replace(ra->rdnss, opt_rdnss);
e9c6da38
PF
936
937 ra->n_rdnss = n_dns;
938
939 return 0;
940}
941
faaf3d66
YW
942int sd_radv_set_dnssl(
943 sd_radv *ra,
eca280c8 944 uint64_t lifetime_usec,
faaf3d66
YW
945 char **search_list) {
946
e965d6ab
PF
947 _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL;
948 size_t len = 0;
e965d6ab
PF
949 uint8_t *p;
950
951 assert_return(ra, -EINVAL);
952
eca280c8
YW
953 if (lifetime_usec > RADV_DNSSL_MAX_LIFETIME_USEC)
954 return -EINVAL;
955
97d7974b 956 if (strv_isempty(search_list)) {
e965d6ab 957 ra->dnssl = mfree(ra->dnssl);
e965d6ab
PF
958 return 0;
959 }
960
961 STRV_FOREACH(s, search_list)
962 len += strlen(*s) + 2;
963
964 len = (sizeof(struct sd_radv_opt_dns) + len + 7) & ~0x7;
965
966 opt_dnssl = malloc0(len);
967 if (!opt_dnssl)
968 return -ENOMEM;
969
d6fc0d67 970 opt_dnssl->type = RADV_OPT_DNSSL;
e965d6ab 971 opt_dnssl->length = len / 8;
eca280c8 972 opt_dnssl->lifetime = usec_to_be32_sec(lifetime_usec);
e965d6ab
PF
973
974 p = (uint8_t *)(opt_dnssl + 1);
975 len -= sizeof(struct sd_radv_opt_dns);
976
977 STRV_FOREACH(s, search_list) {
978 int r;
979
980 r = dns_name_to_wire_format(*s, p, len, false);
981 if (r < 0)
982 return r;
983
984 if (len < (size_t)r)
985 return -ENOBUFS;
986
987 p += r;
988 len -= r;
989 }
990
1cc6c93a 991 free_and_replace(ra->dnssl, opt_dnssl);
e965d6ab
PF
992
993 return 0;
994}
995
17347053 996int sd_radv_prefix_new(sd_radv_prefix **ret) {
d2c8eed2 997 sd_radv_prefix *p;
04473969
PF
998
999 assert_return(ret, -EINVAL);
1000
d2c8eed2 1001 p = new(sd_radv_prefix, 1);
04473969
PF
1002 if (!p)
1003 return -ENOMEM;
1004
d2c8eed2
LP
1005 *p = (sd_radv_prefix) {
1006 .n_ref = 1,
04473969 1007
d2c8eed2
LP
1008 .opt.type = ND_OPT_PREFIX_INFORMATION,
1009 .opt.length = (sizeof(p->opt) - 1)/8 + 1,
1010 .opt.prefixlen = 64,
04473969 1011
d2c8eed2
LP
1012 /* RFC 4861, Section 6.2.1 */
1013 .opt.flags = ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO,
204f99d2 1014
d951507d
YW
1015 .lifetime_valid_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
1016 .lifetime_preferred_usec = RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
95e104e0
YW
1017 .valid_until = USEC_INFINITY,
1018 .preferred_until = USEC_INFINITY,
d2c8eed2 1019 };
04473969 1020
d2c8eed2 1021 *ret = p;
04473969
PF
1022 return 0;
1023}
1024
8301aa0b 1025DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_prefix, sd_radv_prefix, mfree);
04473969 1026
faaf3d66
YW
1027int sd_radv_prefix_set_prefix(
1028 sd_radv_prefix *p,
1029 const struct in6_addr *in6_addr,
1030 unsigned char prefixlen) {
1031
04473969
PF
1032 assert_return(p, -EINVAL);
1033 assert_return(in6_addr, -EINVAL);
1034
1035 if (prefixlen < 3 || prefixlen > 128)
1036 return -EINVAL;
1037
1038 if (prefixlen > 64)
1039 /* unusual but allowed, log it */
35388783 1040 log_radv(NULL, "Unusual prefix length %d greater than 64", prefixlen);
04473969
PF
1041
1042 p->opt.in6_addr = *in6_addr;
1043 p->opt.prefixlen = prefixlen;
1044
1045 return 0;
1046}
1047
faaf3d66
YW
1048int sd_radv_prefix_get_prefix(
1049 sd_radv_prefix *p,
1050 struct in6_addr *ret_in6_addr,
1051 unsigned char *ret_prefixlen) {
1052
34332af2
SS
1053 assert_return(p, -EINVAL);
1054 assert_return(ret_in6_addr, -EINVAL);
1055 assert_return(ret_prefixlen, -EINVAL);
1056
1057 *ret_in6_addr = p->opt.in6_addr;
1058 *ret_prefixlen = p->opt.prefixlen;
1059
1060 return 0;
1061}
1062
17347053 1063int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
04473969
PF
1064 assert_return(p, -EINVAL);
1065
1066 SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink);
1067
1068 return 0;
1069}
1070
faaf3d66 1071int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration) {
04473969
PF
1072 assert_return(p, -EINVAL);
1073
1074 SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
1075
1076 return 0;
1077}
1078
17347053 1079int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
04473969
PF
1080 assert_return(p, -EINVAL);
1081
95e104e0
YW
1082 p->lifetime_valid_usec = lifetime_usec;
1083 p->valid_until = valid_until;
04473969
PF
1084
1085 return 0;
1086}
1087
17347053 1088int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
04473969
PF
1089 assert_return(p, -EINVAL);
1090
95e104e0
YW
1091 p->lifetime_preferred_usec = lifetime_usec;
1092 p->preferred_until = valid_until;
04473969
PF
1093
1094 return 0;
1095}
203d4df5 1096
17347053 1097int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
203d4df5
SS
1098 sd_radv_route_prefix *p;
1099
1100 assert_return(ret, -EINVAL);
1101
1102 p = new(sd_radv_route_prefix, 1);
1103 if (!p)
1104 return -ENOMEM;
1105
1106 *p = (sd_radv_route_prefix) {
1107 .n_ref = 1,
1108
d6fc0d67 1109 .opt.type = RADV_OPT_ROUTE_INFORMATION,
203d4df5
SS
1110 .opt.length = DIV_ROUND_UP(sizeof(p->opt), 8),
1111 .opt.prefixlen = 64,
1112
d951507d 1113 .lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
95e104e0 1114 .valid_until = USEC_INFINITY,
203d4df5
SS
1115 };
1116
1117 *ret = p;
1118 return 0;
1119}
1120
1121DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree);
1122
faaf3d66
YW
1123int sd_radv_route_prefix_set_prefix(
1124 sd_radv_route_prefix *p,
1125 const struct in6_addr *in6_addr,
1126 unsigned char prefixlen) {
1127
203d4df5
SS
1128 assert_return(p, -EINVAL);
1129 assert_return(in6_addr, -EINVAL);
1130
1131 if (prefixlen > 128)
1132 return -EINVAL;
1133
1134 if (prefixlen > 64)
1135 /* unusual but allowed, log it */
35388783 1136 log_radv(NULL, "Unusual prefix length %u greater than 64", prefixlen);
203d4df5
SS
1137
1138 p->opt.in6_addr = *in6_addr;
1139 p->opt.prefixlen = prefixlen;
1140
1141 return 0;
1142}
1143
17347053 1144int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
203d4df5
SS
1145 assert_return(p, -EINVAL);
1146
95e104e0
YW
1147 p->lifetime_usec = lifetime_usec;
1148 p->valid_until = valid_until;
203d4df5
SS
1149
1150 return 0;
1151}
1925f829
SS
1152
1153int sd_radv_pref64_prefix_new(sd_radv_pref64_prefix **ret) {
1154 sd_radv_pref64_prefix *p;
1155
1156 assert_return(ret, -EINVAL);
1157
1158 p = new(sd_radv_pref64_prefix, 1);
1159 if (!p)
1160 return -ENOMEM;
1161
1162 *p = (sd_radv_pref64_prefix) {
1163 .n_ref = 1,
1164
1165 .opt.type = RADV_OPT_PREF64,
1166 .opt.length = 2,
1167 };
1168
1169 *ret = p;
1170 return 0;
1171}
1172
1173DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_pref64_prefix, sd_radv_pref64_prefix, mfree);
1174
1175int sd_radv_pref64_prefix_set_prefix(
1176 sd_radv_pref64_prefix *p,
1177 const struct in6_addr *prefix,
1178 uint8_t prefixlen,
1179 uint64_t lifetime_usec) {
1180
1181 uint16_t pref64_lifetime;
1182 uint8_t prefixlen_code;
6e8f5e4c 1183 int r;
1925f829
SS
1184
1185 assert_return(p, -EINVAL);
1186 assert_return(prefix, -EINVAL);
1187
6e8f5e4c
SS
1188 r = pref64_prefix_length_to_plc(prefixlen, &prefixlen_code);
1189 if (r < 0)
f31fa080
ZJS
1190 return log_radv_errno(NULL, r,
1191 "Unsupported PREF64 prefix length %u. Valid lengths are 32, 40, 48, 56, 64 and 96", prefixlen);
1925f829 1192
eca280c8 1193 if (lifetime_usec > PREF64_MAX_LIFETIME_USEC)
1925f829
SS
1194 return -EINVAL;
1195
1196 /* RFC 8781 - 4.1 rounding up lifetime to multiply of 8 */
1197 pref64_lifetime = DIV_ROUND_UP(lifetime_usec, 8 * USEC_PER_SEC) << 3;
1198 pref64_lifetime |= prefixlen_code;
1199
1200 unaligned_write_be16(&p->opt.lifetime_and_plc, pref64_lifetime);
1201 memcpy(&p->opt.prefix, prefix, sizeof(p->opt.prefix));
1202
1203 p->in6_addr = *prefix;
1204 p->prefixlen = prefixlen;
1205
1206 return 0;
1207}