Check assumed SLAAC addresses by pinging them.

This commit is contained in:
Simon Kelley
2012-03-19 20:07:51 +00:00
parent e759d426fa
commit 353ae4d270
13 changed files with 414 additions and 177 deletions

View File

@@ -65,7 +65,7 @@ version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \ 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 \ 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 \ 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 \ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h dns-protocol.h radv-protocol.h

View File

@@ -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 \ netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \ rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \ dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c radv.c slaac.c
LOCAL_MODULE := dnsmasq LOCAL_MODULE := dnsmasq

View File

@@ -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, iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform, pxe_fd, iface_addr); now, unicast_dest, &is_inform, pxe_fd, iface_addr);
lease_update_file(now); lease_update_file(now);
lease_update_dns(); lease_update_dns(0);
if (iov.iov_len == 0) if (iov.iov_len == 0)
return; return;

View File

@@ -139,7 +139,7 @@ void dhcp6_packet(time_t now)
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now); sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
lease_update_file(now); lease_update_file(now);
lease_update_dns(); lease_update_dns(0);
/* The port in the source address of the original request should /* The port in the source address of the original request should
be correct, but at least once client sends from the server port, be correct, but at least once client sends from the server port,

View File

@@ -23,6 +23,7 @@ struct daemon *daemon;
static volatile pid_t pid = 0; static volatile pid_t pid = 0;
static volatile int pipewrite; static volatile int pipewrite;
static int alarm_queued = 0;
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp); static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(fd_set *set, time_t now); 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) 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; break;
case EVENT_ALARM: case EVENT_ALARM:
alarm_queued = 0;
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
if (daemon->dhcp || daemon->dhcp6) if (daemon->dhcp || daemon->dhcp6)
{ {
@@ -988,13 +1004,8 @@ static void async_event(int pipe, time_t now)
} }
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
else if (daemon->ra_contexts) else if (daemon->ra_contexts)
{ /* Not doing DHCP, so no lease system, manage alarms for ra only */
/* Not doing DHCP, so no lease system, manage send_alarm(periodic_ra(now), now);
alarms for ra only */
time_t next_event = periodic_ra(now);
if (next_event != 0)
alarm((unsigned)difftime(next_event, now));
}
#endif #endif
#endif #endif
break; break;
@@ -1158,7 +1169,7 @@ void clear_cache_and_reload(time_t now)
check_dhcp_hosts(0); check_dhcp_hosts(0);
lease_update_from_configs(); lease_update_from_configs();
lease_update_file(now); lease_update_file(now);
lease_update_dns(); lease_update_dns(1);
} }
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
else if (daemon->ra_contexts) else if (daemon->ra_contexts)

View File

