diff --git a/Makefile b/Makefile
index 30e70a4..ee7d36f 100644
--- a/Makefile
+++ b/Makefile
@@ -65,7 +65,7 @@ version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
- dhcp-common.o outpacket.o radv.o
+ dhcp-common.o outpacket.o radv.o slaac.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h
diff --git a/bld/Android.mk b/bld/Android.mk
index a4bc610..53e4a56 100644
--- a/bld/Android.mk
+++ b/bld/Android.mk
@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
- radv.c
+ radv.c slaac.c
LOCAL_MODULE := dnsmasq
diff --git a/src/dhcp.c b/src/dhcp.c
index b95752c..045eb3a 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -299,7 +299,7 @@ void dhcp_packet(time_t now, int pxe_fd)
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform, pxe_fd, iface_addr);
lease_update_file(now);
- lease_update_dns();
+ lease_update_dns(0);
if (iov.iov_len == 0)
return;
diff --git a/src/dhcp6.c b/src/dhcp6.c
index 70c8dcd..e8502fb 100644
--- a/src/dhcp6.c
+++ b/src/dhcp6.c
@@ -139,7 +139,7 @@ void dhcp6_packet(time_t now)
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
lease_update_file(now);
- lease_update_dns();
+ lease_update_dns(0);
/* The port in the source address of the original request should
be correct, but at least once client sends from the server port,
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 515f08f..b2810c5 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -23,6 +23,7 @@ struct daemon *daemon;
static volatile pid_t pid = 0;
static volatile int pipewrite;
+static int alarm_queued = 0;
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(fd_set *set, time_t now);
@@ -864,9 +865,23 @@ static void sig_handler(int sig)
}
}
-void send_alarm(void)
+/* now == 0 -> queue immediate callback */
+void send_alarm(time_t event, time_t now)
{
- send_event(pipewrite, EVENT_ALARM, 0, NULL);
+
+ if (now != 0 && event == 0)
+ return;
+
+ if ((now == 0 || difftime(event, now) <= 0.0))
+ {
+ if (!alarm_queued)
+ {
+ send_event(pipewrite, EVENT_ALARM, 0, NULL);
+ alarm_queued = 1;
+ }
+ }
+ else
+ alarm((unsigned)difftime(event, now));
}
void send_event(int fd, int event, int data, char *msg)
@@ -980,6 +995,7 @@ static void async_event(int pipe, time_t now)
break;
case EVENT_ALARM:
+ alarm_queued = 0;
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->dhcp6)
{
@@ -988,13 +1004,8 @@ static void async_event(int pipe, time_t now)
}
#ifdef HAVE_DHCP6
else if (daemon->ra_contexts)
- {
- /* Not doing DHCP, so no lease system, manage
- alarms for ra only */
- time_t next_event = periodic_ra(now);
- if (next_event != 0)
- alarm((unsigned)difftime(next_event, now));
- }
+ /* Not doing DHCP, so no lease system, manage alarms for ra only */
+ send_alarm(periodic_ra(now), now);
#endif
#endif
break;
@@ -1158,7 +1169,7 @@ void clear_cache_and_reload(time_t now)
check_dhcp_hosts(0);
lease_update_from_configs();
lease_update_file(now);
- lease_update_dns();
+ lease_update_dns(1);
}
#ifdef HAVE_DHCP6
else if (daemon->ra_contexts)
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index b35b580..88075cc 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -466,6 +466,7 @@ struct frec {
#define LEASE_USED 16 /* used this DHCPv6 transaction */
#define LEASE_NA 32 /* IPv6 no-temporary lease */
#define LEASE_TA 64 /* IPv6 temporary lease */
+#define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */
struct dhcp_lease {
int clid_len; /* length of client identifier */
@@ -483,6 +484,14 @@ struct dhcp_lease {
unsigned char *extradata;
unsigned int extradata_len, extradata_size;
int last_interface;
+#ifdef HAVE_DHCP6
+ struct slaac_address {
+ struct in6_addr addr, local;
+ time_t ping_time;
+ int backoff; /* zero -> confirmed */
+ struct slaac_address *next;
+ } *slaac_address;
+#endif
struct dhcp_lease *next;
};
@@ -627,7 +636,7 @@ struct dhcp_context {
#ifdef HAVE_DHCP6
struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6;
- int prefix;
+ int prefix, if_index;
time_t ra_time;
#endif
int flags;
@@ -651,13 +660,6 @@ struct ping_result {
struct ping_result *next;
};
-struct subnet_map {
- int iface;
- struct in6_addr subnet;
- struct subnet_map *next;
-};
-
-
struct tftp_file {
int refcount, fd;
off_t size;
@@ -957,7 +959,7 @@ char *host_from_dns(struct in_addr addr);
/* lease.c */
#ifdef HAVE_DHCP
void lease_update_file(time_t now);
-void lease_update_dns();
+void lease_update_dns(int force);
void lease_init(time_t now);
struct dhcp_lease *lease4_allocate(struct in_addr addr);
#ifdef HAVE_DHCP6
@@ -966,12 +968,13 @@ struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
int lease_type, int iaid, struct in6_addr *addr);
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
u64 lease_find_max_addr6(struct dhcp_context *context);
+void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
#endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
- unsigned char *clid, int hw_len, int hw_type, int clid_len);
+ unsigned char *clid, int hw_len, int hw_type, int clid_len, time_t now);
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
-void lease_set_interface(struct dhcp_lease *lease, int interface);
+void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
@@ -1000,7 +1003,7 @@ unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int make_icmp_sock(void);
int icmp_ping(struct in_addr addr);
#endif
-void send_alarm(void);
+void send_alarm(time_t event, time_t now);
void send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now);
void poll_resolv(int force, int do_reload, time_t now);
@@ -1121,6 +1124,14 @@ void put_opt6_string(char *s);
void ra_init(time_t now);
void icmp6_packet(void);
time_t periodic_ra(time_t now);
-void ra_start_unsolicted(time_t now);
-struct subnet_map *build_subnet_map(void);
+void ra_start_unsolicted(time_t now, struct dhcp_context *context);
+#endif
+
+/* slaac.c */
+#ifdef HAVE_DHCP6
+void build_subnet_map(void);
+void slaac_add_addrs(struct dhcp_lease *lease, time_t now);
+time_t periodic_slaac(time_t now, struct dhcp_lease *leases);
+void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases);
+void schedule_subnet_map(void);
#endif
diff --git a/src/lease.c b/src/lease.c
index 3bc2222..608ca7d 100644
--- a/src/lease.c
+++ b/src/lease.c
@@ -97,7 +97,8 @@ void lease_init(time_t now)
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
- lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
+ lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet,
+ hw_len, hw_type, clid_len, now);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL);
@@ -118,7 +119,7 @@ void lease_init(time_t now)
if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
{
- lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len);
+ lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len, now);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL);
@@ -306,9 +307,16 @@ void lease_update_file(time_t now)
next_event = 0;
#ifdef HAVE_DHCP6
- /* do timed RAs and determine when the next is */
+ /* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */
if (daemon->ra_contexts)
- next_event = periodic_ra(now);
+ {
+ time_t ra_event = periodic_slaac(now, leases);
+
+ next_event = periodic_ra(now);
+
+ if (next_event == 0 || difftime(next_event, ra_event) > 0.0)
+ next_event = ra_event;
+ }
#endif
for (lease = leases; lease; lease = lease->next)
@@ -326,8 +334,7 @@ void lease_update_file(time_t now)
(unsigned int)difftime(next_event, now));
}
- if (next_event != 0)
- alarm((unsigned)difftime(next_event, now));
+ send_alarm(next_event, now);
}
@@ -342,7 +349,7 @@ static int find_interface_v4(struct in_addr local, int if_index,
for (lease = leases; lease; lease = lease->next)
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net(local, lease->addr, netmask))
- lease->last_interface = if_index;
+ lease_set_interface(lease, if_index, *((time_t *)vparam));
return 1;
}
@@ -353,17 +360,22 @@ static int find_interface_v6(struct in6_addr *local, int prefix,
{
struct dhcp_lease *lease;
- (void) scope;
- (void) vparam;
+ (void)scope;
(void)dad;
for (lease = leases; lease; lease = lease->next)
if ((lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net6(local, (struct in6_addr *)&lease->hwaddr, prefix))
- lease->last_interface = if_index;
+ lease_set_interface(lease, if_index, *((time_t *)vparam));
return 1;
}
+
+void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface)
+{
+ slaac_ping_reply(sender, packet, interface, leases);
+}
+
#endif
@@ -373,9 +385,13 @@ static int find_interface_v6(struct in6_addr *local, int prefix,
start-time. */
void lease_find_interfaces(time_t now)
{
- iface_enumerate(AF_INET, NULL, find_interface_v4);
#ifdef HAVE_DHCP6
- iface_enumerate(AF_INET6, NULL, find_interface_v6);
+ build_subnet_map();
+#endif
+
+ iface_enumerate(AF_INET, &now, find_interface_v4);
+#ifdef HAVE_DHCP6
+ iface_enumerate(AF_INET6, &now, find_interface_v6);
/* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */
if (!daemon->duid && daemon->dhcp6)
@@ -388,16 +404,12 @@ void lease_find_interfaces(time_t now)
-void lease_update_dns(void)
+void lease_update_dns(int force)
{
struct dhcp_lease *lease;
- if (daemon->port != 0 && dns_dirty)
+ if (daemon->port != 0 && (dns_dirty || force))
{
-#ifdef HAVE_DHCP6
- struct subnet_map *subnets = build_subnet_map();
-#endif
-
cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next)
@@ -409,41 +421,15 @@ void lease_update_dns(void)
prot = AF_INET6;
else if (lease->hostname || lease->fqdn)
{
- struct subnet_map *map;
- for (map = subnets; map; map = map->next)
- if (lease->last_interface == map->iface)
+ struct slaac_address *slaac;
+
+ for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
+ if (slaac->backoff == 0)
{
- struct in6_addr addr = map->subnet;
- if (lease->hwaddr_len == 6 &&
- (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
- {
- /* convert MAC address to EUI-64 */
- memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
- memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
- addr.s6_addr[11] = 0xff;
- addr.s6_addr[12] = 0xfe;
- }
-#if defined(ARPHRD_EUI64)
- else if (lease->hwaddr_len == 8 &&
- lease->hwaddr_type == ARPHRD_EUI64)
- memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
-#endif
-#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
- else if (lease->clid_len == 9 &&
- lease->clid[0] == ARPHRD_EUI64 &&
- lease->hwaddr_type == ARPHRD_IEEE1394)
- /* firewire has EUI-64 identifier as clid */
- memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
-#endif
- else
- continue;
-
- addr.s6_addr[8] ^= 0x02;
-
if (lease->fqdn)
- cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&addr, lease->expires);
+ cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
- cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&addr, lease->expires);
+ cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
}
}
#endif
@@ -713,8 +699,13 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
}
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
- unsigned char *clid, int hw_len, int hw_type, int clid_len)
+ unsigned char *clid, int hw_len, int hw_type, int clid_len, time_t now)
{
+#ifdef HAVE_DHCP6
+ int change = 0;
+ lease->flags |= LEASE_HAVE_HWADDR;
+#endif
+
if (hw_len != lease->hwaddr_len ||
hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
@@ -725,6 +716,9 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
lease->hwaddr_type = hw_type;
lease->flags |= LEASE_CHANGED;
file_dirty = 1; /* run script on change */
+#ifdef HAVE_DHCP6
+ change = 1;
+#endif
}
/* only update clid when one is available, stops packets
@@ -742,17 +736,27 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
free(lease->clid);
if (!(lease->clid = whine_malloc(clid_len)))
return;
+#ifdef HAVE_DHCP6
+ change = 1;
+#endif
}
else if (memcmp(lease->clid, clid, clid_len) != 0)
{
lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
+#ifdef HAVE_DHCP6
+ change = 1;
+#endif
}
-
+
lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len);
}
-
+
+#ifdef HAVE_DHCP6
+ if (change)
+ slaac_add_addrs(lease, now);
+#endif
}
static void kill_name(struct dhcp_lease *lease)
@@ -870,13 +874,17 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *do
lease->flags |= LEASE_CHANGED; /* run script on change */
}
-void lease_set_interface(struct dhcp_lease *lease, int interface)
+void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
{
if (lease->last_interface == interface)
return;
lease->last_interface = interface;
lease->flags |= LEASE_CHANGED;
+
+#ifdef HAVE_DHCP6
+ slaac_add_addrs(lease, now);
+#endif
}
void rerun_scripts(void)
@@ -919,6 +927,14 @@ int do_script_run(time_t now)
}
else
{
+#ifdef HAVE_DHCP6
+ struct slaac_address *slaac, *tmp;
+ for (slaac = lease->slaac_address; slaac; slaac = tmp)
+ {
+ tmp = slaac->next;
+ free(slaac);
+ }
+#endif
kill_name(lease);
#ifdef HAVE_SCRIPT
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
diff --git a/src/netlink.c b/src/netlink.c
index 993c1f9..d20654d 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -345,10 +345,11 @@ static void nl_routechange(struct nlmsghdr *h)
/* force RAs to sync new network and pick up new interfaces. */
if (daemon->ra_contexts)
{
- ra_start_unsolicted(dnsmasq_time());
+ schedule_subnet_map();
+ ra_start_unsolicted(dnsmasq_time(), NULL);
/* cause lease_update_file to run after we return, in case we were called from
iface_enumerate and can't re-enter it now */
- send_alarm();
+ send_alarm(0, 0);
}
#endif
diff --git a/src/radv-protocol.h b/src/radv-protocol.h
index 8f3244f..8e83169 100644
--- a/src/radv-protocol.h
+++ b/src/radv-protocol.h
@@ -17,6 +17,13 @@
#define ALL_HOSTS "FF02::1"
#define ALL_ROUTERS "FF02::2"
+struct ping_packet {
+ u8 type, code;
+ u16 checksum;
+ u16 identifier;
+ u16 sequence_no;
+};
+
struct ra_packet {
u8 type, code;
u16 checksum;
@@ -32,9 +39,6 @@ struct prefix_opt {
struct in6_addr prefix;
};
-#define ICMP6_ROUTER_SOLICIT 133
-#define ICMP6_ROUTER_ADVERT 134
-
#define ICMP6_OPT_SOURCE_MAC 1
#define ICMP6_OPT_PREFIX 3
#define ICMP6_OPT_MTU 5
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 @@ void ra_init(time_t now)
#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 @@ void ra_init(time_t now)
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 @@ void icmp6_packet(void)
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 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
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 @@ static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
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 @@ static int iface_search(struct in6_addr *local, int prefix,
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 @@ static int iface_search(struct in6_addr *local, int prefix,
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
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 39edf78..83dc4c1 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -483,14 +483,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
logaddr = &mess->yiaddr;
- lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
+ lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now);
if (hostname)
lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
/* infinite lease unless nailed in dhcp-host line. */
lease_set_expires(lease,
have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
now);
- lease_set_interface(lease, int_index);
+ lease_set_interface(lease, int_index, now);
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
@@ -1276,7 +1276,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
- lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
+ lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now);
/* if all the netids in the ignore_name list are present, ignore client-supplied name */
if (!hostname_auth)
@@ -1310,7 +1310,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
lease_set_expires(lease, time, now);
- lease_set_interface(lease, int_index);
+ lease_set_interface(lease, int_index, now);
if (override.s_addr != 0)
lease->override = override;
@@ -1387,7 +1387,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else
time = (unsigned int)difftime(lease->expires, now);
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
- lease_set_interface(lease, int_index);
+ lease_set_interface(lease, int_index, now);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
diff --git a/src/rfc3315.c b/src/rfc3315.c
index 353a03e..c55d90c 100644
--- a/src/rfc3315.c
+++ b/src/rfc3315.c
@@ -637,8 +637,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (lease)
{
lease_set_expires(lease, lease_time, now);
- lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len);
- lease_set_interface(lease, interface);
+ lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len, now);
+ lease_set_interface(lease, interface, now);
if (hostname && ia_type == OPTION6_IA_NA)
{
char *addr_domain = get_domain6(addrp);
diff --git a/src/slaac.c b/src/slaac.c
new file mode 100644
index 0000000..fbb21a1
--- /dev/null
+++ b/src/slaac.c
@@ -0,0 +1,243 @@
+/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_DHCP6
+
+#include
+
+static int map_rebuild = 0;
+static int ping_id = 0;
+
+void slaac_add_addrs(struct dhcp_lease *lease, time_t now)
+{
+ struct slaac_address *slaac, *old, **up;
+ struct dhcp_context *context;
+
+ if (!(lease->flags & LEASE_HAVE_HWADDR) ||
+ lease->last_interface == 0 ||
+ !lease->hostname)
+ return ;
+
+ old = lease->slaac_address;
+ lease->slaac_address = NULL;
+
+ for (context = daemon->ra_contexts; context; context = context->next)
+ if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index)
+ {
+ struct in6_addr addr = context->start6;
+ if (lease->hwaddr_len == 6 &&
+ (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
+ {
+ /* convert MAC address to EUI-64 */
+ memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
+ memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
+ addr.s6_addr[11] = 0xff;
+ addr.s6_addr[12] = 0xfe;
+ }
+#if defined(ARPHRD_EUI64)
+ else if (lease->hwaddr_len == 8 &&
+ lease->hwaddr_type == ARPHRD_EUI64)
+ memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
+#endif
+#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
+ else if (lease->clid_len == 9 &&
+ lease->clid[0] == ARPHRD_EUI64 &&
+ lease->hwaddr_type == ARPHRD_IEEE1394)
+ /* firewire has EUI-64 identifier as clid */
+ memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
+#endif
+ else
+ continue;
+
+ addr.s6_addr[8] ^= 0x02;
+
+ /* check if we already have this one */
+ for (up = &old, slaac = old; slaac; slaac = slaac->next)
+ {
+ if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
+ {
+ *up = slaac->next;
+ break;
+ }
+ up = &slaac->next;
+ }
+
+ /* No, make new one */
+ if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
+ {
+ slaac->ping_time = now;
+ slaac->backoff = 1;
+ slaac->addr = addr;
+ slaac->local = context->local6;
+ /* Do RA's to prod it */
+ ra_start_unsolicted(now, context);
+ }
+
+ if (slaac)
+ {
+ slaac->next = lease->slaac_address;
+ lease->slaac_address = slaac;
+ }
+ }
+
+ /* Free any no reused */
+ for (; old; old = slaac)
+ {
+ slaac = old->next;
+ free(old);
+ }
+}
+
+
+time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
+{
+ struct dhcp_context *context;
+ struct dhcp_lease *lease;
+ struct slaac_address *slaac;
+ time_t next_event = 0;
+
+ for (context = daemon->ra_contexts; context; context = context->next)
+ if ((context->flags & CONTEXT_RA_NAME))
+ break;
+
+ /* nothing configured */
+ if (!context)
+ return 0;
+
+ while (ping_id == 0)
+ ping_id = rand16();
+
+ if (map_rebuild)
+ {
+ map_rebuild = 0;
+ build_subnet_map();
+ }
+
+ for (lease = leases; lease; lease = lease->next)
+ for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
+ {
+ /* confirmed? */
+ if (slaac->backoff == 0)
+ continue;
+
+ if (difftime(slaac->ping_time, now) <= 0.0)
+ {
+ struct ping_packet *ping;
+ struct sockaddr_in6 addr;
+
+ save_counter(0);
+ ping = expand(sizeof(struct ping_packet));
+ ping->type = ICMP6_ECHO_REQUEST;
+ ping->code = 0;
+ ping->identifier = ping_id;
+ ping->sequence_no = slaac->backoff;
+
+ memset(&addr, 0, sizeof(addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+ addr.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons(IPPROTO_ICMPV6);
+ addr.sin6_addr = slaac->addr;
+
+ send_from(daemon->icmp6fd, 0, daemon->outpacket.iov_base, save_counter(0),
+ (union mysockaddr *)&addr, (struct all_addr *)&slaac->local, lease->last_interface);
+
+ slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
+ if (slaac->backoff > 4)
+ slaac->ping_time += rand16()/4000; /* 0 - 15 */
+ slaac->backoff++;
+ }
+
+ if (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0)
+ next_event = slaac->ping_time;
+ }
+
+ return next_event;
+}
+
+
+void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
+{
+ struct dhcp_lease *lease;
+ struct slaac_address *slaac;
+ struct ping_packet *ping = (struct ping_packet *)packet;
+ int gotone = 0;
+
+ if (ping->identifier == ping_id)
+ for (lease = leases; lease; lease = lease->next)
+ for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
+ if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
+ {
+ slaac->backoff = 0;
+ gotone = 1;
+ inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
+ my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname);
+ }
+
+ lease_update_dns(gotone);
+}
+
+/* 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.
+*/
+static int add_subnet(struct in6_addr *local, int prefix,
+ int scope, int if_index, int dad, void *vparam)
+{
+ struct dhcp_context *context;
+
+ (void)scope;
+ (void)dad;
+ (void)vparam;
+
+ 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))
+ {
+ context->if_index = if_index;
+ context->local6 = *local;
+ }
+
+ return 1;
+}
+
+void build_subnet_map(void)
+{
+ struct dhcp_context *context;
+ int ok = 0;
+
+ for (context = daemon->ra_contexts; context; context = context->next)
+ {
+ context->if_index = 0;
+ if ((context->flags & CONTEXT_RA_NAME))
+ ok = 1;
+ }
+
+ /* ra-names configured */
+ if (ok)
+ iface_enumerate(AF_INET6, NULL, add_subnet);
+}
+
+void schedule_subnet_map(void)
+{
+ map_rebuild = 1;
+}
+#endif