farp plugin sends ARP responses for any tunneled address, not only virtual IPs

This commit is contained in:
Martin Willi
2012-03-06 16:06:33 +01:00
parent 21796bac9a
commit 45f20f8a79
3 changed files with 91 additions and 81 deletions
+78 -69
View File
@@ -15,7 +15,7 @@
#include "farp_listener.h" #include "farp_listener.h"
#include <utils/hashtable.h> #include <utils/linked_list.h>
#include <threading/rwlock.h> #include <threading/rwlock.h>
typedef struct private_farp_listener_t private_farp_listener_t; typedef struct private_farp_listener_t private_farp_listener_t;
@@ -31,9 +31,9 @@ struct private_farp_listener_t {
farp_listener_t public; farp_listener_t public;
/** /**
* Hashtable with active virtual IPs * List with entry_t
*/ */
hashtable_t *ips; linked_list_t *entries;
/** /**
* RWlock for IP list * RWlock for IP list
@@ -42,88 +42,99 @@ struct private_farp_listener_t {
}; };
/** /**
* Hashtable hash function * Traffic selector cache entry
*/ */
static u_int hash(host_t *key) typedef struct {
{ /** list of local selectors */
return chunk_hash(key->get_address(key)); linked_list_t *local;
} /** list of remote selectors */
linked_list_t *remote;
/** reqid of CHILD_SA */
u_int32_t reqid;
} entry_t;
/** METHOD(listener_t, child_updown, bool,
* Hashtable equals function private_farp_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
*/ bool up)
static bool equals(host_t *a, host_t *b)
{ {
return a->ip_equals(a, b); enumerator_t *enumerator;
} entry_t *entry;
METHOD(listener_t, ike_updown, bool, if (up)
private_farp_listener_t *this, ike_sa_t *ike_sa, bool up)
{
if (!up)
{ {
host_t *ip; INIT(entry,
.local = child_sa->get_traffic_selectors(child_sa, TRUE),
.remote = child_sa->get_traffic_selectors(child_sa, FALSE),
.reqid = child_sa->get_reqid(child_sa),
);
entry->local = entry->local->clone_offset(entry->local,
offsetof(traffic_selector_t, clone));
entry->remote = entry->remote->clone_offset(entry->remote,
offsetof(traffic_selector_t, clone));
ip = ike_sa->get_virtual_ip(ike_sa, FALSE); this->lock->write_lock(this->lock);
if (ip) this->entries->insert_last(this->entries, entry);
this->lock->unlock(this->lock);
}
else
{
this->lock->write_lock(this->lock);
enumerator = this->entries->create_enumerator(this->entries);
while (enumerator->enumerate(enumerator, &entry))
{ {
this->lock->write_lock(this->lock); if (entry->reqid == child_sa->get_reqid(child_sa))
ip = this->ips->remove(this->ips, ip); {
this->lock->unlock(this->lock); this->entries->remove_at(this->entries, enumerator);
DESTROY_IF(ip); entry->local->destroy_offset(entry->local,
offsetof(traffic_selector_t, destroy));
entry->remote->destroy_offset(entry->remote,
offsetof(traffic_selector_t, destroy));
free(entry);
}
} }
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
} }
return TRUE; return TRUE;
} }
METHOD(listener_t, message_hook, bool, METHOD(farp_listener_t, has_tunnel, bool,
private_farp_listener_t *this, ike_sa_t *ike_sa, private_farp_listener_t *this, host_t *local, host_t *remote)
message_t *message, bool incoming)
{ {
if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && enumerator_t *entries, *locals, *remotes;
message->get_exchange_type(message) == IKE_AUTH && traffic_selector_t *ts;
!message->get_request(message)) bool found = FALSE;
{ entry_t *entry;
host_t *ip;
ip = ike_sa->get_virtual_ip(ike_sa, FALSE);
if (ip)
{
ip = ip->clone(ip);
this->lock->write_lock(this->lock);
ip = this->ips->put(this->ips, ip, ip);
this->lock->unlock(this->lock);
DESTROY_IF(ip);
}
}
return TRUE;
}
METHOD(farp_listener_t, is_active, bool,
private_farp_listener_t *this, host_t *ip)
{
bool active;
this->lock->read_lock(this->lock); this->lock->read_lock(this->lock);
active = this->ips->get(this->ips, ip) != NULL; entries = this->entries->create_enumerator(this->entries);
while (!found && entries->enumerate(entries, &entry))
{
remotes = entry->remote->create_enumerator(entry->remote);
while (!found && remotes->enumerate(remotes, &ts))
{
if (ts->includes(ts, remote))
{
locals = entry->local->create_enumerator(entry->local);
while (!found && locals->enumerate(locals, &ts))
{
found = ts->includes(ts, local);
}
locals->destroy(locals);
}
}
remotes->destroy(remotes);
}
entries->destroy(entries);
this->lock->unlock(this->lock); this->lock->unlock(this->lock);
return active;
return found;
} }
METHOD(farp_listener_t, destroy, void, METHOD(farp_listener_t, destroy, void,
private_farp_listener_t *this) private_farp_listener_t *this)
{ {
enumerator_t *enumerator; this->entries->destroy(this->entries);
host_t *key, *value;
enumerator = this->ips->create_enumerator(this->ips);
while (enumerator->enumerate(enumerator, &key, &value))
{
value->destroy(value);
}
enumerator->destroy(enumerator);
this->ips->destroy(this->ips);
this->lock->destroy(this->lock); this->lock->destroy(this->lock);
free(this); free(this);
} }
@@ -138,14 +149,12 @@ farp_listener_t *farp_listener_create()
INIT(this, INIT(this,
.public = { .public = {
.listener = { .listener = {
.ike_updown = _ike_updown, .child_updown = _child_updown,
.message = _message_hook,
}, },
.is_active = _is_active, .has_tunnel = _has_tunnel,
.destroy = _destroy, .destroy = _destroy,
}, },
.ips = hashtable_create((hashtable_hash_t)hash, .entries = linked_list_create(),
(hashtable_equals_t)equals, 8),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
); );
+5 -4
View File
@@ -37,12 +37,13 @@ struct farp_listener_t {
listener_t listener; listener_t listener;
/** /**
* Check if a given IP is currently used as virtual IP by a peer. * Check if we have a tunnel between two IP addresses.
* *
* @param ip IP to check * @param local local IP
* @return TRUE if IP is an active virtual IP * @param remote remote IP
* @return TRUE if a tunnel is active
*/ */
bool (*is_active)(farp_listener_t *this, host_t *ip); bool (*has_tunnel)(farp_listener_t *this, host_t *local, host_t *remote);
/** /**
* Destroy a farp_listener_t. * Destroy a farp_listener_t.
+8 -8
View File
@@ -108,7 +108,7 @@ static job_requeue_t receive_arp(private_farp_spoofer_t *this)
arp_t arp; arp_t arp;
int oldstate; int oldstate;
ssize_t len; ssize_t len;
host_t *ip; host_t *local, *remote;
oldstate = thread_cancelability(TRUE); oldstate = thread_cancelability(TRUE);
len = recvfrom(this->skt, &arp, sizeof(arp), 0, len = recvfrom(this->skt, &arp, sizeof(arp), 0,
@@ -117,16 +117,16 @@ static job_requeue_t receive_arp(private_farp_spoofer_t *this)
if (len == sizeof(arp)) if (len == sizeof(arp))
{ {
ip = host_create_from_chunk(AF_INET, local = host_create_from_chunk(AF_INET,
chunk_create((char*)&arp.sender_ip, 4), 0);
remote = host_create_from_chunk(AF_INET,
chunk_create((char*)&arp.target_ip, 4), 0); chunk_create((char*)&arp.target_ip, 4), 0);
if (ip) if (this->listener->has_tunnel(this->listener, local, remote))
{ {
if (this->listener->is_active(this->listener, ip)) send_arp(this, &arp, &addr);
{
send_arp(this, &arp, &addr);
}
ip->destroy(ip);
} }
local->destroy(local);
remote->destroy(remote);
} }
return JOB_REQUEUE_DIRECT; return JOB_REQUEUE_DIRECT;