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;
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);
+ // check ra_default
+ if (iface->default_router) {
+ default_route = true;
- addr_cnt = iface->addr6_len;
+ if (iface->default_router > 1)
+ valid_prefix = true;
+ }
+
+ if (valid_addr_cnt + invalid_addr_cnt) {
+ addrs = alloca(sizeof(*addrs) * (valid_addr_cnt + invalid_addr_cnt));
+
+ if (valid_addr_cnt) {
+ memcpy(addrs, iface->addr6, sizeof(*addrs) * valid_addr_cnt);
+
+ /* Check default route */
+ if (!default_route && parse_routes(addrs, valid_addr_cnt))
+ default_route = true;
+ }
- /* Check default route */
- if (iface->default_router) {
- default_route = true;
+ if (invalid_addr_cnt) {
+ size_t i = 0;
- if (iface->default_router > 1)
- valid_prefix = true;
- } else if (parse_routes(addrs, addr_cnt))
- default_route = true;
+ 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);
continue;
}
- if (odhcpd_bmemcmp(&addr->addr, &iface->pio_filter_addr,
- iface->pio_filter_length) != 0 ||
- addr->prefix < iface->pio_filter_length) {
+ if (ADDR_MATCH_PIO_FILTER(addr, iface)) {
syslog(LOG_INFO, "Address %s filtered out as RA prefix on %s",
inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)),
iface->name);
preferred = TIME_LEFT(addr->preferred, now);
if (iface->ra_useleasetime &&
- preferred > iface->dhcp_leasetime)
- preferred = iface->dhcp_leasetime;
+ preferred > iface->preferred_lifetime)
+ 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,
msecs = calc_adv_interval(iface, minvalid, &maxival);
lifetime = calc_ra_lifetime(iface, maxival);
- if (default_route) {
- if (!valid_prefix) {
- syslog(LOG_WARNING, "A default route is present but there is no public prefix "
- "on %s thus we don't announce a default route!", iface->name);
- adv.h.nd_ra_router_lifetime = 0;
- } else
- adv.h.nd_ra_router_lifetime = htons(lifetime < UINT16_MAX ? lifetime : UINT16_MAX);
+ if (!iface->have_link_local) {
+ syslog(LOG_NOTICE, "Skip sending a RA on %s as no link local address is available", iface->name);
+ goto out;
+ }
- } else
+ if (default_route && valid_prefix) {
+ adv.h.nd_ra_router_lifetime = htons(lifetime < UINT16_MAX ? lifetime : UINT16_MAX);
+ } else {
adv.h.nd_ra_router_lifetime = 0;
+ if (default_route) {
+ syslog(LOG_WARNING, "A default route is present but there is no public prefix "
+ "on %s thus we don't announce a default route by overriding ra_lifetime!", iface->name);
+ } else {
+ syslog(LOG_WARNING, "No default route present, overriding ra_lifetime!");
+ }
+ }
+
syslog(LOG_DEBUG, "Using a RA lifetime of %d seconds on %s", ntohs(adv.h.nd_ra_router_lifetime), iface->name);
/* DNS options */
* 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;
continue; /* Address not suitable */
}
- if (odhcpd_bmemcmp(&addr->addr, &iface->pio_filter_addr,
- iface->pio_filter_length) != 0 ||
- addr->prefix < iface->pio_filter_length) {
+ if (ADDR_MATCH_PIO_FILTER(addr, iface)) {
syslog(LOG_INFO, "Address %s filtered out as RA route on %s",
inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)),
iface->name);
if (odhcpd_send(iface->router_event.uloop.fd, &dest, iov, ARRAY_SIZE(iov), iface) > 0)
iface->ra_sent++;
+out:
free(pfxs);
free(routes);