From 1f97e1aacaa461aafa0387d63f8480ca161742c1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 17 Sep 2012 18:09:51 +0200 Subject: Use a hashtable to quickly check for usable IP addresses/interfaces --- .../plugins/kernel_netlink/kernel_netlink_net.c | 185 ++++++++++++++++----- 1 file changed, 143 insertions(+), 42 deletions(-) (limited to 'src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c') diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index 09a850667..745f40968 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -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) + if (name) { - break; + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "%H is on interface %s", ip, *name); } + this->mutex->unlock(this->mutex); + return TRUE; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - if (!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) { - if (!found) - { - DBG2(DBG_KNL, "%H is not a local address", ip); - } - else if (name) - { - DBG2(DBG_KNL, "%H is on interface %s", ip, *name); - } + DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip); } - return found && !ignored; + 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), -- cgit v1.2.3