Skip to content

Commit

Permalink
router: advertise removed addresses as invalid in 3 consecutive RAs
Browse files Browse the repository at this point in the history
On prefix removal, router advertisement daemon is supposed to send
advertise with an invalid PI entry (see RFC 7084 L-13).

Signed-off-by: Alin Nastac <[email protected]>
Signed-off-by: Hans Dedecker <[email protected]>
  • Loading branch information
alinnastac authored and dedeckeh committed Jan 10, 2022
1 parent 01b4e60 commit 83e14f4
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ static void close_interface(struct interface *iface)
clean_interface(iface);
free(iface->addr4);
free(iface->addr6);
free(iface->invalid_addr6);
free(iface->ifname);
free(iface);
}
Expand Down
56 changes: 56 additions & 0 deletions src/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,66 @@ static void refresh_iface_addr6(int ifindex)
change = len != (ssize_t)iface->addr6_len;
for (ssize_t i = 0; !change && i < len; ++i) {
if (!IN6_ARE_ADDR_EQUAL(&addr[i].addr.in6, &iface->addr6[i].addr.in6) ||
addr[i].prefix != iface->addr6[i].prefix ||
(addr[i].preferred > (uint32_t)now) != (iface->addr6[i].preferred > (uint32_t)now) ||
addr[i].valid < iface->addr6[i].valid || addr[i].preferred < iface->addr6[i].preferred)
change = true;
}

if (change) {
/*
* Keep track on removed prefixes, so we could advertise them as invalid
* for at least a couple of times.
*
* RFC7084 § 4.3 :
* L-13: If the delegated prefix changes, i.e., the current prefix is
* replaced with a new prefix without any overlapping time
* period, then the IPv6 CE router MUST immediately advertise the
* old prefix with a Preferred Lifetime of zero and a Valid
* Lifetime of either a) zero or b) the lower of the current
* Valid Lifetime and two hours (which must be decremented in
* real time) in a Router Advertisement message as described in
* Section 5.5.3, (e) of [RFC4862].
*/

for (size_t i = 0; i < iface->addr6_len; ++i) {
bool removed = true;

if (iface->addr6[i].valid <= (uint32_t)now)
continue;

for (ssize_t j = 0; removed && j < len; ++j) {
size_t plen = min(addr[j].prefix, iface->addr6[i].prefix);

if (odhcpd_bmemcmp(&addr[j].addr.in6, &iface->addr6[i].addr.in6, plen) == 0)
removed = false;
}

for (size_t j = 0; removed && j < iface->invalid_addr6_len; ++j) {
size_t plen = min(iface->invalid_addr6[j].prefix, iface->addr6[i].prefix);

if (odhcpd_bmemcmp(&iface->invalid_addr6[j].addr.in6, &iface->addr6[i].addr.in6, plen) == 0)
removed = false;
}

if (removed) {
size_t pos = iface->invalid_addr6_len;
struct odhcpd_ipaddr *new_invalid_addr6 = realloc(iface->invalid_addr6,
sizeof(*iface->invalid_addr6) * (pos + 1));

if (!new_invalid_addr6)
break;

iface->invalid_addr6 = new_invalid_addr6;
iface->invalid_addr6_len++;
memcpy(&iface->invalid_addr6[pos], &iface->addr6[i], sizeof(*iface->invalid_addr6));
iface->invalid_addr6[pos].valid = iface->invalid_addr6[pos].preferred = (uint32_t)now;

if (iface->invalid_addr6[pos].prefix < 64)
iface->invalid_addr6[pos].prefix = 64;
}
}
}
}

iface->addr6 = addr;
Expand Down
18 changes: 14 additions & 4 deletions src/odhcpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
#include <libubox/ustream.h>
#include <libubox/vlist.h>

#define min(a, b) (((a) < (b)) ? (a) : (b))
#define max(a, b) (((a) > (b)) ? (a) : (b))

// RFC 6106 defines this router advertisement option
#define ND_OPT_ROUTE_INFO 24
#define ND_OPT_RECURSIVE_DNS 25
Expand Down Expand Up @@ -118,11 +121,16 @@ struct odhcpd_ipaddr {
uint32_t preferred;
uint32_t valid;

/* ipv6 only */
uint8_t dprefix;
union {
/* ipv6 only */
struct {
uint8_t dprefix;
uint8_t invalid_advertisements;
};

/* ipv4 only */
struct in_addr broadcast;
/* ipv4 only */
struct in_addr broadcast;
};
};

