Fix logic for associating leases with interfaces.

This handles the case that more than one interface contains
the network the lease address is on, but the interfaces have different
prefix lengths. Use the longest prefix length.
This commit is contained in:
Simon Kelley
2014-07-12 16:39:00 +01:00
3 changed files with 47 additions and 9 deletions

View File

@@ -641,6 +641,8 @@ 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;
int new_interface; /* save possible originated interface */
int new_prefixlen; /* and its prefix length */
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
struct in6_addr addr6; struct in6_addr addr6;
int iaid; int iaid;
@@ -1132,6 +1134,7 @@ int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(const char *a, const char *b); int hostname_isequal(const char *a, const char *b);
time_t dnsmasq_time(void); time_t dnsmasq_time(void);
int netmask_length(struct in_addr mask);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask); int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen); int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
@@ -1244,6 +1247,7 @@ char *host_from_dns(struct in_addr addr);
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
void lease_update_file(time_t now); void lease_update_file(time_t now);
void lease_update_dns(int force); void lease_update_dns(int force);
void lease_update_interface(time_t now);
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

View File

@@ -352,16 +352,21 @@ static int find_interface_v4(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam) struct in_addr netmask, struct in_addr broadcast, void *vparam)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
int prefix;
(void) label; (void) label;
(void) broadcast; (void) broadcast;
(void) vparam; (void) vparam;
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)) prefix = netmask_length(netmask);
lease_set_interface(lease, if_index, *((time_t *)vparam)); if (is_same_net(local, lease->addr, netmask) && prefix > lease->new_prefixlen) {
lease->new_interface = if_index;
lease->new_prefixlen = prefix;
}
}
return 1; return 1;
} }
@@ -371,17 +376,23 @@ static int find_interface_v6(struct in6_addr *local, int prefix,
int preferred, int valid, void *vparam) int preferred, int valid, void *vparam)
{ {
struct dhcp_lease *lease; struct dhcp_lease *lease;
(void)scope; (void)scope;
(void)flags; (void)flags;
(void)preferred; (void)preferred;
(void)valid; (void)valid;
(void)vparam;
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, &lease->addr6, prefix)) if (is_same_net6(local, &lease->addr6, prefix) && prefix > lease->new_prefixlen) {
lease_set_interface(lease, if_index, *((time_t *)vparam)); /* save prefix length for comparison, as we might get shorter matching
* prefix in upcoming netlink GETADDR responses
* */
lease->new_interface = if_index;
lease->new_prefixlen = prefix;
}
return 1; return 1;
} }
@@ -418,6 +429,7 @@ void lease_find_interfaces(time_t now)
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
iface_enumerate(AF_INET6, &now, find_interface_v6); iface_enumerate(AF_INET6, &now, find_interface_v6);
#endif #endif
lease_update_interface(now);
} }
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
@@ -492,6 +504,16 @@ void lease_update_dns(int force)
} }
} }
void lease_update_interface(time_t now)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
if (lease->new_interface > 0) {
lease_set_interface(lease, lease->new_interface, now);
}
}
void lease_prune(struct dhcp_lease *target, time_t now) void lease_prune(struct dhcp_lease *target, time_t now)
{ {
struct dhcp_lease *lease, *tmp, **up; struct dhcp_lease *lease, *tmp, **up;

View File

@@ -319,6 +319,18 @@ time_t dnsmasq_time(void)
#endif #endif
} }
int netmask_length(struct in_addr mask)
{
int zero_count = 0;
while (0x0 == (mask.s_addr & 0x1)) {
mask.s_addr >>= 1;
++zero_count;
}
return 32 - zero_count;
}
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask) int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
{ {
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr); return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);