strongswan/src/libcharon/plugins/farp/farp_listener.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;
}