enum odhcpd_mode {
Expand Down Expand Up @@ -232,6 +240,8 @@ struct interface {
// IPv6 runtime data
struct odhcpd_ipaddr *addr6;
size_t addr6_len;
struct odhcpd_ipaddr *invalid_addr6;
size_t invalid_addr6_len;

// RA runtime data
struct odhcpd_event router_event;
Expand Down
73 changes: 52 additions & 21 deletions src/router.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
struct iovec iov[IOV_RA_TOTAL];
struct sockaddr_in6 dest;
size_t dns_sz = 0, search_sz = 0, pfxs_cnt = 0, routes_cnt = 0;
ssize_t addr_cnt = 0;
ssize_t valid_addr_cnt = 0, invalid_addr_cnt = 0;
uint32_t minvalid = UINT32_MAX, maxival, lifetime;
int msecs, mtu = iface->ra_mtu, hlim = iface->ra_hoplimit;
bool default_route = false;
Expand Down Expand Up @@ -485,33 +485,61 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
iov[IOV_RA_ADV].iov_base = (char *)&adv;
iov[IOV_RA_ADV].iov_len = sizeof(adv);

/* If not shutdown */
if (iface->timer_rs.cb) {
size_t size = sizeof(*addrs) * iface->addr6_len;
valid_addr_cnt = (iface->timer_rs.cb /* if not shutdown */ ? iface->addr6_len : 0);
invalid_addr_cnt = iface->invalid_addr6_len;

addrs = alloca(size);
memcpy(addrs, iface->addr6, size);
if (valid_addr_cnt + invalid_addr_cnt) {
addrs = alloca(sizeof(*addrs) * (valid_addr_cnt + invalid_addr_cnt));

addr_cnt = iface->addr6_len;
if (valid_addr_cnt) {
memcpy(addrs, iface->addr6, sizeof(*addrs) * valid_addr_cnt);

/* Check default route */
if (iface->default_router) {
default_route = true;
/* Check default route */
if (iface->default_router) {
default_route = true;

if (iface->default_router > 1)
valid_prefix = true;
} else if (parse_routes(addrs, addr_cnt))
default_route = true;
if (iface->default_router > 1)
valid_prefix = true;
} else if (parse_routes(addrs, valid_addr_cnt))
default_route = true;
}

if (invalid_addr_cnt) {
size_t i = 0;

memcpy(&addrs[valid_addr_cnt], iface->invalid_addr6, sizeof(*addrs) * invalid_addr_cnt);

/* Remove invalid prefixes that were advertised 3 times */
while (i < iface->invalid_addr6_len) {
if (++iface->invalid_addr6[i].invalid_advertisements >= 3) {
if (i + 1 < iface->invalid_addr6_len)
memmove(&iface->invalid_addr6[i], &iface->invalid_addr6[i + 1], sizeof(*addrs) * (iface->invalid_addr6_len - i - 1));

iface->invalid_addr6_len--;

if (iface->invalid_addr6_len) {
struct odhcpd_ipaddr *new_invalid_addr6 = realloc(iface->invalid_addr6, sizeof(*addrs) * iface->invalid_addr6_len);

if (new_invalid_addr6)
iface->invalid_addr6 = new_invalid_addr6;
} else {
free(iface->invalid_addr6);
iface->invalid_addr6 = NULL;
}
} else
++i;
}
}
}

/* Construct Prefix Information options */
for (ssize_t i = 0; i < addr_cnt; ++i) {
for (ssize_t i = 0; i < valid_addr_cnt + invalid_addr_cnt; ++i) {
struct odhcpd_ipaddr *addr = &addrs[i];
struct nd_opt_prefix_info *p = NULL;
uint32_t preferred = 0;
uint32_t valid = 0;

if (addr->prefix > 96 || addr->valid <= (uint32_t)now) {
if (addr->prefix > 96 || (i < valid_addr_cnt && addr->valid <= (uint32_t)now)) {
syslog(LOG_INFO, "Address %s (prefix %d, valid %u) not suitable as RA prefix on %s",
inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)), addr->prefix,
addr->valid, iface->name);
Expand Down Expand Up @@ -554,14 +582,17 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
preferred = iface->preferred_lifetime;
}

valid = TIME_LEFT(addr->valid, now);
if (iface->ra_useleasetime && valid > iface->dhcp_leasetime)
valid = iface->dhcp_leasetime;
if (addr->valid > (uint32_t)now) {
valid = TIME_LEFT(addr->valid, now);

if (iface->ra_useleasetime && valid > iface->dhcp_leasetime)
valid = iface->dhcp_leasetime;
}

if (minvalid > valid)
minvalid = valid;

if (!IN6_IS_ADDR_ULA(&addr->addr.in6) || iface->default_router)
if ((!IN6_IS_ADDR_ULA(&addr->addr.in6) || iface->default_router) && valid)
valid_prefix = true;

odhcpd_bmemcpy(&p->nd_opt_pi_prefix, &addr->addr.in6,
Expand Down Expand Up @@ -667,7 +698,7 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
* WAN interface.
*/

for (ssize_t i = 0; i < addr_cnt; ++i) {
for (ssize_t i = 0; i < valid_addr_cnt; ++i) {
struct odhcpd_ipaddr *addr = &addrs[i];
struct nd_opt_route_info *tmp;
uint32_t valid;
Expand Down

0 comments on commit 83e14f4

Please sign in to comment.