]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
child-cfg: Use traffic selector list
authorTobias Brunner <tobias@strongswan.org>
Tue, 1 Apr 2025 08:02:31 +0000 (10:02 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 28 May 2025 09:06:19 +0000 (11:06 +0200)
src/libcharon/config/child_cfg.c
src/libcharon/config/child_cfg.h

index 23c96646e7a161bb9500a8b02f933eefccbf4f7a..3b5d60af680c3dd1a7d508c6de63862bf8520ed8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2019 Tobias Brunner
+ * Copyright (C) 2008-2025 Tobias Brunner
  * Copyright (C) 2016 Andreas Steffen
  * Copyright (C) 2005-2007 Martin Willi
  * Copyright (C) 2005 Jan Hutter
@@ -67,12 +67,12 @@ struct private_child_cfg_t {
        /**
         * list for traffic selectors for my site
         */
-       linked_list_t *my_ts;
+       traffic_selector_list_t *my_ts;
 
        /**
         * list for traffic selectors for others site
         */
-       linked_list_t *other_ts;
+       traffic_selector_list_t *other_ts;
 
        /**
         * updown script
@@ -258,190 +258,66 @@ METHOD(child_cfg_t, add_traffic_selector, void,
 {
        if (local)
        {
-               this->my_ts->insert_last(this->my_ts, ts);
+               this->my_ts->add(this->my_ts, ts);
        }
        else
        {
-               this->other_ts->insert_last(this->other_ts, ts);
+               this->other_ts->add(this->other_ts, 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.
+ * Check whether the config is for regular transport mode.
  */
-static linked_list_t *resolve_dynamic_ts(private_child_cfg_t *this,
-                                                                               linked_list_t *list,
-                                                                               linked_list_t *hosts,
-                                                                               bool narrowing)
+static bool is_transport_mode(private_child_cfg_t *this)
 {
-       enumerator_t *e1, *e2;
-       traffic_selector_t *ts1, *ts2;
-       linked_list_t *result;
-       host_t *host;
-       bool transport_mode;
-
-       if (!hosts || !hosts->get_count(hosts))
-       {
-               return list->clone_offset(list, offsetof(traffic_selector_t, clone));
-       }
-       transport_mode = !narrowing && this->mode == MODE_TRANSPORT &&
-                                        !has_option(this, OPT_PROXY_MODE);
-
-       result = linked_list_create();
-       e1 = list->create_enumerator(list);
-       while (e1->enumerate(e1, &ts1))
-       {
-               /* set hosts if TS is dynamic or as initiator in transport mode */
-               bool dynamic = ts1->is_dynamic(ts1);
-               if (!dynamic && !transport_mode)
-               {
-                       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);
+       return this->mode == MODE_TRANSPORT && !has_option(this, OPT_PROXY_MODE);
 }
 
 METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
        private_child_cfg_t *this, bool local, linked_list_t *hosts)
 {
-       linked_list_t *result;
+       traffic_selector_list_t *ts = local ? this->my_ts : this->other_ts;
 
-       result = resolve_dynamic_ts(this, local ? this->my_ts : this->other_ts,
-                                                               hosts, FALSE);
-       remove_duplicate_ts(result);
-       return result;
+       /* force replacing non-dynamic TS to the IPs in transport mode */
+       return ts->get(ts, hosts, is_transport_mode(this));
 }
 
-METHOD(child_cfg_t, select_traffic_selectors, linked_list_t*,
-       private_child_cfg_t *this, bool local, linked_list_t *supplied,
-       linked_list_t *hosts)
+/*
+ * Described in header
+ */
+linked_list_t *child_cfg_select_ts(child_cfg_t *cfg, bool local,
+                                                                  traffic_selector_list_t *list,
+                                                                  linked_list_t *supplied, linked_list_t *hosts)
 {
-       enumerator_t *e1, *e2;
-       traffic_selector_t *ts1, *ts2, *selected;
-       linked_list_t *resolved, *result;
-
-       result = linked_list_create();
-       resolved = resolve_dynamic_ts(this, local ? this->my_ts : this->other_ts,
-                                                                 hosts, supplied);
+       private_child_cfg_t *this = (private_child_cfg_t*)cfg;
+       traffic_selector_list_t *ts = list ?: (local ? this->my_ts : this->other_ts);
+       linked_list_t *result;
+       bool force, narrowed = FALSE;
 
        DBG2(DBG_CFG, "%s traffic selectors for %s:",
                 supplied ? "selecting" : "proposing", local ? "us" : "other");
 
-       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);
-
-               /* check if we/peer did any narrowing, raise alert */
-               e1 = resolved->create_enumerator(resolved);
-               e2 = result->create_enumerator(result);
-               while (e1->enumerate(e1, &ts1))
-               {
-                       if (!e2->enumerate(e2, &ts2) || !ts1->equals(ts1, ts2))
-                       {
-                               charon->bus->alert(charon->bus, ALERT_TS_NARROWED,
-                                                                  local, result, this);
-                               break;
-                       }
-               }
-               e1->destroy(e1);
-               e2->destroy(e2);
+       /* force replacing non-dynamic TS to the IPs in transport mode, but only
+        * when proposing as initiator */
+       force = supplied && is_transport_mode(this);
 
+       result = ts->select(ts, supplied, hosts, force, &narrowed);
+       if (narrowed)
+       {
+               charon->bus->alert(charon->bus, ALERT_TS_NARROWED,
+                                                  local, result, this);
        }
-       resolved->destroy_offset(resolved, offsetof(traffic_selector_t, destroy));
-       remove_duplicate_ts(result);
        return result;
 }
 
+METHOD(child_cfg_t, select_traffic_selectors, linked_list_t*,
+       private_child_cfg_t *this, bool local, linked_list_t *supplied,
+       linked_list_t *hosts)
+{
+       return child_cfg_select_ts(&this->public, local, NULL, supplied, hosts);
+}
+
 METHOD(child_cfg_t, get_updown, char*,
        private_child_cfg_t *this)
 {
@@ -706,13 +582,8 @@ METHOD(child_cfg_t, equals, bool,
        {
                return FALSE;
        }
-       if (!this->my_ts->equals_offset(this->my_ts, other->my_ts,
-                                                                       offsetof(traffic_selector_t, equals)))
-       {
-               return FALSE;
-       }
-       if (!this->other_ts->equals_offset(this->other_ts, other->other_ts,
-                                                                          offsetof(traffic_selector_t, equals)))
+       if (!this->my_ts->equals(this->my_ts, other->my_ts) ||
+               !this->other_ts->equals(this->other_ts, other->other_ts))
        {
                return FALSE;
        }
@@ -758,8 +629,8 @@ METHOD(child_cfg_t, destroy, void,
        if (ref_put(&this->refcount))
        {
                this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
-               this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
-               this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
+               this->my_ts->destroy(this->my_ts);
+               this->other_ts->destroy(this->other_ts);
                DESTROY_IF(this->label);
                free(this->updown);
                free(this->interface);
@@ -835,8 +706,8 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
                .interface = strdupnull(data->interface),
                .refcount = 1,
                .proposals = linked_list_create(),
-               .my_ts = linked_list_create(),
-               .other_ts = linked_list_create(),
+               .my_ts = traffic_selector_list_create(),
+               .other_ts = traffic_selector_list_create(),
                .replay_window = lib->settings->get_int(lib->settings,
                                                        "%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
                .hw_offload = data->hw_offload,
index eb78b3d32f5f7b380a6613b503dbc9c3d85d82d2..e6219f68cf479396de888b60167ab9d0b4a333a5 100644 (file)
@@ -32,6 +32,7 @@ typedef struct child_cfg_create_t child_cfg_create_t;
 
 #include <library.h>
 #include <selectors/traffic_selector.h>
+#include <selectors/traffic_selector_list.h>
 #include <crypto/proposal/proposal.h>
 #include <kernel/kernel_ipsec.h>
 
@@ -121,12 +122,12 @@ struct child_cfg_t {
        /**
         * Get a list of configured traffic selectors to use for the CHILD_SA.
         *
-        * The config contains two set of traffic selectors, one for the local
+        * The config contains two sets of traffic selectors, one for the local
         * side, one for the remote side.
         *
         * Some traffic selectors may be "dynamic", meaning they are narrowed down
         * to a specific address (host-to-host or virtual-IP setups). Use the
-        * "hosts" parameter to narrow such traffic selectors to an address.
+        * \p hosts parameter to narrow such traffic selectors to an address.
         *
         * Returned list and its traffic selectors must be destroyed after use.
         *
@@ -143,7 +144,7 @@ struct child_cfg_t {
        /**
         * Select a list of traffic selectors to use for the CHILD_SA.
         *
-        * The config contains two set of traffic selectors, one for the local
+        * The config contains two sets of traffic selectors, one for the local
         * side, one for the remote side.
         *
         * If a list with traffic selectors is supplied, these are used to narrow
@@ -151,10 +152,13 @@ struct child_cfg_t {
         *
         * Some traffic selectors may be "dynamic", meaning they are narrowed down
         * to a specific address (host-to-host or virtual-IP setups). Use the
-        * "hosts" parameter to narrow such traffic selectors to an address.
+        * \p hosts parameter to narrow such traffic selectors to an address.
         *
         * Returned list and its traffic selectors must be destroyed after use.
         *
+        * Details about the selection process are logged and an alert is triggered
+        * if narrowing occurred.
+        *
         * @param local                 TRUE for TS on local side, FALSE for remote
         * @param supplied              list with TS to select from, or NULL
         * @param hosts                 addresses to use for narrowing "dynamic" TS', host_t
@@ -473,4 +477,21 @@ struct child_cfg_create_t {
  */
 child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data);
 
+/**
+ * Select and narrow traffic selectors in the given traffic selector list.
+ * Refer to child_cfg_t::select_traffic_selectors() for details, the difference
+ * is that this can work with external traffic selector lists.
+ *
+ * @param this                         config to use
+ * @param local                                TRUE for TS on local side, FALSE for remote (also
+ *                                                     used for logging)
+ * @param list                         traffic selectors to use instead of those in config
+ * @param supplied                     list with TS to select from, or NULL
+ * @param hosts                                addresses to use for narrowing "dynamic" TS', host_t
+ * @return                                     list containing the traffic selectors
+ */
+linked_list_t *child_cfg_select_ts(child_cfg_t *this, bool local,
+                                                                  traffic_selector_list_t *list,
+                                                                  linked_list_t *supplied, linked_list_t *hosts);
+
 #endif /** CHILD_CFG_H_ @}*/