summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2012-12-03 14:05:59 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2012-12-03 14:05:59 +0000
commit29d28dda95d77250197a4cd3e199540e13317441 (patch)
tree5242d44ce0745698fc97b4423ce213010862cf32
parent421594f83ddcfcb2ac4fc91eb8b06f7596bb4e44 (diff)
downloaddnsmasq-29d28dda95d77250197a4cd3e199540e13317441.tar.gz
Don't send RAs to the wrong place when DAD in progress.v2.64rc3v2.64
-rw-r--r--CHANGELOG9
-rw-r--r--src/forward.c21
-rw-r--r--src/radv.c20
3 files changed, 32 insertions, 18 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 87c19ce..b98bc3b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -58,6 +58,15 @@ version 2.64
Check interface for outgoing unsolicited router
advertisements, rather than relying on interface address
configuration. Thanks to Gene Czarinski for the patch.
+
+ Handle better attempts to transmit on interfaces which are
+ still doing DAD, and specifically do not just transmit
+ without setting source address and interface, since this
+ can cause very puzzling effects when a router
+ advertisement goes astray. Thanks again to Gene Czarinski.
+
+ Get RA timers right when there is more than one
+ dhcp-range on a subnet.
version 2.63
diff --git a/src/forward.c b/src/forward.c
index f672194..4afc25a 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -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;
- my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
+ /* 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;
}
diff --git a/src/radv.c b/src/radv.c
index 90678a4..25bcb54 100644
--- a/src/radv.c
+++ b/src/radv.c
@@ -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 */
}