Check assumed SLAAC addresses by pinging them.
diff --git a/src/radv.c b/src/radv.c
index c8e2f01..854f35b 100644
--- a/src/radv.c
+++ b/src/radv.c
@@ -55,13 +55,20 @@
#endif
int val = 255; /* radvd uses this value */
socklen_t len = sizeof(int);
-
+ struct dhcp_context *context;
+
/* ensure this is around even if we're not doing DHCPv6 */
expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
-
+
+ /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
+ for (context = daemon->ra_contexts; context; context = context->next)
+ if ((context->flags & CONTEXT_RA_NAME))
+ break;
+
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
- ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+ if (context)
+ ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
@@ -77,21 +84,21 @@
daemon->icmp6fd = fd;
- ra_start_unsolicted(now);
+ ra_start_unsolicted(now, NULL);
}
-void ra_start_unsolicted(time_t now)
+void ra_start_unsolicted(time_t now, struct dhcp_context *context)
{
- struct dhcp_context *context;
-
- /* init timers so that we do ra's for all soon. some ra_times will end up zeroed
+ /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
if it's not appropriate to advertise those contexts.
This gets re-called on a netlink route-change to re-do the advertisement
and pick up new interfaces */
- /* range 0 - 5 */
- for (context = daemon->ra_contexts; context; context = context->next)
- context->ra_time = now + (rand16()/13000);
+ if (context)
+ context->ra_time = now;
+ else
+ for (context = daemon->ra_contexts; context; context = context->next)
+ context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
/* re-do frequently for a minute or so, in case the first gets lost. */
ra_short_period_start = now;
@@ -158,7 +165,19 @@
p = (unsigned char *)daemon->outpacket.iov_base;
- if (p[0] != ICMP6_ROUTER_SOLICIT || p[1] != 0)
+ if (p[1] != 0)
+ return;
+
+ if (p[0] == ICMP6_ECHO_REPLY)
+ {
+ /* We may be doing RA but not DHCPv4, in which case the lease
+ database may not exist and we have nothing to do anyway */
+ if (daemon->dhcp)
+ lease_ping_reply(&from.sin6_addr, p, interface);
+ return;
+ }
+
+ if (p[0] != ND_ROUTER_SOLICIT)
return;
/* look for link-layer address option for logging */
@@ -184,7 +203,7 @@
save_counter(0);
ra = expand(sizeof(struct ra_packet));
- ra->type = ICMP6_ROUTER_ADVERT;
+ ra->type = ND_ROUTER_ADVERT;
ra->code = 0;
ra->hop_limit = hop_limit;
ra->flags = 0;
@@ -238,7 +257,7 @@
addr.sin6_port = htons(IPPROTO_ICMPV6);
if (dest)
{
- memcpy(&addr.sin6_addr, dest, sizeof(struct in6_addr));
+ addr.sin6_addr = *dest;
if (IN6_IS_ADDR_LINKLOCAL(dest) ||
IN6_IS_ADDR_MC_LINKLOCAL(dest))
addr.sin6_scope_id = iface;
@@ -406,7 +425,7 @@
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
- if (context->ra_time != 0 && difftime(context->ra_time, param->now) < 0.0)
+ 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
@@ -426,73 +445,5 @@
return 1; /* keep searching */
}
-static int add_subnet(struct in6_addr *local, int prefix,
- int scope, int if_index, int dad, void *vparam)
-{
- struct dhcp_context *context;
- struct subnet_map **subnets = vparam;
- struct subnet_map *map;
-
- (void)scope;
- (void)dad;
-
- for (context = daemon->ra_contexts; context; context = context->next)
- if ((context->flags & CONTEXT_RA_NAME) &&
- prefix == context->prefix &&
- is_same_net6(local, &context->start6, prefix) &&
- is_same_net6(local, &context->end6, prefix))
- {
- for (map = *subnets; map; map = map->next)
- if (map->iface == 0 ||
- (map->iface == if_index && is_same_net6(local, &map->subnet, prefix)))
- break;
-
- /* It's there already */
- if (map && map->iface != 0)
- continue;
-
- if (!map && (map = whine_malloc(sizeof(struct subnet_map))))
- {
- map->next = *subnets;
- *subnets = map;
- }
-
- if (map)
- {
- map->iface = if_index;
- map->subnet = *local;
- }
- }
-
- return 1;
-}
-
-/* Build a map from ra-names subnets to corresponding interfaces. This
- is used to go from DHCPv4 leases to SLAAC addresses,
- interface->IPv6-subnet, IPv6-subnet + MAC address -> SLAAC.
-*/
-struct subnet_map *build_subnet_map(void)
-{
- struct subnet_map *map;
- struct dhcp_context *context;
- static struct subnet_map *subnets = NULL;
-
- for (context = daemon->ra_contexts; context; context = context->next)
- if ((context->flags & CONTEXT_RA_NAME))
- break;
-
- /* no ra-names, no need to go further. */
- if (!context)
- return NULL;
-
- /* mark unused */
- for (map = subnets; map; map = map->next)
- map->iface = 0;
-
- if (iface_enumerate(AF_INET6, &subnets, add_subnet))
- return subnets;
-
- return NULL;
-}
#endif