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; + + /* 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, ¶m, 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 */ }