@@ -466,6 +466,7 @@ struct frec {
#define LEASE_USED 16 /* used this DHCPv6 transaction */ #define LEASE_USED 16 /* used this DHCPv6 transaction */
#define LEASE_NA 32 /* IPv6 no-temporary lease */ #define LEASE_NA 32 /* IPv6 no-temporary lease */
#define LEASE_TA 64 /* IPv6 temporary lease */ #define LEASE_TA 64 /* IPv6 temporary lease */
#define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */
struct dhcp_lease { struct dhcp_lease {
int clid_len; /* length of client identifier */ int clid_len; /* length of client identifier */
@@ -483,6 +484,14 @@ struct dhcp_lease {
unsigned char *extradata; unsigned char *extradata;
unsigned int extradata_len, extradata_size; unsigned int extradata_len, extradata_size;
int last_interface; 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; struct dhcp_lease *next;
}; };
@@ -627,7 +636,7 @@ struct dhcp_context {
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
struct in6_addr start6, end6; /* range of available addresses */ struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6; struct in6_addr local6;
int prefix; int prefix, if_index;
time_t ra_time; time_t ra_time;
#endif #endif
int flags; int flags;
@@ -651,13 +660,6 @@ struct ping_result {
struct ping_result *next; struct ping_result *next;
}; };
struct subnet_map {
int iface;
struct in6_addr subnet;
struct subnet_map *next;
};
struct tftp_file { struct tftp_file {
int refcount, fd; int refcount, fd;
off_t size; off_t size;
@@ -957,7 +959,7 @@ char *host_from_dns(struct in_addr addr);
/* lease.c */ /* lease.c */
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
void lease_update_file(time_t now); void lease_update_file(time_t now);
void lease_update_dns(); void lease_update_dns(int force);
void lease_init(time_t now); void lease_init(time_t now);
struct dhcp_lease *lease4_allocate(struct in_addr addr); struct dhcp_lease *lease4_allocate(struct in_addr addr);
#ifdef HAVE_DHCP6 #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); int lease_type, int iaid, struct in6_addr *addr);
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 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); u64 lease_find_max_addr6(struct dhcp_context *context);
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
#endif #endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr, 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_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_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, struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len); unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr); 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 make_icmp_sock(void);
int icmp_ping(struct in_addr addr); int icmp_ping(struct in_addr addr);
#endif #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 send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now); void clear_cache_and_reload(time_t now);
void poll_resolv(int force, int do_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 ra_init(time_t now);
void icmp6_packet(void); void icmp6_packet(void);
time_t periodic_ra(time_t now); time_t periodic_ra(time_t now);
void ra_start_unsolicted(time_t now); void ra_start_unsolicted(time_t now, struct dhcp_context *context);
struct subnet_map *build_subnet_map(void); #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 #endif

View File

@@ -97,7 +97,8 @@ void lease_init(time_t now)
if (hw_type == 0 && hw_len != 0) if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER; 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) if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL); 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))) 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) if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL); 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; next_event = 0;
#ifdef HAVE_DHCP6 #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) 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 #endif
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
@@ -326,8 +334,7 @@ void lease_update_file(time_t now)
(unsigned int)difftime(next_event, now)); (unsigned int)difftime(next_event, now));
} }
if (next_event != 0) send_alarm(next_event, now);
alarm((unsigned)difftime(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) for (lease = leases; lease; lease = lease->next)
if (!(lease->flags & (LEASE_TA | LEASE_NA))) if (!(lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net(local, lease->addr, netmask)) if (is_same_net(local, lease->addr, netmask))
lease->last_interface = if_index; lease_set_interface(lease, if_index, *((time_t *)vparam));
return 1; return 1;
} }
@@ -353,17 +360,22 @@ static int find_interface_v6(struct in6_addr *local, int prefix,
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
(void) scope; (void)scope;
(void) vparam;
(void)dad; (void)dad;
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
if ((lease->flags & (LEASE_TA | LEASE_NA))) if ((lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net6(local, (struct in6_addr *)&lease->hwaddr, prefix)) 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; return 1;
} }
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface)
{
slaac_ping_reply(sender, packet, interface, leases);
}
#endif #endif
@@ -373,9 +385,13 @@ static int find_interface_v6(struct in6_addr *local, int prefix,
start-time. */ start-time. */
void lease_find_interfaces(time_t now) void lease_find_interfaces(time_t now)
{ {
iface_enumerate(AF_INET, NULL, find_interface_v4);
#ifdef HAVE_DHCP6 #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 we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */
if (!daemon->duid && daemon->dhcp6) 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; 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(); cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next) for (lease = leases; lease; lease = lease->next)
@@ -409,41 +421,15 @@ void lease_update_dns(void)
prot = AF_INET6; prot = AF_INET6;
else if (lease->hostname || lease->fqdn) else if (lease->hostname || lease->fqdn)
{ {
struct subnet_map *map; struct slaac_address *slaac;
for (map = subnets; map; map = map->next)
if (lease->last_interface == map->iface) 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) 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) 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 #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, 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 || if (hw_len != lease->hwaddr_len ||
hw_type != lease->hwaddr_type || hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0)) (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->hwaddr_type = hw_type;
lease->flags |= LEASE_CHANGED; lease->flags |= LEASE_CHANGED;
file_dirty = 1; /* run script on change */ file_dirty = 1; /* run script on change */
#ifdef HAVE_DHCP6
change = 1;
#endif
} }
/* only update clid when one is available, stops packets /* 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); free(lease->clid);
if (!(lease->clid = whine_malloc(clid_len))) if (!(lease->clid = whine_malloc(clid_len)))
return; return;
#ifdef HAVE_DHCP6
change = 1;
#endif
} }
else if (memcmp(lease->clid, clid, clid_len) != 0) else if (memcmp(lease->clid, clid, clid_len) != 0)
{ {
lease->flags |= LEASE_AUX_CHANGED; lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1; file_dirty = 1;
#ifdef HAVE_DHCP6
change = 1;
#endif
} }
lease->clid_len = clid_len; lease->clid_len = clid_len;
memcpy(lease->clid, clid, 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) 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 */ 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) if (lease->last_interface == interface)
return; return;
lease->last_interface = interface; lease->last_interface = interface;
lease->flags |= LEASE_CHANGED; lease->flags |= LEASE_CHANGED;
#ifdef HAVE_DHCP6
slaac_add_addrs(lease, now);
#endif
} }
void rerun_scripts(void) void rerun_scripts(void)
@@ -919,6 +927,14 @@ int do_script_run(time_t now)
} }
else 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); kill_name(lease);
#ifdef HAVE_SCRIPT #ifdef HAVE_SCRIPT
queue_script(ACTION_DEL, lease, lease->old_hostname, now); queue_script(ACTION_DEL, lease, lease->old_hostname, now);

View File

@@ -345,10 +345,11 @@ static void nl_routechange(struct nlmsghdr *h)
/* force RAs to sync new network and pick up new interfaces. */ /* force RAs to sync new network and pick up new interfaces. */
if (daemon->ra_contexts) 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 /* cause lease_update_file to run after we return, in case we were called from
iface_enumerate and can't re-enter it now */ iface_enumerate and can't re-enter it now */
send_alarm(); send_alarm(0, 0);
} }
#endif #endif

View File

