Don't send RAs to the wrong place when DAD in progress.

This commit is contained in:
Simon Kelley
2012-12-03 14:05:59 +00:00
parent 421594f83d
commit 29d28dda95
3 changed files with 32 additions and 18 deletions

View File

@@ -95,26 +95,19 @@ int send_from(int fd, int nowild, char *packet, size_t len,
#endif
}
retry:
if (sendmsg(fd, &msg, 0) == -1)
while (sendmsg(fd, &msg, 0) == -1)
{
/* certain Linux kernels seem to object to setting the source address in the IPv6 stack
by returning EINVAL from sendmsg. In that case, try again without setting the
source address, since it will nearly alway be correct anyway. IPv6 stinks. */
if (errno == EINVAL && msg.msg_controllen)
{
msg.msg_controllen = 0;
goto retry;
}
if (retry_send())
goto retry;
continue;
/* If interface is still in DAD, EINVAL results - ignore that. */
if (errno == EINVAL)
break;
my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
return 0;
}
return 1;
}

View File

@@ -457,6 +457,7 @@ time_t periodic_ra(time_t now)
char interface[IF_NAMESIZE+1];
param.now = now;
param.iface = 0;
while (1)
{
@@ -482,7 +483,8 @@ time_t periodic_ra(time_t now)
ever be able to send ra's and satistfy it. */
if (iface_enumerate(AF_INET6, &param, iface_search))
context->ra_time = 0;
else if (indextoname(daemon->icmp6fd, param.iface, interface) &&
else if (param.iface != 0 &&
indextoname(daemon->icmp6fd, param.iface, interface) &&
iface_check(AF_LOCAL, NULL, interface))
{
struct iname *tmp;
@@ -512,9 +514,11 @@ static int iface_search(struct in6_addr *local, int prefix,
if (context->ra_time != 0 && difftime(context->ra_time, param->now) <= 0.0)
{
/* found an interface that's overdue for RA determine new
timeout value and zap other contexts on the same interface
so they don't timeout independently .*/
param->iface = if_index;
timeout value and arrange for RA to be sent unless interface is
still doing DAD.*/
if (!dad)
param->iface = if_index;
if (difftime(param->now, ra_short_period_start) < 60.0)
/* range 5 - 20 */
@@ -523,6 +527,14 @@ static int iface_search(struct in6_addr *local, int prefix,
/* range 450 - 600 */
context->ra_time = param->now + 450 + (rand16()/440);
/* zero timers for other contexts on the same subnet, so they don't timeout
independently */
for (context = context->next; context; context = context->next)
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
context->ra_time = 0;
return 0; /* found, abort */
}