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