210 lines
4.9 KiB
C
210 lines
4.9 KiB
C
/*
|
|
* Copyright (C) 2019 Tobias Brunner
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
*
|
|
* Copyright (C) 2010 Martin Willi
|
|
* Copyright (C) 2010 revosec AG
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include "farp_listener.h"
|
|
|
|
#include <collections/linked_list.h>
|
|
#include <threading/rwlock.h>
|
|
|
|
typedef struct private_farp_listener_t private_farp_listener_t;
|
|
|
|
/**
|
|
* Private data of an farp_listener_t object.
|
|
*/
|
|
struct private_farp_listener_t {
|
|
|
|
/**
|
|
* Public farp_listener_t interface.
|
|
*/
|
|
farp_listener_t public;
|
|
|
|
/**
|
|
* List with entry_t
|
|
*/
|
|
linked_list_t *entries;
|
|
|
|
/**
|
|
* RWlock for IP list
|
|
*/
|
|
rwlock_t *lock;
|
|
};
|
|
|
|
/**
|
|
* Traffic selector cache entry
|
|
*/
|
|
typedef struct {
|
|
/** list of local selectors */
|
|
linked_list_t *local;
|
|
/** list of remote selectors */
|
|
linked_list_t *remote;
|
|
/** reqid of CHILD_SA */
|
|
uint32_t reqid;
|
|
} entry_t;
|
|
|
|
/**
|
|
* Destroy a cache entry
|
|
*/
|
|
static void destroy_entry(entry_t *this)
|
|
{
|
|
this->local->destroy_offset(this->local,
|
|
offsetof(traffic_selector_t, destroy));
|
|
this->remote->destroy_offset(this->remote,
|
|
offsetof(traffic_selector_t, destroy));
|
|
free(this);
|
|
}
|
|
|
|
METHOD(listener_t, child_updown, bool,
|
|
private_farp_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
|
|
bool up)
|
|
{
|
|
enumerator_t *enumerator;
|
|
traffic_selector_t *ts;
|
|
entry_t *entry;
|
|
const chunk_t full_from = chunk_from_chars(0x00, 0x00, 0x00, 0x00),
|
|
full_to = chunk_from_chars(0xff, 0xff, 0xff, 0xff);
|
|
|
|
if (up)
|
|
{
|
|
INIT(entry,
|
|
.local = linked_list_create(),
|
|
.remote = linked_list_create(),
|
|
.reqid = child_sa->get_reqid(child_sa),
|
|
);
|
|
|
|
enumerator = child_sa->create_ts_enumerator(child_sa, FALSE);
|
|
while (enumerator->enumerate(enumerator, &ts))
|
|
{
|
|
if (ts->get_type(ts) != TS_IPV4_ADDR_RANGE)
|
|
{
|
|
continue;
|
|
}
|
|
/* ignore 0.0.0.0/0 remote TS because we don't want to
|
|
* reply to ARP requests for locally connected subnets */
|
|
if (chunk_equals(ts->get_from_address(ts), full_from) &&
|
|
chunk_equals(ts->get_to_address(ts), full_to))
|
|
{
|
|
continue;
|
|
}
|
|
entry->remote->insert_last(entry->remote, ts->clone(ts));
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
enumerator = child_sa->create_ts_enumerator(child_sa, TRUE);
|
|
while (enumerator->enumerate(enumerator, &ts))
|
|
{
|
|
if (ts->get_type(ts) != TS_IPV4_ADDR_RANGE)
|
|
{
|
|
continue;
|
|
}
|
|
entry->local->insert_last(entry->local, ts->clone(ts));
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
|
|
if (!entry->remote->get_count(entry->remote) ||
|
|
!entry->local->get_count(entry->local))
|
|
{
|
|
destroy_entry(entry);
|
|
return TRUE;
|
|
}
|
|
|
|
this->lock->write_lock(this->lock);
|
|
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))
|
|
{
|
|
if (entry->reqid == child_sa->get_reqid(child_sa))
|
|
{
|
|
this->entries->remove_at(this->entries, enumerator);
|
|
destroy_entry(entry);
|
|
break;
|
|
}
|
|
}
|
|
enumerator->destroy(enumerator);
|
|
this->lock->unlock(this->lock);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
METHOD(farp_listener_t, has_tunnel, bool,
|
|
private_farp_listener_t *this, host_t *local, host_t *remote)
|
|
{
|
|
enumerator_t *entries, *locals, *remotes;
|
|
traffic_selector_t *ts;
|
|
bool found = FALSE;
|
|
entry_t *entry;
|
|
|
|
this->lock->read_lock(this->lock);
|
|
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);
|
|
|
|
return found;
|
|
}
|
|
|
|
METHOD(farp_listener_t, destroy, void,
|
|
private_farp_listener_t *this)
|
|
{
|
|
this->entries->destroy(this->entries);
|
|
this->lock->destroy(this->lock);
|
|
free(this);
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
farp_listener_t *farp_listener_create()
|
|
{
|
|
private_farp_listener_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.listener = {
|
|
.child_updown = _child_updown,
|
|
},
|
|
.has_tunnel = _has_tunnel,
|
|
.destroy = _destroy,
|
|
},
|
|
.entries = linked_list_create(),
|
|
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
|
);
|
|
|
|
return &this->public;
|
|
}
|