summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network/sd-radv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network/sd-radv.c')
-rw-r--r--src/libsystemd-network/sd-radv.c463
1 files changed, 193 insertions, 270 deletions
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 97d306c..c384d4e 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -19,6 +19,7 @@
#include "iovec-util.h"
#include "macro.h"
#include "memory-util.h"
+#include "ndisc-router-solicit-internal.h"
#include "network-common.h"
#include "radv-internal.h"
#include "random-util.h"
@@ -81,7 +82,8 @@ sd_event *sd_radv_get_event(sd_radv *ra) {
}
int sd_radv_is_running(sd_radv *ra) {
- assert_return(ra, false);
+ if (!ra)
+ return false;
return ra->state != RADV_STATE_IDLE;
}
@@ -121,30 +123,63 @@ static sd_radv *radv_free(sd_radv *ra) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free);
static bool router_lifetime_is_valid(usec_t lifetime_usec) {
+ assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
return lifetime_usec == 0 ||
(lifetime_usec >= RADV_MIN_ROUTER_LIFETIME_USEC &&
lifetime_usec <= RADV_MAX_ROUTER_LIFETIME_USEC);
}
-static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_usec) {
+static int radv_send_router_on_stop(sd_radv *ra) {
+ static const struct nd_router_advert adv = {
+ .nd_ra_type = ND_ROUTER_ADVERT,
+ };
+
+ _cleanup_set_free_ Set *options = NULL;
+ usec_t time_now;
+ int r;
+
+ assert(ra);
+
+ r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
+ if (r < 0)
+ return r;
+
+ if (!ether_addr_is_null(&ra->mac_addr)) {
+ r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &ra->mac_addr);
+ if (r < 0)
+ return r;
+ }
+
+ return ndisc_send(ra->fd, &IN6_ADDR_ALL_NODES_MULTICAST, &adv.nd_ra_hdr, options, time_now);
+}
+
+static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
+ assert(ra);
+
struct sockaddr_in6 dst_addr = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+ .sin6_addr = IN6_ADDR_ALL_NODES_MULTICAST,
+ };
+ struct nd_router_advert adv = {
+ .nd_ra_type = ND_ROUTER_ADVERT,
+ .nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec),
+ .nd_ra_reachable = usec_to_be32_msec(ra->reachable_usec),
+ .nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec),
};
- struct nd_router_advert adv = {};
struct {
struct nd_opt_hdr opthdr;
struct ether_addr slladdr;
} _packed_ opt_mac = {
.opthdr = {
.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
- .nd_opt_len = (sizeof(struct nd_opt_hdr) +
- sizeof(struct ether_addr) - 1) /8 + 1,
+ .nd_opt_len = DIV_ROUND_UP(sizeof(struct nd_opt_hdr) + sizeof(struct ether_addr), 8),
},
+ .slladdr = ra->mac_addr,
};
struct nd_opt_mtu opt_mtu = {
.nd_opt_mtu_type = ND_OPT_MTU,
.nd_opt_mtu_len = 1,
+ .nd_opt_mtu_mtu = htobe32(ra->mtu),
};
/* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, N pref64 prefixes, RDNSS,
* DNSSL, and home agent. */
@@ -157,9 +192,6 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
usec_t time_now;
int r;
- assert(ra);
- assert(router_lifetime_is_valid(lifetime_usec));
-
r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
if (r < 0)
return r;
@@ -167,25 +199,21 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
if (dst && in6_addr_is_set(dst))
dst_addr.sin6_addr = *dst;
- adv.nd_ra_type = ND_ROUTER_ADVERT;
+ /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
+ * simultaneously in the structured initializer in the above. */
adv.nd_ra_curhoplimit = ra->hop_limit;
- adv.nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec);
- adv.nd_ra_flags_reserved = ra->flags;
- assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
- adv.nd_ra_router_lifetime = usec_to_be16_sec(lifetime_usec);
+ /* RFC 4191, Section 2.2,
+ * "...If the Router Lifetime is zero, the preference value MUST be set to (00) by the sender..." */
+ adv.nd_ra_flags_reserved = ra->flags | (ra->lifetime_usec > 0 ? (ra->preference << 3) : 0);
iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
- /* MAC address is optional, either because the link does not use L2
- addresses or load sharing is desired. See RFC 4861, Section 4.2 */
- if (!ether_addr_is_null(&ra->mac_addr)) {
- opt_mac.slladdr = ra->mac_addr;
+ /* MAC address is optional, either because the link does not use L2 addresses or load sharing is
+ * desired. See RFC 4861, Section 4.2. */
+ if (!ether_addr_is_null(&ra->mac_addr))
iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mac, sizeof(opt_mac));
- }
- if (ra->mtu > 0) {
- opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu);
+ if (ra->mtu > 0)
iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mtu, sizeof(opt_mtu));
- }
LIST_FOREACH(prefix, p, ra->prefixes) {
usec_t lifetime_valid_usec, lifetime_preferred_usec;
@@ -236,87 +264,90 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
return 0;
}
-static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_radv *ra = ASSERT_PTR(userdata);
- struct in6_addr src;
- triple_timestamp timestamp;
+static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
int r;
- assert(s);
- assert(ra->event);
+ assert(ra);
+ assert(packet);
- ssize_t buflen = next_datagram_size_fd(fd);
- if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
- return 0;
- if (buflen < 0) {
- log_radv_errno(ra, buflen, "Failed to determine datagram size to read, ignoring: %m");
- return 0;
- }
+ if (icmp6_packet_get_type(packet) != ND_ROUTER_SOLICIT)
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), "Received ICMP6 packet with unexpected type, ignoring.");
- _cleanup_free_ char *buf = new0(char, buflen);
- if (!buf)
- return -ENOMEM;
+ _cleanup_(sd_ndisc_router_solicit_unrefp) sd_ndisc_router_solicit *rs = NULL;
+ rs = ndisc_router_solicit_new(packet);
+ if (!rs)
+ return log_oom_debug();
- r = icmp6_receive(fd, buf, buflen, &src, &timestamp);
- if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r))
- return 0;
+ r = ndisc_router_solicit_parse(ra, rs);
if (r < 0)
- switch (r) {
- case -EADDRNOTAVAIL:
- log_radv(ra, "Received RS from neither link-local nor null address. Ignoring");
- return 0;
+ return r;
- case -EMULTIHOP:
- log_radv(ra, "Received RS with invalid hop limit. Ignoring.");
- return 0;
+ struct in6_addr src;
+ r = sd_ndisc_router_solicit_get_sender_address(rs, &src);
+ if (r == -ENODATA) /* null address is allowed */
+ return sd_radv_send(ra); /* When an unsolicited RA, we need to also update timer. */
+ if (r < 0)
+ return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m");
+ if (in6_addr_equal(&src, &ra->ipv6ll))
+ /* This should be definitely caused by a misconfiguration. If we send RA to ourself, the
+ * kernel complains about that. Let's ignore the packet. */
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EADDRINUSE), "Received RS from the same interface, ignoring.");
- case -EPFNOSUPPORT:
- log_radv(ra, "Received invalid source address from ICMPv6 socket. Ignoring.");
- return 0;
+ r = radv_send_router(ra, &src);
+ if (r < 0)
+ return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src));
- default:
- log_radv_errno(ra, r, "Unexpected error receiving from ICMPv6 socket, ignoring: %m");
- return 0;
- }
+ log_radv(ra, "Sent solicited Router Advertisement to %s.", IN6_ADDR_TO_STRING(&src));
+ return 0;
+}
- if ((size_t) buflen < sizeof(struct nd_router_solicit)) {
- log_radv(ra, "Too short packet received, ignoring");
+static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ sd_radv *ra = ASSERT_PTR(userdata);
+ int r;
+
+ assert(fd >= 0);
+
+ r = icmp6_packet_receive(fd, &packet);
+ if (r < 0) {
+ log_radv_errno(ra, r, "Failed to receive ICMPv6 packet, ignoring: %m");
return 0;
}
- /* TODO: if the sender address is null, check that the message does not have the source link-layer
- * address option. See RFC 4861 Section 6.1.1. */
+ (void) radv_process_packet(ra, packet);
+ return 0;
+}
- const char *addr = IN6_ADDR_TO_STRING(&src);
+static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_radv *ra = ASSERT_PTR(userdata);
- r = radv_send(ra, &src, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", addr);
- else
- log_radv(ra, "Sent solicited Router Advertisement to %s", addr);
+ if (sd_radv_send(ra) < 0)
+ (void) sd_radv_stop(ra);
return 0;
}
-static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+int sd_radv_send(sd_radv *ra) {
usec_t min_timeout, max_timeout, time_now, timeout;
- sd_radv *ra = ASSERT_PTR(userdata);
int r;
- assert(s);
- assert(ra->event);
+ assert_return(ra, -EINVAL);
+ assert_return(ra->event, -EINVAL);
+ assert_return(sd_radv_is_running(ra), -EINVAL);
assert(router_lifetime_is_valid(ra->lifetime_usec));
r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
if (r < 0)
- goto fail;
+ return r;
- r = radv_send(ra, NULL, ra->lifetime_usec);
+ r = radv_send_router(ra, NULL);
if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement, ignoring: %m");
+ return log_radv_errno(ra, r, "Unable to send Router Advertisement: %m");
+
+ ra->ra_sent++;
/* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
- if (ra->ra_sent < RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
+ if (ra->ra_sent <= RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
max_timeout = RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
else
max_timeout = RADV_DEFAULT_MAX_TIMEOUT_USEC;
@@ -340,47 +371,64 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
assert(min_timeout <= max_timeout * 3 / 4);
timeout = min_timeout + random_u64_range(max_timeout - min_timeout);
- log_radv(ra, "Next Router Advertisement in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
+ log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.",
+ FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
+
+ return event_reset_time(
+ ra->event, &ra->timeout_event_source,
+ CLOCK_BOOTTIME,
+ usec_add(time_now, timeout), MSEC_PER_SEC,
+ radv_timeout, ra,
+ ra->event_priority, "radv-timeout", true);
+}
- r = event_reset_time(ra->event, &ra->timeout_event_source,
- CLOCK_BOOTTIME,
- usec_add(time_now, timeout), MSEC_PER_SEC,
- radv_timeout, ra,
- ra->event_priority, "radv-timeout", true);
- if (r < 0)
- goto fail;
+int sd_radv_stop(sd_radv *ra) {
+ int r;
- ra->ra_sent++;
+ if (!sd_radv_is_running(ra))
+ return 0; /* Already stopped. */
- return 0;
+ log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
-fail:
- sd_radv_stop(ra);
+ /* RFC 4861, Section 6.2.5:
+ * the router SHOULD transmit one or more (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final
+ * multicast Router Advertisements on the interface with a Router Lifetime field of zero. */
+ r = radv_send_router_on_stop(ra);
+ if (r < 0)
+ log_radv_errno(ra, r, "Unable to send last Router Advertisement with router lifetime set to zero, ignoring: %m");
+
+ radv_reset(ra);
+ ra->fd = safe_close(ra->fd);
+ ra->state = RADV_STATE_IDLE;
return 0;
}
-int sd_radv_stop(sd_radv *ra) {
+static int radv_setup_recv_event(sd_radv *ra) {
int r;
- if (!ra)
- return 0;
+ assert(ra);
+ assert(ra->event);
+ assert(ra->ifindex > 0);
- if (ra->state == RADV_STATE_IDLE)
- return 0;
+ _cleanup_close_ int fd = -EBADF;
+ fd = icmp6_bind(ra->ifindex, /* is_router = */ true);
+ if (fd < 0)
+ return fd;
- log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ r = sd_event_add_io(ra->event, &s, fd, EPOLLIN, radv_recv, ra);
+ if (r < 0)
+ return r;
- /* RFC 4861, Section 6.2.5, send at least one Router Advertisement
- with zero lifetime */
- r = radv_send(ra, NULL, 0);
+ r = sd_event_source_set_priority(s, ra->event_priority);
if (r < 0)
- log_radv_errno(ra, r, "Unable to send last Router Advertisement with router lifetime set to zero, ignoring: %m");
+ return r;
- radv_reset(ra);
- ra->fd = safe_close(ra->fd);
- ra->state = RADV_STATE_IDLE;
+ (void) sd_event_source_set_description(s, "radv-receive-message");
+ ra->fd = TAKE_FD(fd);
+ ra->recv_event_source = TAKE_PTR(s);
return 0;
}
@@ -391,8 +439,12 @@ int sd_radv_start(sd_radv *ra) {
assert_return(ra->event, -EINVAL);
assert_return(ra->ifindex > 0, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return 0;
+ if (sd_radv_is_running(ra))
+ return 0; /* Already started. */
+
+ r = radv_setup_recv_event(ra);
+ if (r < 0)
+ goto fail;
r = event_reset_time(ra->event, &ra->timeout_event_source,
CLOCK_BOOTTIME,
@@ -402,22 +454,6 @@ int sd_radv_start(sd_radv *ra) {
if (r < 0)
goto fail;
- r = icmp6_bind_router_advertisement(ra->ifindex);
- if (r < 0)
- goto fail;
-
- ra->fd = r;
-
- r = sd_event_add_io(ra->event, &ra->recv_event_source, ra->fd, EPOLLIN, radv_recv, ra);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(ra->recv_event_source, ra->event_priority);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message");
-
ra->state = RADV_STATE_ADVERTISING;
log_radv(ra, "Started IPv6 Router Advertisement daemon");
@@ -432,13 +468,10 @@ int sd_radv_start(sd_radv *ra) {
int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
assert_return(ra, -EINVAL);
+ assert_return(!sd_radv_is_running(ra), -EBUSY);
assert_return(ifindex > 0, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->ifindex = ifindex;
-
return 0;
}
@@ -467,11 +500,20 @@ int sd_radv_get_ifname(sd_radv *ra, const char **ret) {
return 0;
}
-int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr) {
assert_return(ra, -EINVAL);
+ assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL);
+
+ if (addr)
+ ra->ipv6ll = *addr;
+ else
+ zero(ra->ipv6ll);
+
+ return 0;
+}
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+ assert_return(ra, -EINVAL);
if (mac_addr)
ra->mac_addr = *mac_addr;
@@ -493,22 +535,19 @@ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->hop_limit = hop_limit;
-
return 0;
}
-int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
+int sd_radv_set_reachable_time(sd_radv *ra, uint64_t usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
+ ra->reachable_usec = usec;
+ return 0;
+}
- if (usec > RADV_MAX_RETRANSMIT_USEC)
- return -EINVAL;
+int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
+ assert_return(ra, -EINVAL);
ra->retransmit_usec = usec;
return 0;
@@ -517,89 +556,55 @@ int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
if (!router_lifetime_is_valid(usec))
return -EINVAL;
- /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
- * to (00) by the sender..." */
- if (usec == 0 &&
- (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
- return -EINVAL;
-
ra->lifetime_usec = usec;
return 0;
}
-int sd_radv_set_managed_information(sd_radv *ra, int managed) {
+int sd_radv_set_managed_information(sd_radv *ra, int b) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
- SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
-
+ SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, b);
return 0;
}
-int sd_radv_set_other_information(sd_radv *ra, int other) {
+int sd_radv_set_other_information(sd_radv *ra, int b) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
- SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
-
+ SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, b);
return 0;
}
-int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
+int sd_radv_set_preference(sd_radv *ra, uint8_t preference) {
assert_return(ra, -EINVAL);
assert_return(IN_SET(preference,
SD_NDISC_PREFERENCE_LOW,
SD_NDISC_PREFERENCE_MEDIUM,
SD_NDISC_PREFERENCE_HIGH), -EINVAL);
- /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
- * to (00) by the sender..." */
- if (ra->lifetime_usec == 0 && preference != SD_NDISC_PREFERENCE_MEDIUM)
- return -EINVAL;
-
- ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
-
+ ra->preference = preference;
return 0;
}
int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
-
return 0;
}
int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
-
return 0;
}
int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
if (lifetime_usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC)
return -EINVAL;
@@ -609,7 +614,6 @@ int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
sd_radv_prefix *found = NULL;
- int r;
assert_return(ra, -EINVAL);
assert_return(p, -EINVAL);
@@ -621,19 +625,13 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
LIST_FOREACH(prefix, cur, ra->prefixes) {
- r = in_addr_prefix_intersect(AF_INET6,
- (const union in_addr_union*) &cur->opt.in6_addr,
- cur->opt.prefixlen,
- (const union in_addr_union*) &p->opt.in6_addr,
- p->opt.prefixlen);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
+ if (!in6_addr_prefix_intersect(&cur->opt.in6_addr, cur->opt.prefixlen,
+ &p->opt.in6_addr, p->opt.prefixlen))
+ continue; /* no intersection */
if (cur->opt.prefixlen == p->opt.prefixlen) {
found = cur;
- break;
+ break; /* same prefix */
}
return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
@@ -667,19 +665,6 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
log_radv(ra, "Added prefix %s", addr_p);
}
- if (ra->state == RADV_STATE_IDLE)
- return 0;
-
- if (ra->ra_sent == 0)
- return 0;
-
- /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */
- r = radv_send(ra, NULL, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement for added prefix %s, ignoring: %m", addr_p);
- else
- log_radv(ra, "Sent Router Advertisement for added/updated prefix %s.", addr_p);
-
return 0;
}
@@ -710,35 +695,17 @@ void sd_radv_remove_prefix(
int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
sd_radv_route_prefix *found = NULL;
- int r;
assert_return(ra, -EINVAL);
assert_return(p, -EINVAL);
- const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
-
- LIST_FOREACH(prefix, cur, ra->route_prefixes) {
- r = in_addr_prefix_intersect(AF_INET6,
- (const union in_addr_union*) &cur->opt.in6_addr,
- cur->opt.prefixlen,
- (const union in_addr_union*) &p->opt.in6_addr,
- p->opt.prefixlen);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (cur->opt.prefixlen == p->opt.prefixlen) {
+ LIST_FOREACH(prefix, cur, ra->route_prefixes)
+ if (cur->opt.prefixlen == p->opt.prefixlen &&
+ in6_addr_equal(&cur->opt.in6_addr, &p->opt.in6_addr)) {
found = cur;
break;
}
- return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
- "IPv6 route prefix %s conflicts with %s, ignoring.",
- addr_p,
- IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
- }
-
if (found) {
/* p and cur may be equivalent. First increment the reference counter. */
sd_radv_route_prefix_ref(p);
@@ -751,7 +718,7 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
LIST_APPEND(prefix, ra->route_prefixes, p);
log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)",
- strna(addr_p),
+ IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen),
FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
} else {
/* The route prefix is new. Let's simply add it. */
@@ -760,57 +727,26 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
LIST_APPEND(prefix, ra->route_prefixes, p);
ra->n_route_prefixes++;
- log_radv(ra, "Added route prefix %s", strna(addr_p));
+ log_radv(ra, "Added route prefix %s",
+ IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen));
}
- if (ra->state == RADV_STATE_IDLE)
- return 0;
-
- if (ra->ra_sent == 0)
- return 0;
-
- /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
- r = radv_send(ra, NULL, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement for added route prefix %s, ignoring: %m",
- strna(addr_p));
- else
- log_radv(ra, "Sent Router Advertisement for added route prefix %s.", strna(addr_p));
-
return 0;
}
int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
sd_radv_pref64_prefix *found = NULL;
- int r;
assert_return(ra, -EINVAL);
assert_return(p, -EINVAL);
- const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen);
-
- LIST_FOREACH(prefix, cur, ra->pref64_prefixes) {
- r = in_addr_prefix_intersect(AF_INET6,
- (const union in_addr_union*) &cur->in6_addr,
- cur->prefixlen,
- (const union in_addr_union*) &p->in6_addr,
- p->prefixlen);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (cur->prefixlen == p->prefixlen) {
+ LIST_FOREACH(prefix, cur, ra->pref64_prefixes)
+ if (cur->prefixlen == p->prefixlen &&
+ in6_addr_equal(&cur->in6_addr, &p->in6_addr)) {
found = cur;
break;
}
- return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
- "IPv6 PREF64 prefix %s conflicts with %s, ignoring.",
- addr_p,
- IN6_ADDR_PREFIX_TO_STRING(&cur->in6_addr, cur->prefixlen));
- }
-
if (found) {
/* p and cur may be equivalent. First increment the reference counter. */
sd_radv_pref64_prefix_ref(p);
@@ -823,7 +759,7 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
LIST_APPEND(prefix, ra->pref64_prefixes, p);
log_radv(ra, "Updated/replaced IPv6 PREF64 prefix %s (lifetime: %s)",
- strna(addr_p),
+ IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen),
FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
} else {
/* The route prefix is new. Let's simply add it. */
@@ -832,23 +768,10 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
LIST_APPEND(prefix, ra->pref64_prefixes, p);
ra->n_pref64_prefixes++;
- log_radv(ra, "Added PREF64 prefix %s", strna(addr_p));
+ log_radv(ra, "Added PREF64 prefix %s",
+ IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen));
}
- if (ra->state == RADV_STATE_IDLE)
- return 0;
-
- if (ra->ra_sent == 0)
- return 0;
-
- /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
- r = radv_send(ra, NULL, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement for added PREF64 prefix %s, ignoring: %m",
- strna(addr_p));
- else
- log_radv(ra, "Sent Router Advertisement for added PREF64 prefix %s.", strna(addr_p));
-
return 0;
}