traffic-selector-list: Add helper class to manage a collection of TS

Provides functions to optionally resolve dynamic TS and to narrow
them based on a list of supplied TS.
This commit is contained in:
Tobias Brunner
2025-05-28 11:06:19 +02:00
parent 79815b4e67
commit 4b468126ca
5 changed files with 652 additions and 4 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
selectors/sec_label.c selectors/traffic_selector.c \
selectors/sec_label.c selectors/traffic_selector.c selectors/traffic_selector_list.c \
settings/settings.c settings/settings_types.c \
settings/settings_parser.c settings/settings_lexer.c utils/cpu_feature.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
+2 -2
View File
@@ -40,7 +40,7 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
selectors/sec_label.c selectors/traffic_selector.c \
selectors/sec_label.c selectors/traffic_selector.c selectors/traffic_selector_list.c \
settings/settings.c settings/settings_types.c \
settings/settings_parser.y settings/settings_lexer.l utils/cpu_feature.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
@@ -125,7 +125,7 @@ resolver/rr.h resolver/resolver_manager.h \
plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \
processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \
processing/scheduler.h processing/watcher.h \
selectors/sec_label.h selectors/traffic_selector.h \
selectors/sec_label.h selectors/traffic_selector.h selectors/traffic_selector_list.h \
settings/settings.h settings/settings_parser.h threading/thread_value.h \
threading/thread.h threading/windows/thread.h \
threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \
@@ -0,0 +1,292 @@
/*
* Copyright (C) 2008-2025 Tobias Brunner
* Copyright (C) 2005-2007 Martin Willi
*
* Copyright (C) secunet Security Networks 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 "traffic_selector_list.h"
#include <utils/debug.h>
typedef struct private_traffic_selector_list_t private_traffic_selector_list_t;
/**
* Private data.
*/
struct private_traffic_selector_list_t {
/**
* Public interface.
*/
traffic_selector_list_t public;
/**
* List of managed traffic selectors.
*/
linked_list_t *ts;
};
METHOD(traffic_selector_list_t, add, void,
private_traffic_selector_list_t *this, traffic_selector_t *ts)
{
this->ts->insert_last(this->ts, ts);
}
METHOD(traffic_selector_list_t, create_enumerator, enumerator_t*,
private_traffic_selector_list_t *this)
{
return this->ts->create_enumerator(this->ts);
}
/**
* Create a copy of the traffic selectors in the given list, while resolving
* "dynamic" traffic selectors using the given hosts, if any. When not narrowing
* as initiator, we also replace TS in transport mode.
*/
static linked_list_t *resolve_dynamic_ts(private_traffic_selector_list_t *this,
linked_list_t *hosts, bool narrowing,
bool force_dynamic)
{
enumerator_t *e1, *e2;
traffic_selector_t *ts1, *ts2;
linked_list_t *result;
host_t *host;
if (!hosts || !hosts->get_count(hosts))
{
return this->ts->clone_offset(this->ts,
offsetof(traffic_selector_t, clone));
}
result = linked_list_create();
e1 = this->ts->create_enumerator(this->ts);
while (e1->enumerate(e1, &ts1))
{
/* set hosts if TS is dynamic or if forced as initiator in
* transport mode */
bool dynamic = ts1->is_dynamic(ts1);
if (!dynamic && !force_dynamic)
{
result->insert_last(result, ts1->clone(ts1));
continue;
}
e2 = hosts->create_enumerator(hosts);
while (e2->enumerate(e2, &host))
{
if (!dynamic && !host->is_anyaddr(host) &&
!ts1->includes(ts1, host))
{ /* for transport mode, we skip TS that don't match
* specific IPs */
continue;
}
ts2 = ts1->clone(ts1);
if (dynamic || !host->is_anyaddr(host))
{ /* don't make regular TS larger than they were */
ts2->set_address(ts2, host);
}
result->insert_last(result, ts2);
}
e2->destroy(e2);
}
e1->destroy(e1);
return result;
}
/**
* Remove duplicate traffic selectors in the given list.
*/
static void remove_duplicate_ts(linked_list_t *list)
{
enumerator_t *e1, *e2;
traffic_selector_t *ts1, *ts2;
e1 = list->create_enumerator(list);
e2 = list->create_enumerator(list);
while (e1->enumerate(e1, &ts1))
{
while (e2->enumerate(e2, &ts2))
{
if (ts1 != ts2)
{
if (ts2->is_contained_in(ts2, ts1))
{
list->remove_at(list, e2);
ts2->destroy(ts2);
list->reset_enumerator(list, e1);
break;
}
if (ts1->is_contained_in(ts1, ts2))
{
list->remove_at(list, e1);
ts1->destroy(ts1);
break;
}
}
}
list->reset_enumerator(list, e2);
}
e1->destroy(e1);
e2->destroy(e2);
}
METHOD(traffic_selector_list_t, get, linked_list_t*,
private_traffic_selector_list_t *this, linked_list_t *hosts,
bool force_dynamic)
{
linked_list_t *result;
result = resolve_dynamic_ts(this, hosts, FALSE, force_dynamic);
remove_duplicate_ts(result);
return result;
}
METHOD(traffic_selector_list_t, select_, linked_list_t*,
private_traffic_selector_list_t *this, linked_list_t *supplied,
linked_list_t *hosts, bool force_dynamic, bool *narrowed)
{
enumerator_t *e1, *e2;
traffic_selector_t *ts1, *ts2, *selected;
linked_list_t *resolved, *result;
result = linked_list_create();
resolved = resolve_dynamic_ts(this, hosts, supplied != NULL, force_dynamic);
if (!supplied)
{
while (resolved->remove_first(resolved, (void**)&ts1) == SUCCESS)
{
DBG2(DBG_CFG, " %R", ts1);
result->insert_last(result, ts1);
}
}
else
{
e1 = resolved->create_enumerator(resolved);
e2 = supplied->create_enumerator(supplied);
/* enumerate all configured/resolved selectors */
while (e1->enumerate(e1, &ts1))
{
/* enumerate all supplied traffic selectors */
while (e2->enumerate(e2, &ts2))
{
selected = ts1->get_subset(ts1, ts2);
if (selected)
{
DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
ts1, ts2, selected);
result->insert_last(result, selected);
}
else
{
DBG2(DBG_CFG, " config: %R, received: %R => no match",
ts1, ts2);
}
}
supplied->reset_enumerator(supplied, e2);
}
e1->destroy(e1);
e2->destroy(e2);
if (narrowed)
{
*narrowed = FALSE;
e1 = resolved->create_enumerator(resolved);
e2 = result->create_enumerator(result);
while (e1->enumerate(e1, &ts1))
{
if (!e2->enumerate(e2, &ts2) || !ts1->equals(ts1, ts2))
{
*narrowed = TRUE;
break;
}
}
e1->destroy(e1);
e2->destroy(e2);
}
}
resolved->destroy_offset(resolved, offsetof(traffic_selector_t, destroy));
remove_duplicate_ts(result);
return result;
}
METHOD(traffic_selector_list_t, equals, bool,
private_traffic_selector_list_t *this, traffic_selector_list_t *other_pub)
{
private_traffic_selector_list_t *other = (private_traffic_selector_list_t*)other_pub;
return this->ts->equals_offset(this->ts, other->ts,
offsetof(traffic_selector_t, equals));
}
METHOD(traffic_selector_list_t, destroy, void,
private_traffic_selector_list_t *this)
{
this->ts->destroy_offset(this->ts, offsetof(traffic_selector_t, destroy));
free(this);
}
METHOD(traffic_selector_list_t, clone_, traffic_selector_list_t*,
private_traffic_selector_list_t *this)
{
return traffic_selector_list_create_from_list(
this->ts->clone_offset(this->ts, offsetof(traffic_selector_t, clone)));
}
/*
* Described in header
*/
traffic_selector_list_t *traffic_selector_list_create_from_list(linked_list_t *list)
{
private_traffic_selector_list_t *this;
INIT(this,
.public = {
.add = _add,
.create_enumerator = _create_enumerator,
.get = _get,
.select = _select_,
.equals = _equals,
.clone = _clone_,
.destroy = _destroy,
},
.ts = list,
);
return &this->public;
}
/*
* Described in header
*/
traffic_selector_list_t *traffic_selector_list_create()
{
return traffic_selector_list_create_from_list(linked_list_create());
}
/*
* Described in header
*/
traffic_selector_list_t *traffic_selector_list_create_from_enumerator(
enumerator_t *enumerator)
{
traffic_selector_list_t *this = traffic_selector_list_create();
traffic_selector_t *ts;
while (enumerator->enumerate(enumerator, &ts))
{
add((private_traffic_selector_list_t*)this, ts->clone(ts));
}
enumerator->destroy(enumerator);
return this;
}
@@ -0,0 +1,155 @@
/*
* Copyright (C) 2025 Tobias Brunner
*
* Copyright (C) secunet Security Networks 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.
*/
/**
* @defgroup traffic_selector_list traffic_selector_list
* @{ @ingroup selectors
*/
#ifndef TRAFFIC_SELECTOR_LIST_H_
#define TRAFFIC_SELECTOR_LIST_H_
typedef struct traffic_selector_list_t traffic_selector_list_t;
#include "traffic_selector.h"
/**
* Collection of traffic selectors that can be narrowed to a new set of
* traffic selectors.
*/
struct traffic_selector_list_t {
/**
* Add a traffic selector to the collection.
*
* @param ts traffic_selector to add (adopted)
*/
void (*add)(traffic_selector_list_t *this, traffic_selector_t *ts);
/**
* Enumerate all traffic selectors in the collection.
*
* Similar to calling get() without \p hosts, but does not clone the traffic
* selectors and duplicates are not removed.
*
* @return enumerator over traffic_selector_t*
*/
enumerator_t *(*create_enumerator)(traffic_selector_list_t *this);
/**
* Get a list of traffic selectors contained in the collection.
*
* Some traffic selectors may be "dynamic", meaning they are narrowed down
* to a specific address (host-to-host or virtual-IP setups). Use the
* \p hosts parameter to narrow such traffic selectors to an address. If
* \p force_dynamic is also passed, even non-dynamic traffic selectors that
* match are replaced using the IPs in \p hosts (useful as initiator with
* transport mode).
*
* If \p hosts is not passed, the list of traffic selectors is returned as
* configured, except that exact duplicates are removed. However, note that
* "dynamic" traffic selectors are not considered duplicates.
*
* Returned list and its traffic selectors must be destroyed after use.
*
* Note that this method does not log anything. If logging is required, use
* select() without passing supplied traffic selectors.
*
* @param hosts addresses to use for narrowing "dynamic" TS, host_t
* @param force_dynamic TRUE to replace non-"dynamic" TS with \p hosts as
* initiator in transport mode
* @return list containing the traffic selectors
*/
linked_list_t *(*get)(traffic_selector_list_t *this, linked_list_t *hosts,
bool force_dynamic);
/**
* Select a list of traffic selectors contained in the collection.
*
* If a list with traffic selectors is supplied, these are used to narrow
* down the traffic selectors to the greatest common subset.
*
* Some traffic selectors may be "dynamic", meaning they are narrowed down
* to a specific address (host-to-host or virtual-IP setups). Use the
* \p hosts parameter to narrow such traffic selectors to an address. If
* \p force_dynamic is also passed, even non-dynamic traffic selectors that
* match are replaced using the IPs in \p hosts (useful as initiator with
* transport mode).
*
* Details about the selection of each individual traffic selector are
* logged.
*
* Returned list and its traffic selectors must be destroyed after use.
*
* @param supplied list with TS to select from, or NULL
* @param hosts addresses to use for narrowing "dynamic" TS', host_t
* @param force_dynamic TRUE to replace non-"dynamic" TS with \p hosts as
* initiator in transport mode
* @param narrowed[out] optional flag that indicates if any TS were narrowed
* @return list containing the traffic selectors
*/
linked_list_t *(*select)(traffic_selector_list_t *this,
linked_list_t *supplied, linked_list_t *hosts,
bool force_dynamic, bool *narrowed);
/**
* Compare two collections of traffic selectors.
*
* @param other collection to compare with this
* @return TRUE if equal, FALSE otherwise
*/
bool (*equals)(traffic_selector_list_t *this, traffic_selector_list_t *other);
/**
* Clone this collection of traffic selectors.
*
* @return cloned collection
*/
traffic_selector_list_t *(*clone)(traffic_selector_list_t *this);
/**
* Destroys this collection.
*/
void (*destroy)(traffic_selector_list_t *this);
};
/**
* Create an empty traffic selector collection.
*
* @return created object
*/
traffic_selector_list_t *traffic_selector_list_create();
/**
* Create a collection with traffic selectors from the given list (adopted).
*
* @param list list of traffic_selector_t (adopted)
* @return created object
*/
traffic_selector_list_t *traffic_selector_list_create_from_list(
linked_list_t *list);
/**
* Create a collection with traffic selectors from the given enumerator (objects
* are cloned, the enumerator is destroyed).
*
* @param enumerator enumerator over traffic_selector_t (cloned/destroyed)
* @return created object
*/
traffic_selector_list_t *traffic_selector_list_create_from_enumerator(
enumerator_t *enumerator);
#endif /** TRAFFIC_SELECTOR_LIST_H_ @}*/
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Tobias Brunner
* Copyright (C) 2015-2025 Tobias Brunner
* Copyright (C) 2015 Martin Willi
*
* Copyright (C) secunet Security Networks AG
@@ -18,6 +18,7 @@
#include "test_suite.h"
#include <selectors/traffic_selector.h>
#include <selectors/traffic_selector_list.h>
static void verify(const char *str, const char *alt, traffic_selector_t *ts)
@@ -814,6 +815,199 @@ START_TEST(test_printf_hook_hash)
}
END_TEST
/**
* Create a linked list of either traffic selectors or hosts from a
* comma-separated list.
*/
static linked_list_t *create_list(char *str, bool hosts)
{
linked_list_t *list = linked_list_create();
enumerator_t *enumerator;
char *item;
void *obj;
enumerator = enumerator_create_token(str, " ", "");
while (enumerator->enumerate(enumerator, &item))
{
if (hosts)
{
obj = host_create_from_string(item, 0);
}
else if (streq(item, "dynamic"))
{
obj = traffic_selector_create_dynamic(0, 0, 65535);
}
else
{
obj = traffic_selector_create_from_cidr(item, 0, 0, 65535);
}
list->insert_last(list, obj);
}
enumerator->destroy(enumerator);
return list;
}
struct {
char *ts;
char *hosts;
char *get;
char *get_force;
} list_get_tests[] = {
{ "dynamic", NULL, "dynamic", "dynamic" },
{ "dynamic", "10.0.1.1", "10.0.1.1/32", "10.0.1.1/32" },
{ "dynamic", "10.0.1.1 192.168.0.1",
"10.0.1.1/32 192.168.0.1/32", "10.0.1.1/32 192.168.0.1/32" },
{ "0.0.0.0/0", "10.0.1.1", "0.0.0.0/0", "10.0.1.1/32" },
{ "0.0.0.0/0", "10.0.1.1 192.168.0.1",
"0.0.0.0/0", "10.0.1.1/32 192.168.0.1/32" },
{ "10.0.1.0/24", "10.0.1.1", "10.0.1.0/24", "10.0.1.1/32" },
{ "10.0.2.0/24", "10.0.1.1", "10.0.2.0/24", "" },
{ "10.0.2.0/24", "10.0.1.1 10.0.2.1", "10.0.2.0/24", "10.0.2.1/32" },
/* two dynamic TS are not treated as duplicates */
{ "dynamic dynamic", NULL, "dynamic dynamic", "dynamic dynamic" },
{ "dynamic dynamic", "10.0.1.1", "10.0.1.1/32", "10.0.1.1/32" },
{ "dynamic dynamic", "10.0.1.1 192.168.0.1",
"10.0.1.1/32 192.168.0.1/32", "10.0.1.1/32 192.168.0.1/32" },
{ "0.0.0.0/0 0.0.0.0/0", "10.0.1.1", "0.0.0.0/0", "10.0.1.1/32" },
{ "0.0.0.0/0 0.0.0.0/0", "10.0.1.1 192.168.0.1",
"0.0.0.0/0", "10.0.1.1/32 192.168.0.1/32" },
{ "10.0.1.0/24 10.0.1.0/24", NULL, "10.0.1.0/24", "10.0.1.0/24" },
{ "10.0.1.1/32 10.0.1.0/24", NULL, "10.0.1.0/24", "10.0.1.0/24" },
{ "10.0.1.0/24 10.0.1.1/32", NULL, "10.0.1.0/24", "10.0.1.0/24" },
{ "10.0.1.0/24 10.0.2.0/24", NULL,
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24" },
{ "10.0.1.0/24 10.0.2.0/24", "10.0.1.1",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.1/32" },
{ "10.0.1.0/24 10.0.2.0/24", "10.0.1.1 10.0.2.1",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.1/32 10.0.2.1/32" },
};
START_TEST(test_list_get)
{
traffic_selector_list_t *ts;
linked_list_t *list, *hosts, *result;
list = create_list(list_get_tests[_i].ts, FALSE);
ts = traffic_selector_list_create_from_enumerator(list->create_enumerator(list));
hosts = list_get_tests[_i].hosts ? create_list(list_get_tests[_i].hosts, TRUE) : NULL;
result = ts->get(ts, hosts, FALSE);
verify_list(list_get_tests[_i].get, NULL, result);
result = ts->get(ts, hosts, TRUE);
verify_list(list_get_tests[_i].get_force, NULL, result);
DESTROY_OFFSET_IF(hosts, offsetof(host_t, destroy));
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
ts->destroy(ts);
}
END_TEST
struct {
char *ts;
char *hosts;
char *supplied;
char *select;
char *select_force;
bool narrowed;
} list_select_tests[] = {
{ "dynamic", NULL, NULL, "dynamic", "dynamic", FALSE },
{ "dynamic", NULL, "10.0.1.0/24", "", "", TRUE },
{ "dynamic", "10.0.1.1", "0.0.0.0/0", "10.0.1.1/32", "10.0.1.1/32", FALSE },
{ "dynamic", "10.0.1.1", "10.0.1.1/32", "10.0.1.1/32", "10.0.1.1/32", FALSE },
{ "dynamic", "10.0.1.1 192.168.0.1", "10.0.1.0/24",
"10.0.1.1/32", "10.0.1.1/32", TRUE },
{ "dynamic", "10.0.1.1 192.168.0.1", "10.0.1.0/24 192.168.0.0/24",
"10.0.1.1/32 192.168.0.1/32", "10.0.1.1/32 192.168.0.1/32", FALSE },
{ "0.0.0.0/0", NULL, "0.0.0.0/0", "0.0.0.0/0", "0.0.0.0/0", FALSE },
{ "0.0.0.0/0", "10.0.1.1", "0.0.0.0/0", "0.0.0.0/0", "10.0.1.1/32", FALSE },
{ "0.0.0.0/0", NULL, "10.0.1.0/24", "10.0.1.0/24", "10.0.1.0/24", TRUE },
{ "0.0.0.0/0", NULL, "10.0.1.0/24 10.0.2.0/24",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24", TRUE },
{ "0.0.0.0/0", NULL, "10.0.2.0/24 10.0.1.0/24",
"10.0.2.0/24 10.0.1.0/24", "10.0.2.0/24 10.0.1.0/24", TRUE },
{ "10.0.1.0/24", NULL, "0.0.0.0/0", "10.0.1.0/24", "10.0.1.0/24", FALSE },
{ "10.0.1.0/24", "10.0.1.1", "0.0.0.0/0", "10.0.1.0/24", "10.0.1.1/32", FALSE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "0.0.0.0/0",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24", FALSE },
{ "10.0.2.0/24 10.0.1.0/24", NULL, "0.0.0.0/0",
"10.0.2.0/24 10.0.1.0/24", "10.0.2.0/24 10.0.1.0/24", FALSE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "10.0.2.0/24 10.0.1.0/24",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24", FALSE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "10.0.1.1/32 10.0.1.0/24",
"10.0.1.0/24", "10.0.1.0/24", TRUE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "10.0.2.1/32 10.0.1.0/24",
"10.0.1.0/24 10.0.2.1/32", "10.0.1.0/24 10.0.2.1/32", TRUE },
};
START_TEST(test_list_select)
{
traffic_selector_list_t *ts;
linked_list_t *list, *hosts, *supplied, *result;
bool narrowed = FALSE;
list = create_list(list_select_tests[_i].ts, FALSE);
ts = traffic_selector_list_create_from_enumerator(list->create_enumerator(list));
hosts = list_select_tests[_i].hosts ? create_list(list_select_tests[_i].hosts, TRUE) : NULL;
supplied = list_select_tests[_i].supplied ? create_list(list_select_tests[_i].supplied, FALSE) : NULL;
result = ts->select(ts, supplied, hosts, FALSE, &narrowed);
verify_list(list_select_tests[_i].select, NULL, result);
ck_assert(narrowed == list_select_tests[_i].narrowed);
result = ts->select(ts, supplied, hosts, TRUE, &narrowed);
verify_list(list_select_tests[_i].select_force, NULL, result);
ck_assert(narrowed == list_select_tests[_i].narrowed);
DESTROY_OFFSET_IF(hosts, offsetof(host_t, destroy));
DESTROY_OFFSET_IF(supplied, offsetof(traffic_selector_t, destroy));
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
ts->destroy(ts);
}
END_TEST
START_TEST(test_list_equals)
{
traffic_selector_list_t *ts1, *ts2;
ts1 = traffic_selector_list_create();
ts2 = traffic_selector_list_create();
ck_assert(ts1->equals(ts1, ts2));
ts1->add(ts1, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ck_assert(!ts1->equals(ts1, ts2));
ts2->add(ts2, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ck_assert(ts1->equals(ts1, ts2));
ts1->destroy(ts1);
ts2->destroy(ts2);
}
END_TEST
START_TEST(test_list_clone)
{
traffic_selector_list_t *ts1, *ts2;
ts1 = traffic_selector_list_create();
ts2 = ts1->clone(ts1);
ck_assert(ts1->equals(ts1, ts2));
ts2->destroy(ts2);
ts1->add(ts1, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ts2 = ts1->clone(ts1);
ck_assert(ts1->equals(ts1, ts2));
ts2->destroy(ts2);
ts1->add(ts1, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ts2 = ts1->clone(ts1);
ck_assert(ts1->equals(ts1, ts2));
ts1->destroy(ts1);
ts2->destroy(ts2);
}
END_TEST
Suite *traffic_selector_suite_create()
{
Suite *s;
@@ -890,5 +1084,12 @@ Suite *traffic_selector_suite_create()
tcase_add_test(tc, test_printf_hook_hash);
suite_add_tcase(s, tc);
tc = tcase_create("list");
tcase_add_loop_test(tc, test_list_get, 0, 0/*countof(list_get_tests)*/);
tcase_add_loop_test(tc, test_list_select, 0, countof(list_select_tests));
tcase_add_test(tc, test_list_equals);
tcase_add_test(tc, test_list_clone);
suite_add_tcase(s, tc);
return s;
}