diff options
author | Tobias Brunner <tobias@strongswan.org> | 2012-09-17 18:09:51 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2012-09-21 18:16:26 +0200 |
commit | 1f97e1aacaa461aafa0387d63f8480ca161742c1 (patch) | |
tree | db3f22964f91ecc7ea12e15866c4721b7a7bdf9d /src/libhydra | |
parent | 090c556ce862e59eebdaea1c91c74ab561ac3d6e (diff) | |
download | strongswan-1f97e1aacaa461aafa0387d63f8480ca161742c1.tar.bz2 strongswan-1f97e1aacaa461aafa0387d63f8480ca161742c1.tar.xz |
Use a hashtable to quickly check for usable IP addresses/interfaces
Diffstat (limited to 'src/libhydra')
-rw-r--r-- | src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c | 185 | ||||
-rw-r--r-- | src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c | 184 |
2 files changed, 284 insertions, 85 deletions
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 @@ -133,11 +133,71 @@ static bool iface_entry_by_index(iface_entry_t *this, int *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; @@ -273,6 +333,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) */ netlink_socket_t *socket; @@ -486,6 +551,48 @@ static int get_vip_refcount(private_kernel_netlink_net_t *this, host_t* ip) } /** + * 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 * found return the address as above. @@ -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), diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 0095c6630..ae137096e 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -100,13 +100,72 @@ static void iface_entry_destroy(iface_entry_t *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; @@ -131,6 +190,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 */ mutex_t *mutex_pfroute; @@ -157,6 +221,48 @@ struct private_kernel_pfroute_net_t }; /** + * 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 */ static job_requeue_t roam_event(uintptr_t address) @@ -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)) - { - 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) + /* 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) { - 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), ); |