Use a hashtable to quickly check for usable IP addresses/interfaces

This commit is contained in:
Tobias Brunner 2012-09-17 18:09:51 +02:00
parent 090c556ce8
commit 1f97e1aaca
2 changed files with 285 additions and 86 deletions

View File

@ -132,12 +132,72 @@ static bool iface_entry_by_index(iface_entry_t *this, int *ifindex)
return this->ifindex == *ifindex;
}
/**
* check if an interface is up
*/
static inline bool iface_entry_up(iface_entry_t *iface)
{
return (iface->flags & IFF_UP) == IFF_UP;
}
/**
* check if an interface is up and usable
*/
static inline bool iface_entry_up_and_usable(iface_entry_t *iface)
{
return iface->usable && (iface->flags & IFF_UP) == IFF_UP;
return iface->usable && iface_entry_up(iface);
}
typedef struct addr_map_entry_t addr_map_entry_t;
/**
* Entry that maps an IP address to an interface entry
*/
struct addr_map_entry_t {
/** The IP address */
host_t *ip;
/** The interface this address is installed on */
iface_entry_t *iface;
};
/**
* Hash a addr_map_entry_t object, all entries with the same IP address
* are stored in the same bucket
*/
static u_int addr_map_entry_hash(addr_map_entry_t *this)
{
return chunk_hash(this->ip->get_address(this->ip));
}
/**
* Compare two addr_map_entry_t objects, two entries are equal if they are
* installed on the same interface
*/
static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b)
{
return a->iface->ifindex == b->iface->ifindex &&
a->ip->ip_equals(a->ip, b->ip);
}
/**
* Used with get_match this finds an address entry if it is installed on
* an up and usable interface
*/
static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a,
addr_map_entry_t *b)
{
return iface_entry_up_and_usable(b->iface) &&
a->ip->ip_equals(a->ip, b->ip);
}
/**
* Used with get_match this finds an address entry if it is installed on
* any active local interface
*/
static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b)
{
return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip);
}
typedef struct route_entry_t route_entry_t;
@ -272,6 +332,11 @@ struct private_kernel_netlink_net_t {
*/
linked_list_t *ifaces;
/**
* Map for IP addresses to iface_entry_t objects (addr_map_entry_t)
*/
hashtable_t *addrs;
/**
* netlink rt socket (routing)
*/
@ -485,6 +550,48 @@ static int get_vip_refcount(private_kernel_netlink_net_t *this, host_t* ip)
return refcount;
}
/**
* Add an address map entry
*/
static void addr_map_entry_add(private_kernel_netlink_net_t *this,
addr_entry_t *addr, iface_entry_t *iface)
{
addr_map_entry_t *entry;
if (addr->virtual)
{ /* don't map virtual IPs */
return;
}
INIT(entry,
.ip = addr->ip,
.iface = iface,
);
entry = this->addrs->put(this->addrs, entry, entry);
free(entry);
}
/**
* Remove an address map entry (the argument order is a bit strange because
* it is also used with linked_list_t.invoke_function)
*/
static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
private_kernel_netlink_net_t *this)
{
addr_map_entry_t *entry, lookup = {
.ip = addr->ip,
.iface = iface,
};
if (addr->virtual)
{ /* these are never mapped, but this check avoids problems if a
* virtual IP equals a regular one */
return;
}
entry = this->addrs->remove(this->addrs, &lookup);
free(entry);
}
/**
* get the first non-virtual ip address on the given interface.
* if a candidate address is given, we first search for that address and if not
@ -681,6 +788,8 @@ static void process_link(private_kernel_netlink_net_t *this,
DBG1(DBG_KNL, "interface %s deleted", current->ifname);
}
this->ifaces->remove_at(this->ifaces, enumerator);
current->addrs->invoke_function(current->addrs,
(void*)addr_map_entry_remove, current, this);
iface_entry_destroy(current);
break;
}
@ -774,6 +883,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
DBG1(DBG_KNL, "%H disappeared from %s",
host, iface->ifname);
}
addr_map_entry_remove(addr, iface, this);
addr_entry_destroy(addr);
}
else if (hdr->nlmsg_type == RTM_NEWADDR && addr->virtual)
@ -798,6 +908,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
addr->scope = msg->ifa_scope;
iface->addrs->insert_last(iface->addrs, addr);
addr_map_entry_add(this, addr, iface);
if (event && iface->usable)
{
DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname);
@ -1059,59 +1170,37 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
METHOD(kernel_net_t, get_interface_name, bool,
private_kernel_netlink_net_t *this, host_t* ip, char **name)
{
enumerator_t *ifaces, *addrs;
iface_entry_t *iface;
addr_entry_t *addr;
bool found = FALSE, ignored = FALSE;
addr_map_entry_t *entry, lookup = {
.ip = ip,
};
if (ip->is_anyaddr(ip))
{
return FALSE;
}
this->mutex->lock(this->mutex);
ifaces = this->ifaces->create_enumerator(this->ifaces);
while (ifaces->enumerate(ifaces, &iface))
/* first try to find it on an up and usable interface */
entry = this->addrs->get_match(this->addrs, &lookup,
(void*)addr_map_entry_match_up_and_usable);
if (entry)
{
addrs = iface->addrs->create_enumerator(iface->addrs);
while (addrs->enumerate(addrs, &addr))
{
if (ip->ip_equals(ip, addr->ip))
{
found = TRUE;
if (!iface->usable)
{
ignored = TRUE;
break;
}
if (name)
{
*name = strdup(iface->ifname);
}
break;
}
}
addrs->destroy(addrs);
if (found)
{
break;
}
}
ifaces->destroy(ifaces);
this->mutex->unlock(this->mutex);
if (!ignored)
{
if (!found)
{
DBG2(DBG_KNL, "%H is not a local address", ip);
}
else if (name)
if (name)
{
*name = strdup(entry->iface->ifname);
DBG2(DBG_KNL, "%H is on interface %s", ip, *name);
}
this->mutex->unlock(this->mutex);
return TRUE;
}
return found && !ignored;
/* maybe it is installed on an ignored interface */
entry = this->addrs->get_match(this->addrs, &lookup,
(void*)addr_map_entry_match_up);
if (!entry)
{
DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip);
}
this->mutex->unlock(this->mutex);
return FALSE;
}
/**
@ -1955,6 +2044,7 @@ METHOD(kernel_net_t, destroy, void,
{
enumerator_t *enumerator;
route_entry_t *route;
addr_map_entry_t *addr;
if (this->routing_table)
{
@ -1982,6 +2072,14 @@ METHOD(kernel_net_t, destroy, void,
this->net_changes->destroy(this->net_changes);
this->net_changes_lock->destroy(this->net_changes_lock);
enumerator = this->addrs->create_enumerator(this->addrs);
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
{
free(addr);
}
enumerator->destroy(enumerator);
this->addrs->destroy(this->addrs);
this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
this->rt_exclude->destroy(this->rt_exclude);
this->condvar->destroy(this->condvar);
@ -2020,6 +2118,9 @@ kernel_netlink_net_t *kernel_netlink_net_create()
.net_changes = hashtable_create(
(hashtable_hash_t)net_change_hash,
(hashtable_equals_t)net_change_equals, 16),
.addrs = hashtable_create(
(hashtable_hash_t)addr_map_entry_hash,
(hashtable_equals_t)addr_map_entry_equals, 16),
.net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT),
.ifaces = linked_list_create(),
.mutex = mutex_create(MUTEX_TYPE_RECURSIVE),

View File

@ -99,14 +99,73 @@ static void iface_entry_destroy(iface_entry_t *this)
free(this);
}
/**
* check if an interface is up
*/
static inline bool iface_entry_up(iface_entry_t *iface)
{
return (iface->flags & IFF_UP) == IFF_UP;
}
/**
* check if an interface is up and usable
*/
static inline bool iface_entry_up_and_usable(iface_entry_t *iface)
{
return iface->usable && (iface->flags & IFF_UP) == IFF_UP;
return iface->usable && iface_entry_up(iface);
}
typedef struct addr_map_entry_t addr_map_entry_t;
/**
* Entry that maps an IP address to an interface entry
*/
struct addr_map_entry_t {
/** The IP address */
host_t *ip;
/** The interface this address is installed on */
iface_entry_t *iface;
};
/**
* Hash a addr_map_entry_t object, all entries with the same IP address
* are stored in the same bucket
*/
static u_int addr_map_entry_hash(addr_map_entry_t *this)
{
return chunk_hash(this->ip->get_address(this->ip));
}
/**
* Compare two addr_map_entry_t objects, two entries are equal if they are
* installed on the same interface
*/
static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b)
{
return a->iface->ifindex == b->iface->ifindex &&
a->ip->ip_equals(a->ip, b->ip);
}
/**
* Used with get_match this finds an address entry if it is installed on
* an up and usable interface
*/
static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a,
addr_map_entry_t *b)
{
return iface_entry_up_and_usable(b->iface) &&
a->ip->ip_equals(a->ip, b->ip);
}
/**
* Used with get_match this finds an address entry if it is installed on
* any active local interface
*/
static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b)
{
return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip);
}
typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t;
@ -130,6 +189,11 @@ struct private_kernel_pfroute_net_t
*/
linked_list_t *ifaces;
/**
* Map for IP addresses to iface_entry_t objects (addr_map_entry_t)
*/
hashtable_t *addrs;
/**
* mutex to lock access to the PF_ROUTE socket
*/
@ -156,6 +220,48 @@ struct private_kernel_pfroute_net_t
timeval_t last_roam;
};
/**
* Add an address map entry
*/
static void addr_map_entry_add(private_kernel_netlink_net_t *this,
addr_entry_t *addr, iface_entry_t *iface)
{
addr_map_entry_t *entry;
if (addr->virtual)
{ /* don't map virtual IPs */
return;
}
INIT(entry,
.ip = addr->ip,
.iface = iface,
);
entry = this->addrs->put(this->addrs, entry, entry);
free(entry);
}
/**
* Remove an address map entry (the argument order is a bit strange because
* it is also used with linked_list_t.invoke_function)
*/
static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
private_kernel_netlink_net_t *this)
{
addr_map_entry_t *entry, lookup = {
.ip = addr->ip,
.iface = iface,
};
if (addr->virtual)
{ /* these are never mapped, but this check avoid problems if a virtual IP
* equals a regular one */
return;
}
entry = this->addrs->remove(this->addrs, &lookup);
free(entry);
}
/**
* callback function that raises the delayed roam event
*/
@ -246,6 +352,7 @@ static void process_addr(private_kernel_pfroute_net_t *this,
DBG1(DBG_KNL, "%H disappeared from %s",
host, iface->ifname);
}
addr_map_entry_remove(addr, iface, this);
addr_entry_destroy(addr);
}
else if (ifa->ifam_type == RTM_NEWADDR && addr->virtual)
@ -264,6 +371,7 @@ static void process_addr(private_kernel_pfroute_net_t *this,
addr->virtual = FALSE;
addr->refcount = 1;
iface->addrs->insert_last(iface->addrs, addr);
addr_map_entry_add(this, addr, iface);
if (iface->usable)
{
DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname);
@ -496,59 +604,37 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
METHOD(kernel_net_t, get_interface_name, bool,
private_kernel_pfroute_net_t *this, host_t* ip, char **name)
{
enumerator_t *ifaces, *addrs;
iface_entry_t *iface;
addr_entry_t *addr;
bool found = FALSE, ignored = FALSE;
addr_map_entry_t *entry, lookup = {
.ip = ip,
};
if (ip->is_anyaddr(ip))
{
return FALSE;
}
this->mutex->lock(this->mutex);
ifaces = this->ifaces->create_enumerator(this->ifaces);
while (ifaces->enumerate(ifaces, &iface))
/* first try to find it on an up and usable interface */
entry = this->addrs->get_match(this->addrs, &lookup,
(void*)addr_map_entry_match_up_and_usable);
if (entry)
{
addrs = iface->addrs->create_enumerator(iface->addrs);
while (addrs->enumerate(addrs, &addr))
{
if (ip->ip_equals(ip, addr->ip))
{
found = TRUE;
if (!iface->usable)
{
ignored = TRUE;
break;
}
if (name)
{
*name = strdup(iface->ifname);
}
break;
}
}
addrs->destroy(addrs);
if (found)
{
break;
}
}
ifaces->destroy(ifaces);
this->mutex->unlock(this->mutex);
if (!ignored)
{
if (!found)
{
DBG2(DBG_KNL, "%H is not a local address", ip);
}
else if (name)
if (name)
{
*name = strdup(entry->iface->ifname);
DBG2(DBG_KNL, "%H is on interface %s", ip, *name);
}
this->mutex->unlock(this->mutex);
return TRUE;
}
return found && !ignored;
/* maybe it is installed on an ignored interface */
entry = this->addrs->get_match(this->addrs, &lookup,
(void*)addr_map_entry_match_up);
if (!entry)
{ /* the address does not exist, is on a down interface */
DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip);
}
this->mutex->unlock(this->mutex);
return FALSE;
}
METHOD(kernel_net_t, get_source_addr, host_t*,
@ -678,6 +764,8 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this)
METHOD(kernel_net_t, destroy, void,
private_kernel_pfroute_net_t *this)
{
enumerator_t *enumerator;
if (this->socket > 0)
{
close(this->socket);
@ -686,6 +774,13 @@ METHOD(kernel_net_t, destroy, void,
{
close(this->socket_events);
}
enumerator = this->addrs->create_enumerator(this->addrs);
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
{
free(addr);
}
enumerator->destroy(enumerator);
this->addrs->destroy(this->addrs);
this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
this->mutex->destroy(this->mutex);
this->mutex_pfroute->destroy(this->mutex_pfroute);
@ -715,6 +810,9 @@ kernel_pfroute_net_t *kernel_pfroute_net_create()
},
},
.ifaces = linked_list_create(),
.addrs = hashtable_create(
(hashtable_hash_t)addr_map_entry_hash,
(hashtable_equals_t)addr_map_entry_equals, 16),
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.mutex_pfroute = mutex_create(MUTEX_TYPE_DEFAULT),
);