Changed how kernel-netlink handles virtual IP addresses
Also tried to avoid the use of enumerators.
This commit is contained in:
@@ -66,21 +66,21 @@
|
|||||||
typedef struct addr_entry_t addr_entry_t;
|
typedef struct addr_entry_t addr_entry_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IP address in an inface_entry_t
|
* IP address in an iface_entry_t
|
||||||
*/
|
*/
|
||||||
struct addr_entry_t {
|
struct addr_entry_t {
|
||||||
|
|
||||||
/** The ip address */
|
/** the ip address */
|
||||||
host_t *ip;
|
host_t *ip;
|
||||||
|
|
||||||
/** virtual IP managed by us */
|
|
||||||
bool virtual;
|
|
||||||
|
|
||||||
/** scope of the address */
|
/** scope of the address */
|
||||||
u_char scope;
|
u_char scope;
|
||||||
|
|
||||||
/** Number of times this IP is used, if virtual */
|
/** number of times this IP is used, if virtual (> 0 IP is managed by us) */
|
||||||
u_int refcount;
|
u_int refcount;
|
||||||
|
|
||||||
|
/** TRUE once it is installed, if virtual */
|
||||||
|
bool installed;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,6 +132,14 @@ static bool iface_entry_by_index(iface_entry_t *this, int *ifindex)
|
|||||||
return this->ifindex == *ifindex;
|
return this->ifindex == *ifindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find an interface entry by name
|
||||||
|
*/
|
||||||
|
static bool iface_entry_by_name(iface_entry_t *this, char *ifname)
|
||||||
|
{
|
||||||
|
return streq(this->ifname, ifname);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check if an interface is up
|
* check if an interface is up
|
||||||
*/
|
*/
|
||||||
@@ -157,6 +165,9 @@ struct addr_map_entry_t {
|
|||||||
/** The IP address */
|
/** The IP address */
|
||||||
host_t *ip;
|
host_t *ip;
|
||||||
|
|
||||||
|
/** The address entry for this IP address */
|
||||||
|
addr_entry_t *addr;
|
||||||
|
|
||||||
/** The interface this address is installed on */
|
/** The interface this address is installed on */
|
||||||
iface_entry_t *iface;
|
iface_entry_t *iface;
|
||||||
};
|
};
|
||||||
@@ -200,6 +211,15 @@ 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);
|
return iface_entry_up(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 local interface
|
||||||
|
*/
|
||||||
|
static bool addr_map_entry_match(addr_map_entry_t *a, addr_map_entry_t *b)
|
||||||
|
{
|
||||||
|
return a->ip->ip_equals(a->ip, b->ip);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct route_entry_t route_entry_t;
|
typedef struct route_entry_t route_entry_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -337,6 +357,11 @@ struct private_kernel_netlink_net_t {
|
|||||||
*/
|
*/
|
||||||
hashtable_t *addrs;
|
hashtable_t *addrs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map for virtual IP addresses to iface_entry_t objects (addr_map_entry_t)
|
||||||
|
*/
|
||||||
|
hashtable_t *vips;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* netlink rt socket (routing)
|
* netlink rt socket (routing)
|
||||||
*/
|
*/
|
||||||
@@ -517,78 +542,74 @@ static void queue_route_reinstall(private_kernel_netlink_net_t *this,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the refcount of a virtual ip
|
* check if the given IP is known as virtual IP and currently installed
|
||||||
|
*
|
||||||
|
* this function will also return TRUE if the virtual IP entry disappeared.
|
||||||
|
* in that case the returned entry will be NULL.
|
||||||
|
*
|
||||||
|
* this->mutex must be locked when calling this function
|
||||||
*/
|
*/
|
||||||
static int get_vip_refcount(private_kernel_netlink_net_t *this, host_t* ip)
|
static bool is_vip_installed_or_gone(private_kernel_netlink_net_t *this,
|
||||||
|
host_t *ip, addr_map_entry_t **entry)
|
||||||
{
|
{
|
||||||
enumerator_t *ifaces, *addrs;
|
addr_map_entry_t lookup = {
|
||||||
iface_entry_t *iface;
|
.ip = ip,
|
||||||
addr_entry_t *addr;
|
};
|
||||||
int refcount = 0;
|
|
||||||
|
|
||||||
ifaces = this->ifaces->create_enumerator(this->ifaces);
|
*entry = this->vips->get_match(this->vips, &lookup,
|
||||||
while (ifaces->enumerate(ifaces, (void**)&iface))
|
(void*)addr_map_entry_match);
|
||||||
{
|
if (*entry == NULL)
|
||||||
addrs = iface->addrs->create_enumerator(iface->addrs);
|
{ /* the virtual IP disappeared */
|
||||||
while (addrs->enumerate(addrs, (void**)&addr))
|
return TRUE;
|
||||||
{
|
|
||||||
if (addr->virtual && (iface->flags & IFF_UP) &&
|
|
||||||
ip->ip_equals(ip, addr->ip))
|
|
||||||
{
|
|
||||||
refcount = addr->refcount;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addrs->destroy(addrs);
|
|
||||||
if (refcount)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ifaces->destroy(ifaces);
|
return (*entry)->addr->installed;
|
||||||
|
}
|
||||||
|
|
||||||
return refcount;
|
/**
|
||||||
|
* check if the given IP is known as virtual IP
|
||||||
|
*
|
||||||
|
* this->mutex must be locked when calling this function
|
||||||
|
*/
|
||||||
|
static bool is_known_vip(private_kernel_netlink_net_t *this, host_t *ip)
|
||||||
|
{
|
||||||
|
addr_map_entry_t lookup = {
|
||||||
|
.ip = ip,
|
||||||
|
};
|
||||||
|
|
||||||
|
return this->vips->get_match(this->vips, &lookup,
|
||||||
|
(void*)addr_map_entry_match) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an address map entry
|
* Add an address map entry
|
||||||
*/
|
*/
|
||||||
static void addr_map_entry_add(private_kernel_netlink_net_t *this,
|
static void addr_map_entry_add(hashtable_t *map, addr_entry_t *addr,
|
||||||
addr_entry_t *addr, iface_entry_t *iface)
|
iface_entry_t *iface)
|
||||||
{
|
{
|
||||||
addr_map_entry_t *entry;
|
addr_map_entry_t *entry;
|
||||||
|
|
||||||
if (addr->virtual)
|
|
||||||
{ /* don't map virtual IPs */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
INIT(entry,
|
INIT(entry,
|
||||||
.ip = addr->ip,
|
.ip = addr->ip,
|
||||||
|
.addr = addr,
|
||||||
.iface = iface,
|
.iface = iface,
|
||||||
);
|
);
|
||||||
entry = this->addrs->put(this->addrs, entry, entry);
|
entry = map->put(map, entry, entry);
|
||||||
free(entry);
|
free(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an address map entry (the argument order is a bit strange because
|
* Remove an address map entry
|
||||||
* it is also used with linked_list_t.invoke_function)
|
|
||||||
*/
|
*/
|
||||||
static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
|
static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr,
|
||||||
private_kernel_netlink_net_t *this)
|
iface_entry_t *iface)
|
||||||
{
|
{
|
||||||
addr_map_entry_t *entry, lookup = {
|
addr_map_entry_t *entry, lookup = {
|
||||||
.ip = addr->ip,
|
.ip = addr->ip,
|
||||||
|
.addr = addr,
|
||||||
.iface = iface,
|
.iface = iface,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (addr->virtual)
|
entry = map->remove(map, &lookup);
|
||||||
{ /* 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);
|
free(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,26 +622,22 @@ static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface,
|
|||||||
static host_t *get_interface_address(private_kernel_netlink_net_t *this,
|
static host_t *get_interface_address(private_kernel_netlink_net_t *this,
|
||||||
int ifindex, int family, host_t *candidate)
|
int ifindex, int family, host_t *candidate)
|
||||||
{
|
{
|
||||||
enumerator_t *ifaces, *addrs;
|
|
||||||
iface_entry_t *iface;
|
iface_entry_t *iface;
|
||||||
|
enumerator_t *addrs;
|
||||||
addr_entry_t *addr;
|
addr_entry_t *addr;
|
||||||
host_t *ip = NULL;
|
host_t *ip = NULL;
|
||||||
|
|
||||||
this->mutex->lock(this->mutex);
|
this->mutex->lock(this->mutex);
|
||||||
ifaces = this->ifaces->create_enumerator(this->ifaces);
|
if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index,
|
||||||
while (ifaces->enumerate(ifaces, &iface))
|
(void**)&iface, &ifindex) == SUCCESS)
|
||||||
{
|
{
|
||||||
if (iface->ifindex == ifindex)
|
if (iface->usable)
|
||||||
{
|
{ /* only use interfaces not excluded by config */
|
||||||
if (!iface->usable)
|
|
||||||
{ /* ignore interfaces excluded by config */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
addrs = iface->addrs->create_enumerator(iface->addrs);
|
addrs = iface->addrs->create_enumerator(iface->addrs);
|
||||||
while (addrs->enumerate(addrs, &addr))
|
while (addrs->enumerate(addrs, &addr))
|
||||||
{
|
{
|
||||||
if (addr->virtual)
|
if (addr->refcount)
|
||||||
{
|
{ /* ignore virtual IP addresses */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (addr->ip->get_family(addr->ip) == family)
|
if (addr->ip->get_family(addr->ip) == family)
|
||||||
@@ -639,10 +656,8 @@ static host_t *get_interface_address(private_kernel_netlink_net_t *this,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
addrs->destroy(addrs);
|
addrs->destroy(addrs);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
if (ip)
|
if (ip)
|
||||||
{
|
{
|
||||||
ip = ip->clone(ip);
|
ip = ip->clone(ip);
|
||||||
@@ -689,6 +704,8 @@ static void fire_roam_event(private_kernel_netlink_net_t *this, bool address)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* check if an interface with a given index is up and usable
|
* check if an interface with a given index is up and usable
|
||||||
|
*
|
||||||
|
* this->mutex must be locked when calling this function
|
||||||
*/
|
*/
|
||||||
static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this,
|
static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this,
|
||||||
int index)
|
int index)
|
||||||
@@ -703,6 +720,23 @@ static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unregister the current addr_entry_t from the hashtable it is stored in
|
||||||
|
*
|
||||||
|
* this->mutex must be locked when calling this function
|
||||||
|
*/
|
||||||
|
static void unregister_addr_entry(addr_entry_t *addr, iface_entry_t *iface,
|
||||||
|
private_kernel_netlink_net_t *this)
|
||||||
|
{
|
||||||
|
if (addr->refcount)
|
||||||
|
{
|
||||||
|
addr_map_entry_remove(this->vips, addr, iface);
|
||||||
|
this->condvar->broadcast(this->condvar);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addr_map_entry_remove(this->addrs, addr, iface);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* process RTM_NEWLINK/RTM_DELLINK from kernel
|
* process RTM_NEWLINK/RTM_DELLINK from kernel
|
||||||
*/
|
*/
|
||||||
@@ -737,24 +771,16 @@ static void process_link(private_kernel_netlink_net_t *this,
|
|||||||
{
|
{
|
||||||
case RTM_NEWLINK:
|
case RTM_NEWLINK:
|
||||||
{
|
{
|
||||||
enumerator = this->ifaces->create_enumerator(this->ifaces);
|
if (this->ifaces->find_first(this->ifaces,
|
||||||
while (enumerator->enumerate(enumerator, ¤t))
|
(void*)iface_entry_by_index, (void**)&entry,
|
||||||
|
&msg->ifi_index) != SUCCESS)
|
||||||
{
|
{
|
||||||
if (current->ifindex == msg->ifi_index)
|
INIT(entry,
|
||||||
{
|
.ifindex = msg->ifi_index,
|
||||||
entry = current;
|
.addrs = linked_list_create(),
|
||||||
break;
|
.usable = hydra->kernel_interface->is_interface_usable(
|
||||||
}
|
hydra->kernel_interface, name),
|
||||||
}
|
);
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
if (!entry)
|
|
||||||
{
|
|
||||||
entry = malloc_thing(iface_entry_t);
|
|
||||||
entry->ifindex = msg->ifi_index;
|
|
||||||
entry->flags = 0;
|
|
||||||
entry->usable = hydra->kernel_interface->is_interface_usable(
|
|
||||||
hydra->kernel_interface, name);
|
|
||||||
entry->addrs = linked_list_create();
|
|
||||||
this->ifaces->insert_last(this->ifaces, entry);
|
this->ifaces->insert_last(this->ifaces, entry);
|
||||||
}
|
}
|
||||||
strncpy(entry->ifname, name, IFNAMSIZ);
|
strncpy(entry->ifname, name, IFNAMSIZ);
|
||||||
@@ -787,9 +813,11 @@ static void process_link(private_kernel_netlink_net_t *this,
|
|||||||
update = TRUE;
|
update = TRUE;
|
||||||
DBG1(DBG_KNL, "interface %s deleted", current->ifname);
|
DBG1(DBG_KNL, "interface %s deleted", current->ifname);
|
||||||
}
|
}
|
||||||
|
/* TODO: move virtual IPs installed on this interface to
|
||||||
|
* another interface? */
|
||||||
this->ifaces->remove_at(this->ifaces, enumerator);
|
this->ifaces->remove_at(this->ifaces, enumerator);
|
||||||
current->addrs->invoke_function(current->addrs,
|
current->addrs->invoke_function(current->addrs,
|
||||||
(void*)addr_map_entry_remove, current, this);
|
(void*)unregister_addr_entry, current, this);
|
||||||
iface_entry_destroy(current);
|
iface_entry_destroy(current);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -805,7 +833,6 @@ static void process_link(private_kernel_netlink_net_t *this,
|
|||||||
queue_route_reinstall(this, strdup(name));
|
queue_route_reinstall(this, strdup(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send an update to all IKE_SAs */
|
|
||||||
if (update && event)
|
if (update && event)
|
||||||
{
|
{
|
||||||
fire_roam_event(this, TRUE);
|
fire_roam_event(this, TRUE);
|
||||||
@@ -822,9 +849,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
|
|||||||
struct rtattr *rta = IFA_RTA(msg);
|
struct rtattr *rta = IFA_RTA(msg);
|
||||||
size_t rtasize = IFA_PAYLOAD (hdr);
|
size_t rtasize = IFA_PAYLOAD (hdr);
|
||||||
host_t *host = NULL;
|
host_t *host = NULL;
|
||||||
enumerator_t *ifaces, *addrs;
|
|
||||||
iface_entry_t *iface;
|
iface_entry_t *iface;
|
||||||
addr_entry_t *addr;
|
|
||||||
chunk_t local = chunk_empty, address = chunk_empty;
|
chunk_t local = chunk_empty, address = chunk_empty;
|
||||||
char *route_ifname = NULL;
|
char *route_ifname = NULL;
|
||||||
bool update = FALSE, found = FALSE, changed = FALSE;
|
bool update = FALSE, found = FALSE, changed = FALSE;
|
||||||
@@ -863,70 +888,81 @@ static void process_addr(private_kernel_netlink_net_t *this,
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->mutex->lock(this->mutex);
|
this->mutex->lock(this->mutex);
|
||||||
ifaces = this->ifaces->create_enumerator(this->ifaces);
|
if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index,
|
||||||
while (ifaces->enumerate(ifaces, &iface))
|
(void**)&iface, &msg->ifa_index) == SUCCESS)
|
||||||
{
|
{
|
||||||
if (iface->ifindex == msg->ifa_index)
|
addr_map_entry_t *entry, lookup = {
|
||||||
{
|
.ip = host,
|
||||||
addrs = iface->addrs->create_enumerator(iface->addrs);
|
.iface = iface,
|
||||||
while (addrs->enumerate(addrs, &addr))
|
};
|
||||||
{
|
addr_entry_t *addr;
|
||||||
if (host->ip_equals(host, addr->ip))
|
|
||||||
{
|
|
||||||
found = TRUE;
|
|
||||||
if (hdr->nlmsg_type == RTM_DELADDR)
|
|
||||||
{
|
|
||||||
iface->addrs->remove_at(iface->addrs, addrs);
|
|
||||||
if (!addr->virtual && iface->usable)
|
|
||||||
{
|
|
||||||
changed = TRUE;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
addr->refcount = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addrs->destroy(addrs);
|
|
||||||
|
|
||||||
|
entry = this->vips->get(this->vips, &lookup);
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
if (hdr->nlmsg_type == RTM_NEWADDR)
|
||||||
|
{ /* mark as installed and signal waiting threads */
|
||||||
|
entry->addr->installed = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* the address was already marked as uninstalled */
|
||||||
|
addr = entry->addr;
|
||||||
|
iface->addrs->remove(iface->addrs, addr, NULL);
|
||||||
|
addr_map_entry_remove(this->vips, addr, iface);
|
||||||
|
addr_entry_destroy(addr);
|
||||||
|
}
|
||||||
|
/* no roam events etc. for virtual IPs */
|
||||||
|
this->condvar->broadcast(this->condvar);
|
||||||
|
this->mutex->unlock(this->mutex);
|
||||||
|
host->destroy(host);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry = this->addrs->get(this->addrs, &lookup);
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
if (hdr->nlmsg_type == RTM_DELADDR)
|
||||||
|
{
|
||||||
|
found = TRUE;
|
||||||
|
addr = entry->addr;
|
||||||
|
iface->addrs->remove(iface->addrs, addr, NULL);
|
||||||
|
if (iface->usable)
|
||||||
|
{
|
||||||
|
changed = TRUE;
|
||||||
|
DBG1(DBG_KNL, "%H disappeared from %s", host,
|
||||||
|
iface->ifname);
|
||||||
|
}
|
||||||
|
addr_map_entry_remove(this->addrs, addr, iface);
|
||||||
|
addr_entry_destroy(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (hdr->nlmsg_type == RTM_NEWADDR)
|
if (hdr->nlmsg_type == RTM_NEWADDR)
|
||||||
{
|
{
|
||||||
if (!found)
|
found = TRUE;
|
||||||
|
changed = TRUE;
|
||||||
|
route_ifname = strdup(iface->ifname);
|
||||||
|
INIT(addr,
|
||||||
|
.ip = host->clone(host),
|
||||||
|
.scope = msg->ifa_scope,
|
||||||
|
);
|
||||||
|
iface->addrs->insert_last(iface->addrs, addr);
|
||||||
|
addr_map_entry_add(this->addrs, addr, iface);
|
||||||
|
if (event && iface->usable)
|
||||||
{
|
{
|
||||||
found = TRUE;
|
DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname);
|
||||||
changed = TRUE;
|
|
||||||
route_ifname = strdup(iface->ifname);
|
|
||||||
addr = malloc_thing(addr_entry_t);
|
|
||||||
addr->ip = host->clone(host);
|
|
||||||
addr->virtual = FALSE;
|
|
||||||
addr->refcount = 1;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found && (iface->flags & IFF_UP))
|
}
|
||||||
{
|
if (found && (iface->flags & IFF_UP))
|
||||||
update = TRUE;
|
{
|
||||||
}
|
update = TRUE;
|
||||||
if (!iface->usable)
|
}
|
||||||
{ /* ignore events for interfaces excluded by config */
|
if (!iface->usable)
|
||||||
update = changed = FALSE;
|
{ /* ignore events for interfaces excluded by config */
|
||||||
}
|
update = changed = FALSE;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
this->mutex->unlock(this->mutex);
|
this->mutex->unlock(this->mutex);
|
||||||
|
|
||||||
if (update && event && route_ifname)
|
if (update && event && route_ifname)
|
||||||
@@ -999,7 +1035,7 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h
|
|||||||
}
|
}
|
||||||
if (host)
|
if (host)
|
||||||
{
|
{
|
||||||
if (!get_vip_refcount(this, host))
|
if (!is_known_vip(this, host))
|
||||||
{ /* ignore routes added for virtual IPs */
|
{ /* ignore routes added for virtual IPs */
|
||||||
fire_roam_event(this, FALSE);
|
fire_roam_event(this, FALSE);
|
||||||
}
|
}
|
||||||
@@ -1055,12 +1091,10 @@ static job_requeue_t receive_events(private_kernel_netlink_net_t *this)
|
|||||||
case RTM_NEWADDR:
|
case RTM_NEWADDR:
|
||||||
case RTM_DELADDR:
|
case RTM_DELADDR:
|
||||||
process_addr(this, hdr, TRUE);
|
process_addr(this, hdr, TRUE);
|
||||||
this->condvar->broadcast(this->condvar);
|
|
||||||
break;
|
break;
|
||||||
case RTM_NEWLINK:
|
case RTM_NEWLINK:
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
process_link(this, hdr, TRUE);
|
process_link(this, hdr, TRUE);
|
||||||
this->condvar->broadcast(this->condvar);
|
|
||||||
break;
|
break;
|
||||||
case RTM_NEWROUTE:
|
case RTM_NEWROUTE:
|
||||||
case RTM_DELROUTE:
|
case RTM_DELROUTE:
|
||||||
@@ -1099,7 +1133,7 @@ static void address_enumerator_destroy(address_enumerator_t *data)
|
|||||||
static bool filter_addresses(address_enumerator_t *data,
|
static bool filter_addresses(address_enumerator_t *data,
|
||||||
addr_entry_t** in, host_t** out)
|
addr_entry_t** in, host_t** out)
|
||||||
{
|
{
|
||||||
if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->virtual)
|
if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->refcount)
|
||||||
{ /* skip virtual interfaces added by us */
|
{ /* skip virtual interfaces added by us */
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -1201,23 +1235,17 @@ METHOD(kernel_net_t, get_interface_name, bool,
|
|||||||
*/
|
*/
|
||||||
static int get_interface_index(private_kernel_netlink_net_t *this, char* name)
|
static int get_interface_index(private_kernel_netlink_net_t *this, char* name)
|
||||||
{
|
{
|
||||||
enumerator_t *ifaces;
|
|
||||||
iface_entry_t *iface;
|
iface_entry_t *iface;
|
||||||
int ifindex = 0;
|
int ifindex = 0;
|
||||||
|
|
||||||
DBG2(DBG_KNL, "getting iface index for %s", name);
|
DBG2(DBG_KNL, "getting iface index for %s", name);
|
||||||
|
|
||||||
this->mutex->lock(this->mutex);
|
this->mutex->lock(this->mutex);
|
||||||
ifaces = this->ifaces->create_enumerator(this->ifaces);
|
if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name,
|
||||||
while (ifaces->enumerate(ifaces, &iface))
|
(void**)&iface, name) == SUCCESS)
|
||||||
{
|
{
|
||||||
if (streq(name, iface->ifname))
|
ifindex = iface->ifindex;
|
||||||
{
|
|
||||||
ifindex = iface->ifindex;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
this->mutex->unlock(this->mutex);
|
this->mutex->unlock(this->mutex);
|
||||||
|
|
||||||
if (ifindex == 0)
|
if (ifindex == 0)
|
||||||
@@ -1437,7 +1465,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
|
|||||||
{ /* verify source address, if any */
|
{ /* verify source address, if any */
|
||||||
host_t *src = host_create_from_chunk(msg->rtm_family,
|
host_t *src = host_create_from_chunk(msg->rtm_family,
|
||||||
route->src, 0);
|
route->src, 0);
|
||||||
if (src && get_vip_refcount(this, src))
|
if (src && is_known_vip(this, src))
|
||||||
{ /* ignore routes installed by us */
|
{ /* ignore routes installed by us */
|
||||||
src->destroy(src);
|
src->destroy(src);
|
||||||
continue;
|
continue;
|
||||||
@@ -1618,10 +1646,10 @@ static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type
|
|||||||
METHOD(kernel_net_t, add_ip, status_t,
|
METHOD(kernel_net_t, add_ip, status_t,
|
||||||
private_kernel_netlink_net_t *this, host_t *virtual_ip, host_t *iface_ip)
|
private_kernel_netlink_net_t *this, host_t *virtual_ip, host_t *iface_ip)
|
||||||
{
|
{
|
||||||
|
addr_map_entry_t *entry, lookup = {
|
||||||
|
.ip = virtual_ip,
|
||||||
|
};
|
||||||
iface_entry_t *iface;
|
iface_entry_t *iface;
|
||||||
addr_entry_t *addr;
|
|
||||||
enumerator_t *addrs, *ifaces;
|
|
||||||
int ifindex;
|
|
||||||
|
|
||||||
if (!this->install_virtual_ip)
|
if (!this->install_virtual_ip)
|
||||||
{ /* disabled by config */
|
{ /* disabled by config */
|
||||||
@@ -1629,76 +1657,95 @@ METHOD(kernel_net_t, add_ip, status_t,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DBG2(DBG_KNL, "adding virtual IP %H", virtual_ip);
|
DBG2(DBG_KNL, "adding virtual IP %H", virtual_ip);
|
||||||
|
|
||||||
this->mutex->lock(this->mutex);
|
this->mutex->lock(this->mutex);
|
||||||
ifaces = this->ifaces->create_enumerator(this->ifaces);
|
/* the virtual IP might actually be installed as regular IP, in which case
|
||||||
while (ifaces->enumerate(ifaces, &iface))
|
* we don't track it as virtual IP */
|
||||||
{
|
entry = this->addrs->get_match(this->addrs, &lookup,
|
||||||
bool iface_found = FALSE;
|
(void*)addr_map_entry_match);
|
||||||
|
if (!entry)
|
||||||
addrs = iface->addrs->create_enumerator(iface->addrs);
|
{ /* otherwise it might already be installed as virtual IP */
|
||||||
while (addrs->enumerate(addrs, &addr))
|
entry = this->vips->get_match(this->vips, &lookup,
|
||||||
{
|
(void*)addr_map_entry_match);
|
||||||
if (iface_ip->ip_equals(iface_ip, addr->ip))
|
if (entry)
|
||||||
|
{ /* the vip we found can be in one of three states: 1) installed and
|
||||||
|
* ready, 2) just added by another thread, but not yet confirmed to
|
||||||
|
* be installed by the kernel, 3) just deleted, but not yet gone.
|
||||||
|
* Then while we wait below, several things could happen (as we
|
||||||
|
* release the mutex). For instance, the interface could disappear.
|
||||||
|
* Or the IP is finally deleted, and it reappears on a different
|
||||||
|
* interface. All these cases are handled by the call below. */
|
||||||
|
while (!is_vip_installed_or_gone(this, virtual_ip, &entry))
|
||||||
{
|
{
|
||||||
iface_found = TRUE;
|
this->condvar->wait(this->condvar, this->mutex);
|
||||||
}
|
}
|
||||||
else if (virtual_ip->ip_equals(virtual_ip, addr->ip))
|
if (entry)
|
||||||
{
|
{
|
||||||
addr->refcount++;
|
entry->addr->refcount++;
|
||||||
DBG2(DBG_KNL, "virtual IP %H already installed on %s",
|
|
||||||
virtual_ip, iface->ifname);
|
|
||||||
addrs->destroy(addrs);
|
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
this->mutex->unlock(this->mutex);
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addrs->destroy(addrs);
|
|
||||||
|
|
||||||
if (iface_found)
|
|
||||||
{
|
|
||||||
ifindex = iface->ifindex;
|
|
||||||
addr = malloc_thing(addr_entry_t);
|
|
||||||
addr->ip = virtual_ip->clone(virtual_ip);
|
|
||||||
addr->refcount = 0;
|
|
||||||
addr->virtual = TRUE;
|
|
||||||
addr->scope = RT_SCOPE_UNIVERSE;
|
|
||||||
iface->addrs->insert_last(iface->addrs, addr);
|
|
||||||
|
|
||||||
if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL,
|
|
||||||
ifindex, virtual_ip) == SUCCESS)
|
|
||||||
{
|
|
||||||
while (get_vip_refcount(this, virtual_ip) == 0)
|
|
||||||
{ /* wait until address appears */
|
|
||||||
this->condvar->wait(this->condvar, this->mutex);
|
|
||||||
}
|
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
this->mutex->unlock(this->mutex);
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
this->mutex->unlock(this->mutex);
|
|
||||||
DBG1(DBG_KNL, "adding virtual IP %H failed", virtual_ip);
|
|
||||||
return FAILED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ifaces->destroy(ifaces);
|
if (entry)
|
||||||
this->mutex->unlock(this->mutex);
|
{
|
||||||
|
DBG2(DBG_KNL, "virtual IP %H is already installed on %s", virtual_ip,
|
||||||
|
entry->iface->ifname);
|
||||||
|
this->mutex->unlock(this->mutex);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
/* try to find the target interface */
|
||||||
|
lookup.ip = iface_ip;
|
||||||
|
entry = this->addrs->get_match(this->addrs, &lookup,
|
||||||
|
(void*)addr_map_entry_match);
|
||||||
|
if (!entry)
|
||||||
|
{ /* if we don't find the requested interface we just use the first */
|
||||||
|
if (this->ifaces->get_first(this->ifaces, (void**)&iface) != SUCCESS)
|
||||||
|
{
|
||||||
|
iface = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iface = entry->iface;
|
||||||
|
}
|
||||||
|
if (iface)
|
||||||
|
{
|
||||||
|
addr_entry_t *addr;
|
||||||
|
|
||||||
DBG1(DBG_KNL, "interface address %H not found, unable to install"
|
INIT(addr,
|
||||||
"virtual IP %H", iface_ip, virtual_ip);
|
.ip = virtual_ip->clone(virtual_ip),
|
||||||
|
.refcount = 1,
|
||||||
|
.scope = RT_SCOPE_UNIVERSE,
|
||||||
|
);
|
||||||
|
iface->addrs->insert_last(iface->addrs, addr);
|
||||||
|
addr_map_entry_add(this->vips, addr, iface);
|
||||||
|
if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL,
|
||||||
|
iface->ifindex, virtual_ip) == SUCCESS)
|
||||||
|
{
|
||||||
|
while (!is_vip_installed_or_gone(this, virtual_ip, &entry))
|
||||||
|
{ /* wait until address appears */
|
||||||
|
this->condvar->wait(this->condvar, this->mutex);
|
||||||
|
}
|
||||||
|
if (entry)
|
||||||
|
{ /* we fail if the interface got deleted in the meantime */
|
||||||
|
this->mutex->unlock(this->mutex);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->mutex->unlock(this->mutex);
|
||||||
|
DBG1(DBG_KNL, "adding virtual IP %H failed", virtual_ip);
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
this->mutex->unlock(this->mutex);
|
||||||
|
DBG1(DBG_KNL, "no interface available, unable to install virtual IP %H",
|
||||||
|
virtual_ip);
|
||||||
return FAILED;
|
return FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(kernel_net_t, del_ip, status_t,
|
METHOD(kernel_net_t, del_ip, status_t,
|
||||||
private_kernel_netlink_net_t *this, host_t *virtual_ip)
|
private_kernel_netlink_net_t *this, host_t *virtual_ip)
|
||||||
{
|
{
|
||||||
iface_entry_t *iface;
|
addr_map_entry_t *entry, lookup = {
|
||||||
addr_entry_t *addr;
|
.ip = virtual_ip,
|
||||||
enumerator_t *addrs, *ifaces;
|
};
|
||||||
status_t status;
|
|
||||||
int ifindex;
|
|
||||||
|
|
||||||
if (!this->install_virtual_ip)
|
if (!this->install_virtual_ip)
|
||||||
{ /* disabled by config */
|
{ /* disabled by config */
|
||||||
@@ -1708,50 +1755,50 @@ METHOD(kernel_net_t, del_ip, status_t,
|
|||||||
DBG2(DBG_KNL, "deleting virtual IP %H", virtual_ip);
|
DBG2(DBG_KNL, "deleting virtual IP %H", virtual_ip);
|
||||||
|
|
||||||
this->mutex->lock(this->mutex);
|
this->mutex->lock(this->mutex);
|
||||||
ifaces = this->ifaces->create_enumerator(this->ifaces);
|
entry = this->vips->get_match(this->vips, &lookup,
|
||||||
while (ifaces->enumerate(ifaces, &iface))
|
(void*)addr_map_entry_match);
|
||||||
{
|
if (!entry)
|
||||||
addrs = iface->addrs->create_enumerator(iface->addrs);
|
{ /* we didn't install this IP as virtual IP */
|
||||||
while (addrs->enumerate(addrs, &addr))
|
entry = this->addrs->get_match(this->addrs, &lookup,
|
||||||
|
(void*)addr_map_entry_match);
|
||||||
|
if (entry)
|
||||||
{
|
{
|
||||||
if (virtual_ip->ip_equals(virtual_ip, addr->ip))
|
DBG2(DBG_KNL, "not deleting existing IP %H on %s", virtual_ip,
|
||||||
|
entry->iface->ifname);
|
||||||
|
this->mutex->unlock(this->mutex);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip);
|
||||||
|
this->mutex->unlock(this->mutex);
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
if (entry->addr->refcount == 1)
|
||||||
|
{
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
/* we set this flag so that threads calling add_ip will block and wait
|
||||||
|
* until the entry is gone, also so we can wait below */
|
||||||
|
entry->addr->installed = FALSE;
|
||||||
|
status = manage_ipaddr(this, RTM_DELADDR, 0, entry->iface->ifindex,
|
||||||
|
virtual_ip);
|
||||||
|
if (status == SUCCESS)
|
||||||
|
{ /* wait until the address is really gone */
|
||||||
|
while (is_known_vip(this, virtual_ip))
|
||||||
{
|
{
|
||||||
ifindex = iface->ifindex;
|
this->condvar->wait(this->condvar, this->mutex);
|
||||||
if (addr->refcount == 1)
|
|
||||||
{
|
|
||||||
status = manage_ipaddr(this, RTM_DELADDR, 0,
|
|
||||||
ifindex, virtual_ip);
|
|
||||||
if (status == SUCCESS)
|
|
||||||
{ /* wait until the address is really gone */
|
|
||||||
while (get_vip_refcount(this, virtual_ip) > 0)
|
|
||||||
{
|
|
||||||
this->condvar->wait(this->condvar, this->mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addrs->destroy(addrs);
|
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
this->mutex->unlock(this->mutex);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
addr->refcount--;
|
|
||||||
}
|
|
||||||
DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting",
|
|
||||||
virtual_ip);
|
|
||||||
addrs->destroy(addrs);
|
|
||||||
ifaces->destroy(ifaces);
|
|
||||||
this->mutex->unlock(this->mutex);
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addrs->destroy(addrs);
|
this->mutex->unlock(this->mutex);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
ifaces->destroy(ifaces);
|
else
|
||||||
|
{
|
||||||
|
entry->addr->refcount--;
|
||||||
|
}
|
||||||
|
DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting",
|
||||||
|
virtual_ip);
|
||||||
this->mutex->unlock(this->mutex);
|
this->mutex->unlock(this->mutex);
|
||||||
|
return SUCCESS;
|
||||||
DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip);
|
|
||||||
return FAILED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2032,12 +2079,28 @@ static void check_kernel_features(private_kernel_netlink_net_t *this)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy an address to iface map
|
||||||
|
*/
|
||||||
|
static void addr_map_destroy(hashtable_t *map)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
addr_map_entry_t *addr;
|
||||||
|
|
||||||
|
enumerator = map->create_enumerator(map);
|
||||||
|
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
|
||||||
|
{
|
||||||
|
free(addr);
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
map->destroy(map);
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(kernel_net_t, destroy, void,
|
METHOD(kernel_net_t, destroy, void,
|
||||||
private_kernel_netlink_net_t *this)
|
private_kernel_netlink_net_t *this)
|
||||||
{
|
{
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
route_entry_t *route;
|
route_entry_t *route;
|
||||||
addr_map_entry_t *addr;
|
|
||||||
|
|
||||||
if (this->routing_table)
|
if (this->routing_table)
|
||||||
{
|
{
|
||||||
@@ -2065,13 +2128,8 @@ METHOD(kernel_net_t, destroy, void,
|
|||||||
this->net_changes->destroy(this->net_changes);
|
this->net_changes->destroy(this->net_changes);
|
||||||
this->net_changes_lock->destroy(this->net_changes_lock);
|
this->net_changes_lock->destroy(this->net_changes_lock);
|
||||||
|
|
||||||
enumerator = this->addrs->create_enumerator(this->addrs);
|
addr_map_destroy(this->addrs);
|
||||||
while (enumerator->enumerate(enumerator, NULL, (void**)&addr))
|
addr_map_destroy(this->vips);
|
||||||
{
|
|
||||||
free(addr);
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
this->addrs->destroy(this->addrs);
|
|
||||||
|
|
||||||
this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
|
this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy);
|
||||||
this->rt_exclude->destroy(this->rt_exclude);
|
this->rt_exclude->destroy(this->rt_exclude);
|
||||||
@@ -2114,6 +2172,8 @@ kernel_netlink_net_t *kernel_netlink_net_create()
|
|||||||
.addrs = hashtable_create(
|
.addrs = hashtable_create(
|
||||||
(hashtable_hash_t)addr_map_entry_hash,
|
(hashtable_hash_t)addr_map_entry_hash,
|
||||||
(hashtable_equals_t)addr_map_entry_equals, 16),
|
(hashtable_equals_t)addr_map_entry_equals, 16),
|
||||||
|
.vips = 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),
|
.net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||||
.ifaces = linked_list_create(),
|
.ifaces = linked_list_create(),
|
||||||
.mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
|
.mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
|
||||||
|
|||||||
Reference in New Issue
Block a user