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 \
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 \
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 \
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ @}*/
/*
- * Copyright (C) 2015 Tobias Brunner
+ * Copyright (C) 2015-2025 Tobias Brunner
* Copyright (C) 2015 Martin Willi
*
* Copyright (C) secunet Security Networks AG
#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)
}
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;
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;
}