@@ -17,6 +17,13 @@
#define ALL_HOSTS "FF02::1" #define ALL_HOSTS "FF02::1"
#define ALL_ROUTERS "FF02::2" #define ALL_ROUTERS "FF02::2"
struct ping_packet {
u8 type, code;
u16 checksum;
u16 identifier;
u16 sequence_no;
};
struct ra_packet { struct ra_packet {
u8 type, code; u8 type, code;
u16 checksum; u16 checksum;
@@ -32,9 +39,6 @@ struct prefix_opt {
struct in6_addr prefix; struct in6_addr prefix;
}; };
#define ICMP6_ROUTER_SOLICIT 133
#define ICMP6_ROUTER_ADVERT 134
#define ICMP6_OPT_SOURCE_MAC 1 #define ICMP6_OPT_SOURCE_MAC 1
#define ICMP6_OPT_PREFIX 3 #define ICMP6_OPT_PREFIX 3
#define ICMP6_OPT_MTU 5 #define ICMP6_OPT_MTU 5

View File

@@ -55,13 +55,20 @@ void ra_init(time_t now)
#endif #endif
int val = 255; /* radvd uses this value */ int val = 255; /* radvd uses this value */
socklen_t len = sizeof(int); socklen_t len = sizeof(int);
struct dhcp_context *context;
/* ensure this is around even if we're not doing DHCPv6 */ /* ensure this is around even if we're not doing DHCPv6 */
expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet)); 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_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &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 || if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) || getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
@@ -77,21 +84,21 @@ void ra_init(time_t now)
daemon->icmp6fd = fd; 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 some/all soon. some ra_times will end up zeroed
/* init timers so that we do ra's for all soon. some ra_times will end up zeroed
if it's not appropriate to advertise those contexts. if it's not appropriate to advertise those contexts.
This gets re-called on a netlink route-change to re-do the advertisement This gets re-called on a netlink route-change to re-do the advertisement
and pick up new interfaces */ and pick up new interfaces */
/* range 0 - 5 */ if (context)
for (context = daemon->ra_contexts; context; context = context->next) context->ra_time = now;
context->ra_time = now + (rand16()/13000); 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. */ /* re-do frequently for a minute or so, in case the first gets lost. */
ra_short_period_start = now; ra_short_period_start = now;
@@ -158,7 +165,19 @@ void icmp6_packet(void)
p = (unsigned char *)daemon->outpacket.iov_base; 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; return;
/* look for link-layer address option for logging */ /* 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); save_counter(0);
ra = expand(sizeof(struct ra_packet)); ra = expand(sizeof(struct ra_packet));
ra->type = ICMP6_ROUTER_ADVERT; ra->type = ND_ROUTER_ADVERT;
ra->code = 0; ra->code = 0;
ra->hop_limit = hop_limit; ra->hop_limit = hop_limit;
ra->flags = 0; 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); addr.sin6_port = htons(IPPROTO_ICMPV6);
if (dest) if (dest)
{ {
memcpy(&addr.sin6_addr, dest, sizeof(struct in6_addr)); addr.sin6_addr = *dest;
if (IN6_IS_ADDR_LINKLOCAL(dest) || if (IN6_IS_ADDR_LINKLOCAL(dest) ||
IN6_IS_ADDR_MC_LINKLOCAL(dest)) IN6_IS_ADDR_MC_LINKLOCAL(dest))
addr.sin6_scope_id = iface; addr.sin6_scope_id = iface;
@@ -406,7 +425,7 @@ static int iface_search(struct in6_addr *local, int prefix,
if (prefix == context->prefix && if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) && is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, 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 /* found an interface that's overdue for RA determine new
timeout value and zap other contexts on the same interface 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 */ 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 #endif

View File

@@ -483,14 +483,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{ {
logaddr = &mess->yiaddr; 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) if (hostname)
lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain); lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
/* infinite lease unless nailed in dhcp-host line. */ /* infinite lease unless nailed in dhcp-host line. */
lease_set_expires(lease, lease_set_expires(lease,
have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
now); now);
lease_set_interface(lease, int_index); lease_set_interface(lease, int_index, now);
clear_packet(mess, end); clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), 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)); 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 all the netids in the ignore_name list are present, ignore client-supplied name */
if (!hostname_auth) 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_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
lease_set_expires(lease, time, now); lease_set_expires(lease, time, now);
lease_set_interface(lease, int_index); lease_set_interface(lease, int_index, now);
if (override.s_addr != 0) if (override.s_addr != 0)
lease->override = override; lease->override = override;
@@ -1387,7 +1387,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else else
time = (unsigned int)difftime(lease->expires, now); time = (unsigned int)difftime(lease->expires, now);
option_put(mess, end, OPTION_LEASE_TIME, 4, time); 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), do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),

View File

@@ -637,8 +637,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (lease) if (lease)
{ {
lease_set_expires(lease, lease_time, now); lease_set_expires(lease, lease_time, now);
lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len); lease_set_hwaddr(lease, NULL, clid, 0, iaid, clid_len, now);
lease_set_interface(lease, interface); lease_set_interface(lease, interface, now);
if (hostname && ia_type == OPTION6_IA_NA) if (hostname && ia_type == OPTION6_IA_NA)
{ {
char *addr_domain = get_domain6(addrp); char *addr_domain = get_domain6(addrp);

243
src/slaac.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP6
#include <netinet/icmp6.h>
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